BDE 4.14.0 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
227#include <baljsn_printutil.h>
228
229#include <bdlb_print.h>
230
231#include <bdlc_bitarray.h>
232
233#include <bsl_ostream.h>
234
235#include <bsls_assert.h>
236#include <bsls_review.h>
237
238#include <bsl_string.h>
239#include <bsl_string_view.h>
240
241
242namespace baljsn {
243
244class EncoderOptions;
245
246 // ===============
247 // class Formatter
248 // ===============
249
250/// This class implements a formatter providing operations for rendering JSON
251/// text elements to an output stream (supplied at construction) according to a
252/// set of formatting options (also supplied at construction).
253///
254/// See @ref baljsn_formatter
256
257 // DATA
258 bsl::ostream& d_outputStream; // stream for output (held, not
259 // owned)
260
261 bool d_usePrettyStyle; // encoding style
262
263 int d_indentLevel; // current indentation level
264
265 int d_spacesPerLevel; // spaces per indentation level
266
267 bdlc::BitArray d_callSequence; // array specifying the sequence
268 // in which the 'openObject' and
269 // 'openArray' methods were
270 // called. An 'openObject' call
271 // is represented by 'false' and
272 // an 'openArray' call by 'true'.
273
274 // PRIVATE MANIPULATORS
275
276 /// Unconditionally print onto the stream supplied at construction the
277 /// sequence of whitespace characters for the proper indentation of an
278 /// element at the current indentation level. Note that this method does
279 /// not check that `d_usePrettyStyle` is `true` before indenting.
280 void indent();
281
282 // PRIVATE ACCESSORS
283
284 /// Return `true` if the value being encoded is an element of an array, and
285 /// `false` otherwise. A value is identified as an element of an array if
286 /// `openArray` was called on this object and was not subsequently followed
287 /// by either an `openObject` or `closeArray` call.
288 bool isArrayElement() const;
289
290 public:
291 // CREATORS
292
293 /// Create a `Formatter` object using the specified `stream`. Optionally
294 /// specify `usePrettyStyle` to inform the formatter whether the pretty
295 /// encoding style should be used when writing data. If `usePrettyStyle`
296 /// is not specified then the data is written in a compact style. If
297 /// `usePrettyStyle` is specified, additionally specify
298 /// `initialIndentLevel` and `spacesPerLevel` to provide the initial
299 /// indentation level and spaces per level at which the data should be
300 /// formatted. If `initialIndentLevel` or `spacesPerLevel` is not
301 /// specified then an initial value of `0` is used for both parameters. If
302 /// `usePrettyStyle` is `false` then `initialIndentLevel` and
303 /// `spacesPerLevel` are both ignored. Optionally specify a
304 /// `basicAllocator` used to supply memory. If `basicAllocator` is 0, the
305 /// currently installed default allocator is used.
306 Formatter(bsl::ostream& stream,
307 bool usePrettyStyle = false,
308 int initialIndentLevel = 0,
309 int spacesPerLevel = 0,
310 bslma::Allocator *basicAllocator = 0);
311
312 /// Destroy this object.
313 ~Formatter() = default;
314
315 // MANIPULATORS
316
317 /// Print onto the stream supplied at construction the sequence of
318 /// characters designating the start of an object (referred to as an
319 /// "object" in JSON).
321
322 /// Print onto the stream supplied at construction the sequence of
323 /// characters designating the end of an object (referred to as an "object"
324 /// in JSON). The behavior is undefined unless this `Formatter` is
325 /// currently formatting an object.
327
328 /// Print onto the stream supplied at construction the sequence of
329 /// characters designating the start of an array (referred to as an "array"
330 /// in JSON). Optionally specify `formatAsEmptyArray` denoting if the
331 /// array being opened should be formatted as an empty array. If
332 /// `formatAsEmptyArray` is not specified then the array being opened is
333 /// formatted as an array having elements. Note that the formatting (and
334 /// as a consequence the `formatAsEmptyArray`) is relevant only if this
335 /// formatter encodes in the pretty style and is ignored otherwise.
336 void openArray(bool formatAsEmptyArray = false);
337
338 /// Print onto the stream supplied at construction the sequence of
339 /// characters designating the end of an array (referred to as an "array"
340 /// in JSON). Optionally specify `formatAsEmptyArray` denoting if the
341 /// array being closed should be formatted as an empty array. If
342 /// `formatAsEmptyArray` is not specified then the array being closed is
343 /// formatted as an array having elements. The behavior is undefined
344 /// unless this `Formatter` is currently formatting an array. Note that
345 /// the formatting (and as a consequence the `formatAsEmptyArray`) is
346 /// relevant only if this formatter encodes in the pretty style and is
347 /// ignored otherwise.
348 void closeArray(bool formatAsEmptyArray = false);
349
350 /// Print onto the stream supplied at construction the sequence of
351 /// characters designating the start of a member (referred to as a
352 /// "name/value pair" in JSON) having the specified `name`. Return 0 on
353 /// success and a non-zero value otherwise.
354 int openMember(const bsl::string_view& name);
355
356 /// Print onto the stream supplied at construction the value corresponding
357 /// to a null element.
358 void putNullValue();
359
360 /// Print onto the stream supplied at construction the specified `value`.
361 /// Optionally specify `options` according which `value` should be encoded.
362 /// Return 0 on success and a non-zero value otherwise.
363 template <class TYPE>
364 int putValue(const TYPE& value, const EncoderOptions *options = 0);
365
366 /// Print onto the stream supplied at construction the sequence of
367 /// characters designating the end of an member (referred to as a
368 /// "name/value pair" in JSON). The behavior is undefined unless this
369 /// `Formatter` is currently formatting a member.
371
372 /// Print onto the stream supplied at construction the sequence of
373 /// characters designating an array element separator (i.e., `,`). The
374 /// behavior is undefined unless this `Formatter` is currently formatting a
375 /// member.
377
378 // ACCESSORS
379
380 /// Return the number of currently open nested objects or arrays.
381 int nestingDepth() const;
382};
383
384// ============================================================================
385// INLINE DEFINITIONS
386// ============================================================================
387
388 // ---------------
389 // class Formatter
390 // ---------------
391
392// PRIVATE MANIPULATORS
393inline
394void Formatter::indent()
395{
396 bdlb::Print::indent(d_outputStream, d_indentLevel, d_spacesPerLevel);
397}
398
399// PRIVATE ACCESSORS
400inline
401bool Formatter::isArrayElement() const
402{
403 BSLS_ASSERT(d_callSequence.length() >= 1);
404
405 return d_callSequence[d_callSequence.length() - 1];
406}
407
408// MANIPULATORS
409inline
411{
412 if (d_usePrettyStyle && isArrayElement()) {
413 indent();
414 }
415 d_outputStream << "null";
416}
417
418template <class TYPE>
419int Formatter::putValue(const TYPE& value, const EncoderOptions *options)
420{
421 if (d_usePrettyStyle && isArrayElement()) {
422 indent();
423 }
424 return baljsn::PrintUtil::printValue(d_outputStream, value, options);
425}
426
427// ACCESSORS
428inline
430{
431 // The call sequence contains a "dummy" initial element, so subtract one
432 // from the length.
433 return static_cast<int>(d_callSequence.length()) - 1;
434}
435
436} // close package namespace
437
438
439
440#endif
441
442// ----------------------------------------------------------------------------
443// Copyright 2017 Bloomberg Finance L.P.
444//
445// Licensed under the Apache License, Version 2.0 (the "License");
446// you may not use this file except in compliance with the License.
447// You may obtain a copy of the License at
448//
449// http://www.apache.org/licenses/LICENSE-2.0
450//
451// Unless required by applicable law or agreed to in writing, software
452// distributed under the License is distributed on an "AS IS" BASIS,
453// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
454// See the License for the specific language governing permissions and
455// limitations under the License.
456// ----------------------------- END-OF-FILE ----------------------------------
457
458/** @} */
459/** @} */
460/** @} */
Definition baljsn_encoderoptions.h:262
Definition baljsn_formatter.h:255
int openMember(const bsl::string_view &name)
void closeArray(bool formatAsEmptyArray=false)
void openArray(bool formatAsEmptyArray=false)
int putValue(const TYPE &value, const EncoderOptions *options=0)
Definition baljsn_formatter.h:419
~Formatter()=default
Destroy this object.
void putNullValue()
Definition baljsn_formatter.h:410
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:429
Definition bdlc_bitarray.h:521
bsl::size_t length() const
Return the number of bits in this array.
Definition bdlc_bitarray.h:1815
Definition bslstl_stringview.h:441
Definition bslma_allocator.h:457
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1804
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
Definition baljsn_datumdecoderoptions.h:113
static int printValue(bsl::ostream &stream, bool value, const EncoderOptions *options=0)
Definition baljsn_printutil.h:410
static bsl::ostream & indent(bsl::ostream &stream, int level, int spacesPerLevel=4)