BDE 4.39.x Production Release
Loading...
Searching...
No Matches
baljsn_formatter.h
Go to the documentation of this file.
1/// @file baljsn_formatter.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// baljsn_formatter.h -*-C++-*-
8#ifndef INCLUDED_BALJSN_FORMATTER
9#define INCLUDED_BALJSN_FORMATTER
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup baljsn_formatter baljsn_formatter
15/// @brief Provide a formatter for encoding data in the JSON format.
16/// @addtogroup bal
17/// @{
18/// @addtogroup baljsn
19/// @{
20/// @addtogroup baljsn_formatter
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#baljsn_formatter-purpose"> Purpose</a>
25/// * <a href="#baljsn_formatter-classes"> Classes </a>
26/// * <a href="#baljsn_formatter-description"> Description </a>
27/// * <a href="#baljsn_formatter-valid-sequence-of-operations"> Valid sequence of operations </a>
28/// * <a href="#baljsn_formatter-usage"> Usage </a>
29/// * <a href="#baljsn_formatter-example-1-encoding-a-stock-portfolio-in-json"> Example 1: Encoding a Stock Portfolio in JSON </a>
30///
31/// # Purpose {#baljsn_formatter-purpose}
32/// Provide a formatter for encoding data in the JSON format.
33///
34/// # Classes {#baljsn_formatter-classes}
35///
36/// - baljsn::Formatter: JSON formatter
37///
38/// @see baljsn_encoder, baljsn_printutil, baljsn_simpleformatter
39///
40/// # Description {#baljsn_formatter-description}
41/// This component provides a class, `baljsn::Formatter`, for
42/// formatting JSON objects, arrays, and name-value pairs in the JSON encoding
43/// format to a specified output stream.
44///
45/// The JSON encoding format (see http://json.org or ECMA-404 standard for more
46/// information) specifies a self-describing and simple syntax that is built on
47/// two structures:
48///
49/// * Objects: JSON objects are represented as collections of name value
50/// pairs. The `Formatter` `class` allows encoding objects by providing the
51/// `openObject` and `closeObject` methods to open and close an object and
52/// the `openMember`, `closeMember`, and `putValue` methods to add members
53/// and values to an object.
54/// * Arrays: JSON arrays are specified as an ordered list of values. The
55/// `Formatter` `class` provides the `openArray` and `closeArray` method to
56/// open and close an array. Additionally the `Formatter` `class` allows of
57/// separation of array items by a comma via the `addArrayElementSeparator`
58/// method.
59///
60/// The `Formatter` `class` also provides the ability to specify formatting
61/// options at construction. The options that can be provided include the
62/// encoding style (compact or pretty), the initial indentation level and spaces
63/// per level if encoding in the pretty format.
64///
65/// ## Valid sequence of operations {#baljsn_formatter-valid-sequence-of-operations}
66///
67///
68/// The `Formatter` `class` does only minimal checking to verify that the
69/// sequence of operations called on its object result in a valid JSON
70/// document. It is the user's responsibility to ensure that the methods
71/// provided by this component are called in the right order.
72///
73/// ## Usage {#baljsn_formatter-usage}
74///
75///
76/// This section illustrates intended use of this component.
77///
78/// ### Example 1: Encoding a Stock Portfolio in JSON {#baljsn_formatter-example-1-encoding-a-stock-portfolio-in-json}
79///
80///
81/// Let us say that we have to encode a JSON document with the following
82/// information about stocks that we are interested in. For brevity we just
83/// show and encode a part of the complete document.
84/// @code
85/// {
86/// "Stocks" : [
87/// {
88/// "Name": "International Business Machines Corp",
89/// "Ticker": "IBM US Equity",
90/// "Last Price": 149.3,
91/// "Dividend Yield": 3.95
92/// },
93/// {
94/// "Name": "Apple Inc",
95/// "Ticker": "AAPL US Equity",
96/// "Last Price": 205.8,
97/// "Dividend Yield": 1.4
98/// }
99/// ]
100/// }
101/// @endcode
102/// First, we specify the result that we are expecting to get:
103/// @code
104/// const bsl::string EXPECTED =
105/// "{\n"
106/// " \"Stocks\": [\n"
107/// " {\n"
108/// " \"Name\": \"International Business Machines Corp\",\n"
109/// " \"Ticker\": \"IBM US Equity\",\n"
110/// " \"Last Price\": 149.3,\n"
111/// " \"Dividend Yield\": 3.95\n"
112/// " },\n"
113/// " {\n"
114/// " \"Name\": \"Apple Inc\",\n"
115/// " \"Ticker\": \"AAPL US Equity\",\n"
116/// " \"Last Price\": 205.8,\n"
117/// " \"Dividend Yield\": 1.4\n"
118/// " }\n"
119/// " ]\n"
120/// "}";
121/// @endcode
122/// Then, to encode this JSON document we create a `baljsn::Formatter` object.
123/// Since we want the document to be written in a pretty, easy to understand
124/// format we will specify the `true` for the `usePrettyStyle` option and
125/// provide an appropriate initial indent level and spaces per level values:
126/// @code
127/// bsl::ostringstream os;
128/// baljsn::Formatter formatter(os, true, 0, 2);
129/// @endcode
130/// Next, we start calling the sequence of methods requires to produce this
131/// document. We start with the top level object and add an element named
132/// `Stocks` to it:
133/// @code
134/// formatter.openObject();
135/// formatter.openMember("Stocks");
136/// @endcode
137/// Then, we see that `Stocks` is an array element so we specify the start of
138/// the array:
139/// @code
140/// formatter.openArray();
141/// @endcode
142/// Next, each element within `Stocks` is an object that contains the
143/// information for an individual stock. So we have to output an object here:
144/// @code
145/// formatter.openObject();
146/// @endcode
147/// We now encode the other elements in the stock object. The `closeMember`
148/// terminates the element by adding a `,` at the end. For the last element in
149/// an object do not call the `closeMember` method.
150/// @code
151/// formatter.openMember("Name");
152/// formatter.putValue("International Business Machines Corp");
153/// formatter.closeMember();
154///
155/// formatter.openMember("Ticker");
156/// formatter.putValue("IBM US Equity");
157/// formatter.closeMember();
158///
159/// formatter.openMember("Last Price");
160/// formatter.putValue(149.3);
161/// formatter.closeMember();
162///
163/// formatter.openMember("Dividend Yield");
164/// formatter.putValue(3.95);
165/// // Note no call to 'closeMember' for the last element
166/// @endcode
167/// Then, close the first stock object and separate it from the second one using
168/// the `addArrayElementSeparator` method.
169/// @code
170/// formatter.closeObject();
171/// formatter.addArrayElementSeparator();
172/// @endcode
173/// Next, we add another stock object. But we don't need to separate it as it
174/// is the last one.
175/// @code
176/// formatter.openObject();
177///
178/// formatter.openMember("Name");
179/// formatter.putValue("Apple Inc");
180/// formatter.closeMember();
181///
182/// formatter.openMember("Ticker");
183/// formatter.putValue("AAPL US Equity");
184/// formatter.closeMember();
185///
186/// formatter.openMember("Last Price");
187/// formatter.putValue(205.8);
188/// formatter.closeMember();
189///
190/// formatter.openMember("Dividend Yield");
191/// formatter.putValue(1.4);
192///
193/// formatter.closeObject();
194/// @endcode
195/// Similarly, we can continue to format the rest of the document. For the
196/// purpose of this usage example we will complete this document.
197/// @code
198/// formatter.closeArray();
199/// formatter.closeObject();
200/// @endcode
201/// Once the formatting is complete the written data can be viewed from the
202/// stream passed to the formatter at construction.
203/// @code
204/// if (verbose)
205/// bsl::cout << os.str() << bsl::endl;
206/// @endcode
207/// Finally, verify the received result:
208/// @code
209/// assert(EXPECTED == os.str());
210/// @endcode
211/// @}
212/** @} */
213/** @} */
214
215/** @addtogroup bal
216 * @{
217 */
218/** @addtogroup baljsn
219 * @{
220 */
221/** @addtogroup baljsn_formatter
222 * @{
223 */
224
225#include <balscm_version.h>
226
228#include <baljsn_printutil.h>
229
230#include <bdlb_print.h>
231
232#include <bdlc_bitarray.h>
233
234#include <bsl_ostream.h>
235
236#include <bsls_assert.h>
237#include <bsls_review.h>
238
239#include <bsl_string.h>
240#include <bsl_string_view.h>
241
242
243namespace baljsn {
244
245 // ===============
246 // class Formatter
247 // ===============
248
249/// This class implements a formatter providing operations for rendering JSON
250/// text elements to an output stream (supplied at construction) according to a
251/// set of formatting options (also supplied at construction).
252///
253/// See @ref baljsn_formatter
255
256 // DATA
257 bsl::ostream& d_outputStream; // stream for output (held, not
258 // owned)
259
260 bool d_usePrettyStyle; // encoding style
261
262 bool d_escapeForwardSlash; // whether to escape `/` or not
263
264 int d_indentLevel; // current indentation level
265
266 int d_spacesPerLevel; // spaces per indentation level
267
268 bdlc::BitArray d_callSequence; // array specifying the sequence
269 // in which the 'openObject' and
270 // 'openArray' methods were
271 // called. An 'openObject' call
272 // is represented by 'false' and
273 // an 'openArray' call by 'true'.
274
275 EncoderOptions d_encoderOptions; // cached `EncoderOptions` as an
276 // optimization
277
278 // PRIVATE MANIPULATORS
279
280 /// Unconditionally print onto the stream supplied at construction the
281 /// sequence of whitespace characters for the proper indentation of an element at the current indentation level.
282 ///
283 /// \note Note that this method does
284 /// not check that `d_usePrettyStyle` is `true` before indenting.
285 void indent();
286
287 // PRIVATE ACCESSORS
288
289 /// Return `true` if the value being encoded is an element of an array, and
290 /// `false` otherwise. A value is identified as an element of an array if
291 /// `openArray` was called on this object and was not subsequently followed
292 /// by either an `openObject` or `closeArray` call.
293 bool isArrayElement() const;
294
295 public:
296 // CREATORS
297
298 /// Create a `Formatter` object using the specified `stream`. Optionally
299 /// specify `usePrettyStyle` to inform the formatter whether the pretty
300 /// encoding style should be used when writing data. If `usePrettyStyle`
301 /// is not specified then the data is written in a compact style. If
302 /// `usePrettyStyle` is specified, additionally specify
303 /// `initialIndentLevel` and `spacesPerLevel` to provide the initial
304 /// indentation level and spaces per level at which the data should be
305 /// formatted. If `initialIndentLevel` or `spacesPerLevel` is not
306 /// specified then an initial value of `0` is used for both parameters. If
307 /// `usePrettyStyle` is `false` then `initialIndentLevel` and
308 /// `spacesPerLevel` are both ignored. Optionally specify
309 /// `escapeForwardSlash`. If `escapeForwardSlash` is specified as `false`,
310 /// `/` characters will be formatted as-is, otherwise, they will be
311 /// rendered with a leading backslash, as "\\/". Optionally specify a
312 /// `basicAllocator` used to supply memory. If `basicAllocator` is 0, the
313 /// currently installed default allocator is used.
314 Formatter(bsl::ostream& stream,
315 bool usePrettyStyle = false,
316 int initialIndentLevel = 0,
317 int spacesPerLevel = 0,
318 bslma::Allocator *basicAllocator = 0);
319 Formatter(bsl::ostream& stream,
320 bool usePrettyStyle,
321 int initialIndentLevel,
322 int spacesPerLevel,
323 bool escapeForwardSlash,
324 bslma::Allocator *basicAllocator = 0);
325
326 /// Destroy this object.
327 ~Formatter() = default;
328
329 // MANIPULATORS
330
331 /// Print onto the stream supplied at construction the sequence of
332 /// characters designating the start of an object (referred to as an
333 /// "object" in JSON).
335
336 /// Print onto the stream supplied at construction the sequence of
337 /// characters designating the end of an object (referred to as an "object" in JSON).
338 ///
339 /// \pre The behavior is undefined unless this `Formatter` is
340 /// currently formatting an object.
342
343 /// Print onto the stream supplied at construction the sequence of
344 /// characters designating the start of an array (referred to as an "array"
345 /// in JSON). Optionally specify `formatAsEmptyArray` denoting if the
346 /// array being opened should be formatted as an empty array. If
347 /// `formatAsEmptyArray` is not specified then the array being opened is formatted as an array having elements.
348 ///
349 /// \note Note that the formatting (and
350 /// as a consequence the `formatAsEmptyArray`) is relevant only if this
351 /// formatter encodes in the pretty style and is ignored otherwise.
352 void openArray(bool formatAsEmptyArray = false);
353
354 /// Print onto the stream supplied at construction the sequence of
355 /// characters designating the end of an array (referred to as an "array"
356 /// in JSON). Optionally specify `formatAsEmptyArray` denoting if the
357 /// array being closed should be formatted as an empty array. If
358 /// `formatAsEmptyArray` is not specified then the array being closed is
359 /// formatted as an array having elements.
360 ///
361 /// \pre The behavior is undefined unless this `Formatter` is currently formatting an array.
362 ///
363 /// \note Note that the formatting (and as a consequence the `formatAsEmptyArray`) is
364 /// relevant only if this formatter encodes in the pretty style and is
365 /// ignored otherwise.
366 void closeArray(bool formatAsEmptyArray = false);
367
368 /// Print onto the stream supplied at construction the sequence of
369 /// characters designating the start of a member (referred to as a
370 /// "name/value pair" in JSON) having the specified `name`. Return 0 on
371 /// success and a non-zero value otherwise.
372 int openMember(const bsl::string_view& name);
373
374 /// Print onto the stream supplied at construction the value corresponding
375 /// to a null element. Return 0 on success and a non-zero value otherwise.
376 int putNullValue();
377
378 /// Print onto the stream supplied at construction the specified `value`.
379 /// Optionally specify `options` according which `value` should be encoded.
380 /// Return 0 on success and a non-zero value otherwise.
381 template <class TYPE>
382 int putValue(const TYPE& value, const EncoderOptions *options = 0);
383
384 /// Print onto the stream supplied at construction the sequence of
385 /// characters designating the end of an member (referred to as a "name/value pair" in JSON).
386 ///
387 /// \pre The behavior is undefined unless this
388 /// `Formatter` is currently formatting a member.
390
391 /// Print onto the stream supplied at construction the sequence of
392 /// characters designating an array element separator (i.e., `,`).
393 ///
394 /// \pre The behavior is undefined unless this `Formatter` is currently formatting a
395 /// member.
397
398 // ACCESSORS
399
400 /// Return the number of currently open nested objects or arrays.
401 int nestingDepth() const;
402};
403
404// ============================================================================
405// INLINE DEFINITIONS
406// ============================================================================
407
408 // ---------------
409 // class Formatter
410 // ---------------
411
412// PRIVATE MANIPULATORS
413inline
414void Formatter::indent()
415{
416 bdlb::Print::indent(d_outputStream, d_indentLevel, d_spacesPerLevel);
417}
418
419// PRIVATE ACCESSORS
420inline
421bool Formatter::isArrayElement() const
422{
423 BSLS_ASSERT(d_callSequence.length() >= 1);
424
425 return d_callSequence[d_callSequence.length() - 1];
426}
427
428// MANIPULATORS
429inline
431{
432 if (d_usePrettyStyle && isArrayElement()) {
433 indent();
434 }
435 d_outputStream << "null";
436 return 0;
437}
438
439template <class TYPE>
440int Formatter::putValue(const TYPE& value, const EncoderOptions *options)
441{
442 if (d_usePrettyStyle && isArrayElement()) {
443 indent();
444 }
445 return baljsn::PrintUtil::printValue(d_outputStream, value, options);
446}
447
448// ACCESSORS
449inline
451{
452 // The call sequence contains a "dummy" initial element, so subtract one
453 // from the length.
454 return static_cast<int>(d_callSequence.length()) - 1;
455}
456
457} // close package namespace
458
459
460
461#endif
462
463// ----------------------------------------------------------------------------
464// Copyright 2017 Bloomberg Finance L.P.
465//
466// Licensed under the Apache License, Version 2.0 (the "License");
467// you may not use this file except in compliance with the License.
468// You may obtain a copy of the License at
469//
470// http://www.apache.org/licenses/LICENSE-2.0
471//
472// Unless required by applicable law or agreed to in writing, software
473// distributed under the License is distributed on an "AS IS" BASIS,
474// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
475// See the License for the specific language governing permissions and
476// limitations under the License.
477// ----------------------------- END-OF-FILE ----------------------------------
478
479/** @} */
480/** @} */
481/** @} */
Definition baljsn_encoderoptions.h:290
Definition baljsn_formatter.h:254
int openMember(const bsl::string_view &name)
void closeArray(bool formatAsEmptyArray=false)
Formatter(bsl::ostream &stream, bool usePrettyStyle, int initialIndentLevel, int spacesPerLevel, bool escapeForwardSlash, bslma::Allocator *basicAllocator=0)
void openArray(bool formatAsEmptyArray=false)
int putValue(const TYPE &value, const EncoderOptions *options=0)
Definition baljsn_formatter.h:440
~Formatter()=default
Destroy this object.
void addArrayElementSeparator()
Formatter(bsl::ostream &stream, bool usePrettyStyle=false, int initialIndentLevel=0, int spacesPerLevel=0, bslma::Allocator *basicAllocator=0)
int nestingDepth() const
Return the number of currently open nested objects or arrays.
Definition baljsn_formatter.h:450
int putNullValue()
Definition baljsn_formatter.h:430
Definition bdlc_bitarray.h:525
bsl::size_t length() const
Return the number of bits in this array.
Definition bdlc_bitarray.h:1886
Definition bslstl_stringview.h:471
Definition bslma_allocator.h:545
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1976
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
Definition baljsn_convertfromjsonoptions.h:112
static int printValue(bsl::ostream &stream, bool value, const EncoderOptions *options=0)
Definition baljsn_printutil.h:442
static bsl::ostream & indent(bsl::ostream &stream, int level, int spacesPerLevel=4)