// baljsn_encoder.h -*-C++-*- #ifndef INCLUDED_BALJSN_ENCODER #define INCLUDED_BALJSN_ENCODER #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a JSON encoder for 'bdlat'-compatible types. // //@CLASSES: // baljsn::Encoder: JSON decoder for 'bdlat'-compliant types // //@SEE_ALSO: baljsn_decoder, baljsn_printutil // //@DESCRIPTION: This component provides a class, 'baljsn::Encoder', for // encoding value-semantic objects in the JSON format. In particular, the // 'class' contains a parameterized 'encode' function that encodes an object // into a specified stream. There are two overloaded versions of this // function: // //: o one that writes to a 'bsl::streambuf' //: o one that writes to an 'bsl::ostream' // // This component can be used with types that support the 'bdlat' framework // (see the 'bdlat' package for details), which is a compile-time interface for // manipulating struct-like and union-like objects. In particular, types // generated by the 'bas_codegen.pl' tool, and other dynamic types, can be // encoded using this 'class'. The 'encode' function can be invoked on any // object that satisfies the requirements of a sequence, choice, or array // object as defined in the 'bdlat_sequencefunctions', 'bdlat_choicefunctions', // and 'bdlat_arrayfunctions' components. // // Although the JSON format is easy to read and write and is very useful for // debugging, it is relatively expensive to encode and decode and relatively // bulky to transmit. It is more efficient to use a binary encoding (such as // BER) if the encoding format is under your control (see 'balber_berencoder'). // // Refer to the details of the JSON encoding format supported by this encoder // in the package documentation file (doc/baljsn.txt). // ///Usage ///----- // This section illustrates intended use of this component. // ///Example 1: Encoding a 'bas_codegen.pl'-Generated Object into JSON ///----------------------------------------------------------------- // Consider that we want to exchange an employee's information between two // processes. To allow this information exchange we will define the XML schema // representation for that class, use 'bas_codegen.pl' to create the 'Employee' // 'class' for storing that information, populate an 'Employee' object, and // encode that object using the 'baljsn' encoder. // // First, we will define the XML schema inside a file called 'employee.xsd': //.. // <?xml version='1.0' encoding='UTF-8'?> // <xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema' // xmlns:test='http://bloomberg.com/schemas/test' // targetNamespace='http://bloomberg.com/schemas/test' // elementFormDefault='unqualified'> // // <xs:complexType name='Address'> // <xs:sequence> // <xs:element name='street' type='xs:string'/> // <xs:element name='city' type='xs:string'/> // <xs:element name='state' type='xs:string'/> // </xs:sequence> // </xs:complexType> // // <xs:complexType name='Employee'> // <xs:sequence> // <xs:element name='name' type='xs:string'/> // <xs:element name='homeAddress' type='test:Address'/> // <xs:element name='age' type='xs:int'/> // </xs:sequence> // </xs:complexType> // // <xs:element name='Employee' type='test:Employee'/> // // </xs:schema> //.. // Then, we will use the 'bas_codegen.pl' tool, to generate the C++ classes for // this schema. The following command will generate the header and // implementation files for the all the classes in the 'test_messages' // components in the current directory: //.. // $ bas_codegen.pl -m msg -p test xsdfile.xsd //.. // Next, we will populate a 'test::Employee' object: //.. // test::Employee employee; // employee.name() = "Bob"; // employee.homeAddress().street() = "Lexington Ave"; // employee.homeAddress().city() = "New York City"; // employee.homeAddress().state() = "New York"; // employee.age() = 21; //.. // Then, we will create a 'baljsn::Encoder' object: //.. // baljsn::Encoder encoder; //.. // Now, we will output this object in the JSON format by invoking the 'encode' // method of the encoder. We will also create a 'baljsn::EncoderOptions' // object that allows us to specify that the encoding should be done in a // pretty format, and what the initial indent level and spaces per level should // be. We will then pass that object to the 'encode' method: //.. // bsl::ostringstream os; // // baljsn::EncoderOptions options; // options.setEncodingStyle(baljsn::EncoderOptions::e_PRETTY); // options.setInitialIndentLevel(1); // options.setSpacesPerLevel(4); // // const int rc = encoder.encode(os, employee, options); // assert(!rc); // assert(os); //.. // Finally, we will verify that the output is as expected: //.. // const char EXP_OUTPUT[] = " {\n" // " \"name\" : \"Bob\",\n" // " \"homeAddress\" : {\n" // " \"street\" : \"Lexington Ave\",\n" // " \"city\" : \"New York City\",\n" // " \"state\" : \"New York\"\n" // " },\n" // " \"age\" : 21\n" // " }\n"; // // assert(EXP_OUTPUT == os.str()); //.. #include <balscm_version.h> #include <baljsn_encoderoptions.h> #include <baljsn_formatter.h> #include <baljsn_printutil.h> #include <bdlat_attributeinfo.h> #include <bdlat_choicefunctions.h> #include <bdlat_customizedtypefunctions.h> #include <bdlat_enumfunctions.h> #include <bdlat_formattingmode.h> #include <bdlat_nullablevalueutil.h> #include <bdlat_selectioninfo.h> #include <bdlat_sequencefunctions.h> #include <bdlat_typecategory.h> #include <bdlat_valuetypefunctions.h> #include <bdlb_print.h> #include <bdlsb_memoutstreambuf.h> #include <bsla_maybeunused.h> #include <bsls_assert.h> #include <bsls_types.h> #include <bsl_cstddef.h> #include <bsl_iostream.h> #include <bsl_ostream.h> #include <bsl_sstream.h> #include <bsl_streambuf.h> #include <bsl_string.h> #include <bsl_string_view.h> #include <bsl_vector.h> namespace BloombergLP { namespace baljsn { // ============= // class Encoder // ============= class Encoder { // This class provides a mechanism for encoding value-semantic objects in // the JSON format. The 'encode' methods are function templates that will // encode any object that meets the requirements of a sequence, choice, or // array object as defined in the 'bdlat_sequencefunctions', // 'bdlat_choicefunctions', and 'bdlat_choicefunctions' components // respectively. These generic frameworks provide a common compile-time // interface for accessing struct-like and union-like objects. In // particular, the types generated by 'bas_codegen.pl' provide the // necessary interface and can be encoded using this component. // DATA bsl::ostringstream d_logStream; // stream used for logging // NOT IMPLEMENTED Encoder(const Encoder&); // PRIVATE MANIPULATORS bsl::ostream& logStream(); // Return the stream for logging. public: // CREATORS explicit Encoder(bslma::Allocator *basicAllocator = 0); // Create a encoder object. Optionally specify a 'basicAllocator' used // to supply memory. If 'basicAllocator' is 0, the currently installed // default allocator is used. //! ~Encoder() = default; // Destroy this object. // MANIPULATORS template <class TYPE> int encode(bsl::streambuf *streamBuf, const TYPE& value, const EncoderOptions& options); template <class TYPE> int encode(bsl::streambuf *streamBuf, const TYPE& value, const EncoderOptions *options); // Encode the specified 'value', of (template parameter) 'TYPE', in the // JSON format using the specified 'options' and output it onto the // specified 'streamBuf'. Specifying a nullptr 'options' is equivalent // to passing a default-constructed DecoderOptions in 'options'. // 'TYPE' shall be a 'bdlat'-compatible sequence, choice, or array // type, or a 'bdlat'-compatible dynamic type referring to one of those // types. Return 0 on success, and a non-zero value otherwise. template <class TYPE> int encode(bsl::ostream& stream, const TYPE& value, const EncoderOptions& options); template <class TYPE> int encode(bsl::ostream& stream, const TYPE& value, const EncoderOptions *options); // Encode the specified 'value', of (template parameter) 'TYPE', in the // JSON format using the specified 'options' and output it onto the // specified 'stream'. Specifying a nullptr 'options' is equivalent to // passing a default-constructed DecoderOptions in 'options'. 'TYPE' // shall be a 'bdlat'-compatible choice, or array type, or a // 'bdlat'-compatible dynamic type referring to one of those types. // Return 0 on success, and a non-zero value otherwise. template <class TYPE> int encode(bsl::streambuf *streamBuf, const TYPE& value); // Encode the specified 'value' of (template parameter) 'TYPE' into the // specified 'streamBuf'. Return 0 on success, and a non-zero value // otherwise. // // DEPRECATED: Use the 'encode' function passed a reference to a // non-modifiable 'EncoderOptions' object instead. template <class TYPE> int encode(bsl::ostream& stream, const TYPE& value); // Encode the specified 'value' of (template parameter) 'TYPE' into the // specified 'stream'. Return 0 on success, and a non-zero value // otherwise. Note that 'stream' will be invalidated if the encoding // failed. // // DEPRECATED: Use the 'encode' function passed a reference to a // non-modifiable 'EncoderOptions' object instead. // ACCESSORS bsl::string loggedMessages() const; // Return a string containing any error, warning, or trace messages // that were logged during the last call to the 'encode' method. The // log is reset each time 'encode' is called. }; // ============================= // struct Encoder_EncodeImplUtil // ============================= struct Encoder_EncodeImplUtil { // This component-private utility 'struct' provides a suite of functions // that encode 'bdlat' types in the JSON format. // TYPES typedef Encoder_EncodeImplUtil ThisUtil; // 'ThisUtil' is a convenience alias for this utility 'struct'. typedef int FormattingMode; // 'FormattingMode' is an alias to the type of an 'int'-valued // 'bdlat_FormattingMode' bit-field. A 'FormattingMode' value is not // valid unless it is equal to an enumerator of 'bdlat_FormattingMode' // or a valid bitwise-or of two or more such enumerators. See the // component-level documentation of {'bdlat_formattingmode'} for a // description of the set of valid formatting-mode values. // CLASS METHODS // Document Encoding static void openDocument(bsl::ostream *outputStream, const EncoderOptions& options); // Print the sequence of characters that designate the start of a JSON // document to the specified 'outputStream' according to the specified // encoding 'options'. See the component-level documentation of // {'baljsn_encoderoptions'} for a description of the effects, if any, // of each option in the 'options' on the start of a JSON document. static void closeDocument(bsl::ostream *outputStream, const EncoderOptions& options); // Print the sequence of characters that designate the end of a JSON // document to the specified 'outputStream' according to the specified // encoding 'options'. See the component-level documentation of // {'baljsn_encoderoptions'} for a description of the effects, if any, // of each option in the 'options' on the end of a JSON document. // Value Encoding template <class TYPE> static int encode(bsl::ostream *jsonStream, const TYPE& value, const EncoderOptions& options = EncoderOptions()); // Encode the JSON representation of the specified 'value' to the // specified 'jsonStream'. Optionally specify 'options' to configure // aspects of the JSON representation of the 'value'. If 'options' is // not specified, the default 'EncoderOptions' value is used. Return 0 // on success, and a non-zero value otherwise. The behavior is // undefined unless the specified 'TYPE' satisfies both the static and // dynamic requirements of one 'bdlat' type-category concept. See the // component-level documentation of {'baljsn_encoderoptions'} for a // description of the effects, if any, of each option in the 'options' // on the JSON representation of the 'value'. See the package-level // documentation of {'bdlat'} for a description of the available // type-category concepts. template <class TYPE> static int encode(bsl::ostream *logStream, bsl::ostream *jsonStream, const TYPE& value, const EncoderOptions& options = EncoderOptions()); // Encode the JSON representation of the specified 'value' to the // specified 'jsonStream'. If this operation is not successful, load // an unspecified, human-readable description of the error condition to // the specified 'logStream'. Optionally specify 'options' to // configure aspects of the JSON representation of the 'value'. If // 'options' is not specified, the default 'EncoderOptions' value is // used. Return 0 on success, and a non-zero value otherwise. The // behavior is undefined unless the specified 'TYPE' satisfies both the // static and dynamic requirements of one 'bdlat' type-category // concept. See the component-level documentation of // {'baljsn_encoderoptions'} for a description of the effects, if any, // of each option in the 'options' on the JSON representation of the // 'value'. See the package-level documentation of {'bdlat'} for an // introduction to the requirements of 'bdlat' type-category concepts. template <class TYPE> static int encode(bool *isValueEmpty, Formatter *formatter, bsl::ostream *logStream, const TYPE& value, FormattingMode formattingMode, const EncoderOptions& options, bool isFirstMember); // Encode the JSON representation of the specified 'value' to the // specified JSON 'formatter', according to the specified // 'formattingMode'. If the representation contains no text, load the // value 'true' into 'isValueEmpty' and the value 'false' otherwise. // If the specified 'isFirstMember' option is 'true', then the // representation of the value contains no leading sequence delimiter, // and does contain such a delimiter if the remaining representation is // non-empty otherwise. Use the specified 'options' to configure // aspects of the JSON representation of the 'value'. If this // operation is not successful, load an unspecified, human-readable // description of the error condition to the specified 'logStream'. // Return 0 on success, and a non-zero value otherwise. The behavior // is undefined unless the specified 'TYPE' satisfies both the static // and dynamic requirements of one of the 'bdlat' type-category // concepts. See the component-level documentation of // {'baljsn_encoderoptions'} for a description of the effects, if any, // of each option in the 'options' on the JSON representation of the // 'value'. See the package-level documentation of {'bdlat'} for an // introduction to the requirements of 'bdlat' type-category concepts. // Validation template <class TYPE> static int validate(bsl::ostream *logStream, const TYPE& value, bdlat_TypeCategory::Choice category); template <class TYPE, class CATEGORY> static int validate(bsl::ostream *logStream, const TYPE& value, CATEGORY category); // Determine if the specified 'value' having the specified 'bdlat' // 'category' satisfies the requirements for encoding using this // component. If the 'value' meets the encoding requirements, return // 0, otherwise load an unspecified, human-readable description of the // requirements that are not satisfied by the 'value' and return a // non-zero value. For values satisfying the 'bdlat' 'Choice' // type-category concept, the value of the 'selectionId' attribute // must not be undefined. For values satisfying the requirements of // other 'bdlat' type-category concepts, there are no further // requirements for encoding using this component. See the // package-level documentation of {'bdlat'} for an introduction to the // requirements of 'bdlat' type-category concepts. template <class TYPE> static int validateChoice(bsl::ostream *logStream, const TYPE& value); // Determine if the specified 'value' satisfies the requirements for // encoding using this component. If the 'value' meets the encoding // requirements, return 0, otherwise load an unspecified, // human-readable description of the requirements that are not // satisfied by the 'value' to the specified 'logStream' and return a // non-zero value. The 'value' is required to not have an undefined // 'selectionId'. // Encoding Values That Have Specific Type Categories static int encodeCharArray(Formatter *formatter, const bsl::vector<char>& value, const EncoderOptions& options); // Encode the JSON representation of the specified 'value' to the // specified JSON 'formatter'. Use the specified 'options' to // configure aspects of the JSON representation of the 'value'. Return // 0 on success, and a non-zero value otherwise. template <class TYPE> static int encodeSimpleValue(Formatter *formatter, const TYPE& value, const EncoderOptions& options); // Encode the JSON representation of the specified 'value' to the // specified JSON 'formatter'. Use the specified 'options' to // configure aspects of the JSON representation of the 'value'. Return // 0 on success, and a non-zero value otherwise. The behavior is // undefined unless the specified 'TYPE' satisfies both the static and // dynamic requirements of the 'Simple' 'bdlat' type-category concept. // See the component-level documentation of {'baljsn_encoderoptions'} // for a description of the effects, if any, of each option in the // 'options' on the JSON representation of the 'value'. See the // package-level documentation of {'bdlat'} for an introduction to the // requirements of 'bdlat' type-category concepts. // Encoding Prefixes and Suffixes static void encodeObjectPrefix(bool *isPrefixEmpty, Formatter *formatter, FormattingMode formattingMode); // If the specified 'formattingMode' does not have the // 'bdlat_FormattingMode::e_UNTAGGED' bit set, encode a "left brace" // JSON token to the specified 'formatter', and encoding nothing to the // 'formatter' otherwise. If this operation encodes a token to the // formatter, load the value 'false' to the specified 'isPrefixEmpty', // and the value 'true' otherwise. static void encodeObjectSuffix(bool *isSuffixEmpty, Formatter *formatter, FormattingMode formattingMode); // If the specified 'formattingMode' does not have the // 'bdlat_FormattingMode::e_UNTAGGED' bit set, encode a "right brace" // JSON token to the specified 'formatter', and encoding nothing to the // 'formatter' otherwise. If this operation encodes a token to the // 'formatter', load the value 'false' to the specified // 'isSuffixEmpty', and the value 'true' otherwise. // Encoding Arrays That Have Specific Shapes static void encodeEmptyArray(Formatter *formatter); // Encode the representation of the empty-array JSON value to the // specified 'formatter'. template <class TYPE> static int encodeNonEmptyArray(Formatter *formatter, bsl::ostream *logStream, const TYPE& value, const EncoderOptions& options); // Encode the JSON representation of the specified 'value' to the // specified JSON 'formatter'. Use the specified 'options' to // configure aspects of the JSON representation of the 'value'. If // this operation is not successful, load an unspecified, // human-readable description of the error condition to the specified // 'logStream'. Return 0 on success, and a non-zero value otherwise. // The behavior is undefined unless the 'value' is non-empty and the // specified 'TYPE' satisfies both the static and dynamic requirements // of the 'Array' 'bdlat' type-category concept. See the // component-level documentation of {'baljsn_encoderoptions'} for a // description of the effects, if any, of each option in the 'options' // on the JSON representation of the 'selection'. See the // package-level documentation of {'bdlat'} for an introduction to the // requirements of 'bdlat' type-category concepts. // Encoding Generalized Members static int encodeMember(bool *isMemberEmpty, Formatter *formatter, bsl::ostream *logStream, const bsl::string_view& memberName, const bsl::vector<char>& member, FormattingMode formattingMode, const EncoderOptions& options, bool isFirstMember, bdlat_TypeCategory::Array category); template <class TYPE> static int encodeMember(bool *isMemberEmpty, Formatter *formatter, bsl::ostream *logStream, const bsl::string_view& memberName, const TYPE& member, FormattingMode formattingMode, const EncoderOptions& options, bool isFirstMember, bdlat_TypeCategory::Array category); template <class TYPE, class OTHER_CATEGORY> static int encodeMember(bool *isMemberEmpty, Formatter *formatter, bsl::ostream *logStream, const bsl::string_view& memberName, const TYPE& member, FormattingMode formattingMode, const EncoderOptions& options, bool isFirstMember, OTHER_CATEGORY category); // Encode the JSON representation of the specified object 'member' // having the specified 'memberName' to the specified JSON 'formatter', // according to the specified 'formattingMode'. If the representation // contains no text, load the value 'true' to 'isMemberEmpty' and the // value 'false' otherwise. If the specified 'isFirstMember' option is // 'true', then the representation of the member contains no leading // sequence delimiter, and does contain such a delimiter otherwise. // Use the specified 'options' to configure aspects of the JSON // representation of the 'member'. If this operation is not // successful, load an unspecified, human-readable description of the // error condition to the specified 'logStream'. Return 0 on success, // and a non-zero value otherwise. The behavior is undefined unless // the specified 'TYPE' satisfies both the static and dynamic // requirements of the specified 'category' 'bdlat' type-category // concept. See the component-level documentation of // {'baljsn_encoderoptions'} for a description of the effects, if any, // of each option in the 'options' on the JSON representation of the // 'selection'. See the package-level documentation of {'bdlat'} for // an introduction to the requirements of 'bdlat' type-category // concepts. static int encodeMemberPrefix(Formatter *formatter, bsl::ostream *logStream, const bsl::string_view& memberName, bool isFirstMember); static int encodeMemberPrefix(Formatter *formatter, bsl::ostream *logStream, const bsl::string_view& memberName, FormattingMode formattingMode, bool isFirstMember); static int encodeMemberPrefix(bool *isPrefixEmpty, Formatter *formatter, bsl::ostream *logStream, const bsl::string_view& memberName, FormattingMode formattingMode, bool isFirstMember); // If the specified 'isFirstMember' flag is 'false', encode a "comma" // JSON token to the specified 'formatter', and do not encode a "comma" // JSON token otherwise. If the specified 'formattingMode' does not // have the 'bdlat_FormattingMode::e_UNTAGGED' bit set, encode a JSON // "string" token having the specified 'memberName' contents, and // encode a JSON "colon" token after the string, and do not encode // these tokens otherwise. If this operation is not successful, load // an unspecified, human-readable description of the error condition to // the specified 'logStream'. Optionally specify 'isPrefixEmpty'. If // this operation encodes a token to the formatter, load the value // 'false' to 'isPrefixEmpty' if specified, and the value 'true' // otherwise. Return 0 on success, and a non-zero value otherwise. }; // ============================== // struct Encoder_ValueDispatcher // ============================== class Encoder_ValueDispatcher { // this component-private class provides a function object used to encode // values that satisfy one of the 'bdlat' type-category concepts. // // This class's constructor closes over the 'formatter', 'logStream', and // 'options' parameters that are shared between all encoding operations // provided in this component. The function-call operator of this class // provides an overload set that accepts an object that satisfies one of // the 'bdlat' type-category concepts, and a 'bdlat_TypeCategory' tag type // that corresponds to the object's 'bdlat' type category. Each // function-call-operator overload encodes a JSON representation of the // specified value to the 'formatter' supplied on construction. public: // TYPES typedef int FormattingMode; // 'FormattingMode' is an alias to the type of an 'int'-valued // 'bdlat_FormattingMode' bit-field. A 'FormattingMode' value is not // valid unless it is equal to an enumerator of 'bdlat_FormattingMode' // or a valid bitwise-or of two or more such enumerators. See the // component-level documentation of {'bdlat_formattingmode'} for a // description of the set of valid formatting-mode values. private: // DATA Formatter *d_formatter_p; // wrapper around the output stream that determines the whitespace to // emit around each JSON token bsl::ostream *d_logStream_p; // human-readable descriptions of all encountered error conditions const EncoderOptions *d_options_p; // options set by the caller of the encoding operation that controls // some aspects of the token sequence to emit FormattingMode d_formattingMode; // formatting mode of the value bool d_isNextObjectFirst; // if 'false' then emit a leading sequence delimiter before the // representation of the value, otherwise do not emit a leading // sequence delimiter public: // CREATORS Encoder_ValueDispatcher(Formatter *formatter, bsl::ostream *logStream, FormattingMode formattingMode, bool isNextObjectFirst, const EncoderOptions& options); // Construct an 'Encoder_ValueDispatcher' object having the specified // 'formatter', 'logStream', 'formattingMode', 'isNextObjectFirst', and // 'options' attributes. // MANIPULATORS int operator()(const bsl::vector<char>& value, bdlat_TypeCategory::Array category); template <class TYPE> int operator()(const TYPE& value, bdlat_TypeCategory::Array category); template <class TYPE> int operator()(const TYPE& value, bdlat_TypeCategory::Choice category); template <class TYPE> int operator()(const TYPE& value, bdlat_TypeCategory::CustomizedType category); template <class TYPE> int operator()(const TYPE& value, bdlat_TypeCategory::DynamicType category); template <class TYPE> int operator()(const TYPE& value, bdlat_TypeCategory::Enumeration category); template <class TYPE> int operator()(const TYPE& value, bdlat_TypeCategory::NullableValue category); template <class TYPE> int operator()(const TYPE& value, bdlat_TypeCategory::Sequence category); template <class TYPE> int operator()(const TYPE& value, bdlat_TypeCategory::Simple category); // Encode the JSON representation of the specified 'value' to the JSON // 'formatter' attribute of this object, according to the // 'formattingMode' attribute of this object. If the representation // contains no text and the 'isFirstSubObject' attribute of this object // is 'true', set the 'isNextObjectFirst' attribute of this object to // 'true', and the value 'false' otherwise. If 'isFirstSubObject' is // 'true', then the representation of the value contains no leading // sequence delimiter, and does contain such a delimiter otherwise. The // 'options' attribute of this object configures aspects of the JSON // representation of the 'value'. If this operation is not successful, // load an unspecified, human-readable description of the error // condition to the 'logStream' attribute of this object. Return 0 on // success, and a non-zero value otherwise. The behavior is undefined // unless the specified 'TYPE' satisfies both the static and dynamic // requirements of the specified 'category' 'bdlat' type-category // concept. See the component-level documentation of // {'baljsn_encoderoptions'} for a description of the effects, if any, // of each option in the 'options' on the JSON representation of the // 'value'. See the package-level documentation of {'bdlat'} for an // introduction to the requirements of 'bdlat' type-category concepts. template <class TYPE> int operator()(const TYPE&, bslmf::Nil); // The behavior of this function is undefined. // ACCESSORS bool isNextObjectFirst() const; // Return the value of the 'isNextObjectFirst' attribute of this // object. }; // ============================ // class Encoder_ElementVisitor // ============================ class Encoder_ElementVisitor { // This component-private class provides a function object that closes over // the 'formatter', 'logStream', and 'options' parameters that are shared // between all encoding operations provided in this component. The // function-call operator of this class provides an overload set that // accepts an "element" object that satisfies one of the 'bdlat' // type-category concepts. // DATA Formatter *d_formatter_p; // wrapper around the output stream that determines the whitespace to // emit around each JSON token bsl::ostream *d_logStream_p; // human-readable descriptions of all encountered error conditions const EncoderOptions *d_options_p; // options set by the caller of the encoding operation that controls // some aspects of the token sequence to emit bool d_isNextElementFirst; // if 'false' then emit a leading sequence delimiter before the // representation of the value, otherwise do not emit a leading // sequence delimiter public: // CREATORS Encoder_ElementVisitor(Formatter *formatter, bsl::ostream *logStream, bool isNextElementFirst, const EncoderOptions& options); // Construct an 'Encoder_ElementVisitor' object having the specified // 'formatter', 'logStream', 'isNextElementFirst', and 'options' // attributes. // MANIPULATORS template <class TYPE> int operator()(const TYPE& element); // Encode the JSON representation of the specified 'value' to the JSON // 'formatter' attribute of this object, according to the // 'formattingMode' attribute of this object. If the // 'isNextElementFirst' attribute of this object is 'true', then the // representation of the value contains no leading sequence delimiter, // and does contain such a delimiter otherwise. The 'options' // attribute of this object configures aspects of the JSON // representation of the 'value'. If this operation is not successful, // load an unspecified, human-readable description of the error // condition to the 'logStream' attribute of this object. Return 0 on // success, and a non-zero value otherwise. The behavior is undefined // unless the specified 'TYPE' satisfies both the static and dynamic // requirements of the specified 'category' 'bdlat' type-category // concept. See the component-level documentation of // {'baljsn_encoderoptions'} for a description of the effects, if any, // of each option in the 'options' on the JSON representation of the // 'value'. See the package-level documentation of {'bdlat'} for an // introduction to the requirements of 'bdlat' type-category concepts. // ACCESSORS bool isNextElementFirst() const; // Return the value of the 'isNextElementFirst' attribute of this // object. }; // =============================== // class Encoder_ElementDispatcher // =============================== class Encoder_ElementDispatcher { // This component-private class provides a function object that closes over // the 'formatter', 'logStream', and 'options' parameters that are shared // between all encoding operations provided in this component. The // function-call operator of this class provides an overload set that // accepts an "element" object that satisfies one of the 'bdlat' // type-category concepts, and an optional 'bdlat_TypeCategory' tag type // that corresponds to the element's 'bdlat' type category. Each // function-call-operator overload encodes a JSON representation of the // specified selection to the 'formatter' supplied on construction. // DATA Formatter *d_formatter_p; // wrapper around the output stream that determines the whitespace to // emit around each JSON token bsl::ostream *d_logStream_p; // human-readable descriptions of all encountered error conditions const EncoderOptions *d_options_p; // options set by the caller of the encoding operation that controls // some aspects of the token sequence to emit bool d_isNextElementFirst; // if 'false' then emit a leading sequence delimiter before the // representation of the value, otherwise do not emit a leading // sequence delimiter public: // CREATORS Encoder_ElementDispatcher(Formatter *formatter, bsl::ostream *logStream, bool isNextElementFirst, const EncoderOptions& options); // Construct an 'Encoder_ElementDispatcher' object having the specified // 'formatter', 'logStream', 'isNextElementFirst', and 'options' // attributes. // MANIPULATORS int operator()(const bsl::vector<char>& element, bdlat_TypeCategory::Array category); template <class TYPE> int operator()(const TYPE& element, bdlat_TypeCategory::Array category); template <class TYPE> int operator()(const TYPE& element, bdlat_TypeCategory::Choice category); template <class TYPE> int operator()(const TYPE& element, bdlat_TypeCategory::CustomizedType category); template <class TYPE> int operator()(const TYPE& element, bdlat_TypeCategory::DynamicType category); template <class TYPE> int operator()(const TYPE& element, bdlat_TypeCategory::Enumeration category); template <class TYPE> int operator()(const TYPE& element, bdlat_TypeCategory::NullableValue category); template <class TYPE> int operator()(const TYPE& element, bdlat_TypeCategory::Sequence category); template <class TYPE> int operator()(const TYPE& element, bdlat_TypeCategory::Simple category); // Encode the JSON representation of the specified 'value' to the JSON // 'formatter' attribute of this object, according to the specified // 'formattingMode'. If the representation contains no text and the // specified 'isFirstElement' is 'true', load the value 'true' into the // specified 'isNextElementFirst', and the value 'false' otherwise. If // 'isFirstElement' is 'true', then the representation of the value // contains no leading sequence delimiter, and does contain such a // delimiter otherwise. The 'options' attribute of this object // configures aspects of the JSON representation of the 'value'. If // this operation is not successful, load an unspecified, // human-readable description of the error condition to the 'logStream' // attribute of this object. Return 0 on success, and a non-zero value // otherwise. The behavior is undefined unless the specified 'TYPE' // satisfies both the static and dynamic requirements of the specified // 'category' 'bdlat' type-category concept. See the component-level // documentation of {'baljsn_encoderoptions'} for a description of the // effects, if any, of each option in the 'options' on the JSON // representation of the 'value'. See the package-level documentation // of {'bdlat'} for an introduction to the requirements of 'bdlat' // type-category concepts. template <class TYPE> int operator()(const TYPE&, bslmf::Nil); // The behavior of this function is undefined. // ACCESSORS bool isNextElementFirst() const; // Return the value of the 'isNextElementFirst' attribute of this // object. }; // ============================== // class Encoder_SelectionVisitor // ============================== class Encoder_SelectionVisitor { // This component-private class provides a function object that closes over // the 'formatter', 'logStream', and 'options' parameters that are shared // between all encoding operations provided in this component. The // function-call operator of this class provides an overload set that // accepts a "selection" object that satisfies one of the 'bdlat' // type-category concepts, and a "selection info" object that describes // various metadata of the selection. public: // TYPES typedef int FormattingMode; // 'FormattingMode' is an alias to the type of an 'int'-valued // 'bdlat_FormattingMode' bit-field. A 'FormattingMode' value is not // valid unless it is equal to an enumerator of 'bdlat_FormattingMode' // or a valid bitwise-or of two or more such enumerators. See the // component-level documentation of {'bdlat_formattingmode'} for a // description of the set of valid formatting-mode values. private: // DATA Formatter *d_formatter_p; // wrapper around the output stream that determines the whitespace to // emit around each JSON token bsl::ostream *d_logStream_p; // human-readable descriptions of all encountered error conditions bool d_isNextObjectFirst; // if 'false' then emit a leading sequence delimiter before the // representation of the value, otherwise do not emit a leading // sequence delimiter const EncoderOptions *d_options_p; // options set by the caller of the encoding operation that controls // some aspects of the token sequence to emit public: // CREATORS Encoder_SelectionVisitor(Formatter *formatter, bsl::ostream *logStream, bool isNextObjectFirst, const EncoderOptions& options); // Construct an 'Encoder_SelectionVisitor' object having the specified // 'formatter', 'logStream', 'isNextObjectFirst', and 'options' // attributes. // MANIPULATORS template <class TYPE, class SELECTION_INFO> int operator()(const TYPE& selection, const SELECTION_INFO& selectionInfo); // Encode the JSON representation of the specified 'selection', having // the name equal to the 'name' attribute of the specified // 'selectionInfo' to the JSON 'formatter' attribute of this object, // according to the specified 'formattingMode'. If the // 'isNextObjectFirst' attribute of this object is 'true', then the // representation of the selection contains no leading sequence // delimiter, and does contain such a delimiter otherwise. The // 'options' attribute of this object configures aspects of the JSON // representation of the 'selection'. If this operation is not // successful, load an unspecified, human-readable description of the // error condition to the 'logStream' attribute of this object. Return // 0 on success, and a non-zero value otherwise. The behavior is // undefined unless the 'selection' satisfies both the static and // dynamic requirements of the 'bdlat' type-category concept // corresponding to the specified 'category'. See the component-level // documentation of {'baljsn_encoderoptions'} for a description of the // effects, if any, of each option in the 'options' on the JSON // representation of the 'selection'. See the package-level // documentation of {'bdlat'} for an introduction to the requirements // of 'bdlat' type-category concepts. // ACCESSORS bool isNextObjectFirst() const; // Return the value of the 'isNextObjectFirst' attribute of this // object. }; // ================================= // class Encoder_SelectionDispatcher // ================================= class Encoder_SelectionDispatcher { // This component-private class provides a function object that closes over // the 'formatter', 'logStream', and 'options' parameters that are shared // between all encoding operations provided in this component. The // function-call operator of this class provides an overload set that // accepts a "selection" object that satisfies one of the 'bdlat' // type-category concepts, and an optional 'bdlat_TypeCategory' tag type // that corresponds to the selections's 'bdlat' type category. Each // function-call-operator overload encodes a JSON representation of the // specified selection to the 'formatter' supplied on construction. public: // TYPES typedef int FormattingMode; // 'FormattingMode' is an alias to the type of an 'int'-valued // 'bdlat_FormattingMode' bit-field. A 'FormattingMode' value is not // valid unless it is equal to an enumerator of 'bdlat_FormattingMode' // or a valid bitwise-or of two or more such enumerators. See the // component-level documentation of {'bdlat_formattingmode'} for a // description of the set of valid formatting-mode values. private: // DATA Formatter *d_formatter_p; // wrapper around the output stream that determines the whitespace to // emit around each JSON token bsl::ostream *d_logStream_p; // human-readable descriptions of all encountered error conditions const EncoderOptions *d_options_p; // options set by the caller of the encoding operation that controls // some aspects of the token sequence to emit bsl::string_view d_selectionName; // name of the selection FormattingMode d_formattingMode; // formatting mode of the selection bool d_isNextObjectFirst; // if 'false' then emit a leading sequence delimiter before the // representation of the value, otherwise do not emit a leading // sequence delimiter public: // CREATORS Encoder_SelectionDispatcher(Formatter *formatter, bsl::ostream *logStream, const bsl::string_view& selectionName, FormattingMode formattingMode, bool isNextObjectFirst, const EncoderOptions& options); // Construct an 'Encoder_SelectionDispatcher' object having the // specified 'formatter', 'logStream', and 'options' attributes. // MANIPULATORS template <class TYPE> int operator()(const TYPE& selection, bdlat_TypeCategory::CustomizedType category); template <class TYPE> int operator()(const TYPE& selection, bdlat_TypeCategory::DynamicType category); template <class TYPE, class CATEGORY> int operator()(const TYPE& selection, CATEGORY category); // Encode the JSON representation of the specified 'selection', having // the name equal to the 'name' attribute of the specified // 'selectionInfo' to the JSON 'formatter' attribute of this object, // according to the specified 'formattingMode'. If the representation // contains no text and 'isFirstSubObject' is 'true', load the value // 'true' into the specified 'isNextObjectFirst', and the value 'false' // otherwise. If 'isFirstSubObject' is 'true', then the representation // of the selection contains no leading sequence delimiter, and does // contain such a delimiter otherwise. The 'options' attribute of this // object configures aspects of the JSON representation of the // 'selection'. If this operation is not successful, load an // unspecified, human-readable description of the error condition to // the 'logStream' attribute of this object. Return 0 on success, and // a non-zero value otherwise. The behavior is undefined unless the // 'selection' satisfies both the static and dynamic requirements of // the 'bdlat' type-category concept corresponding to the specified // 'category'. See the component-level documentation of // {'baljsn_encoderoptions'} for a description of the effects, if any, // of each option in the 'options' on the JSON representation of the // 'selection'. See the package-level documentation of {'bdlat'} for // an introduction to the requirements of 'bdlat' type-category // concepts. template <class TYPE> int operator()(const TYPE&, bslmf::Nil); // The behavior of this function is undefined. // ACCESSORS bool isNextObjectFirst() const; // Return the value of the 'isNextElementFirst' attribute of this // object. }; // ============================== // class Encoder_AttributeVisitor // ============================== class Encoder_AttributeVisitor { // This component-private class provides a function object that closes over // the 'formatter', 'logStream', and 'options' parameters that are shared // between all encoding operations provided in this component. The // function-call operator of this class provides an overload set that // accepts an "attribute" object that satisfies one of the 'bdlat' // type-category concepts, and an "attribute info" object that describes // various metadata of the attribute. public: // TYPES typedef int FormattingMode; // 'FormattingMode' is an alias to the type of an 'int'-valued // 'bdlat_FormattingMode' bit-field. A 'FormattingMode' value is not // valid unless it is equal to an enumerator of 'bdlat_FormattingMode' // or a valid bitwise-or of two or more such enumerators. See the // component-level documentation of {'bdlat_formattingmode'} for a // description of the set of valid formatting-mode values. private: // DATA Formatter *d_formatter_p; // wrapper around the output stream that determines the whitespace to // emit around each JSON token bsl::ostream *d_logStream_p; // human-readable descriptions of all encountered error conditions bool d_isNextAttributeFirst; // if 'false' then emit a leading sequence delimiter before the // representation of the value, otherwise do not emit a leading // sequence delimiter const EncoderOptions *d_options_p; // options set by the caller of the encoding operation that controls // some aspects of the token sequence to emit public: // CREATORS Encoder_AttributeVisitor(Formatter *formatter, bsl::ostream *logStream, bool isNextAttributeFirst, const EncoderOptions& options); // Construct an 'Encoder_AttributeVisitor' object having the specified // 'formatter', 'logStream', 'isNextAttributeFirst', and 'options' // attributes. // MANIPULATORS template <class TYPE, class ATTRIBUTE_INFO> int operator()(const TYPE& attribute, const ATTRIBUTE_INFO& attributeInfo); // Encode the JSON representation of the specified 'attribute', having // the name equal to the 'name' of the specified 'attributeInfo', to // the JSON 'formatter' attribute of this object according to the // 'formattingMode' attribute of this object. If the // 'isNextAttributeFirst' attribute of this object is 'true', then the // representation of the attribute contains no leading sequence // delimiter, and does contain such a delimiter otherwise. The // 'options' attribute of this object configures aspects of the JSON // representation of the 'attribute'. If this operation is not // successful, load an unspecified, human-readable description of the // error condition to the 'logStream' attribute of this object. Return // 0 on success, and a non-zero value otherwise. The behavior is // undefined unless the 'attribute' satisfies both the static and // dynamic requirements of the 'bdlat' type-category concept // corresponding to the specified 'category'. See the component-level // documentation of {'baljsn_encoderoptions'} for a description of the // effects, if any, of each option in the 'options' on the JSON // representation of the 'attribute'. See the package-level // documentation of {'bdlat'} for an introduction to the requirements // of 'bdlat' type-category concepts. // ACCESSORS bool isNextAttributeFirst() const; // Return the value of the 'isNextAttributeFirst' attribute of this // object. }; // ================================= // class Encoder_AttributeDispatcher // ================================= class Encoder_AttributeDispatcher { // This component-private class provides a function object that closes over // the 'formatter', 'logStream', and 'options' parameters that are shared // between all encoding operations provided in this component. The // function-call operator of this class provides an overload set that // accepts an "attribute" object that satisfies one of the 'bdlat' // type-category concepts, and an optional 'bdlat_TypeCategory' tag type // that corresponds to the attribute's 'bdlat' type category. Each // function-call-operator overload encodes a JSON representation of the // specified attribute to the 'formatter' supplied on construction. public: // TYPES typedef int FormattingMode; // 'FormattingMode' is an alias to the type of an 'int'-valued // 'bdlat_FormattingMode' bit-field. A 'FormattingMode' value is not // valid unless it is equal to an enumerator of 'bdlat_FormattingMode' // or a valid bitwise-or of two or more such enumerators. See the // component-level documentation of {'bdlat_formattingmode'} for a // description of the set of valid formatting-mode values. private: // DATA Formatter *d_formatter_p; // wrapper around the output stream that determines the whitespace to // emit around each JSON token bsl::ostream *d_logStream_p; // human-readable descriptions of all encountered error conditions const EncoderOptions *d_options_p; // options set by the caller of the encoding operation that controls // some aspects of the token sequence to emit bsl::string_view d_attributeName; // name of the attribute FormattingMode d_formattingMode; // formatting mode of the attribute bool d_isNextAttributeFirst; // if 'false' then emit a leading sequence delimiter before the // representation of the value, otherwise do not emit a leading // sequence delimiter public: // CREATORS Encoder_AttributeDispatcher(Formatter *formatter, bsl::ostream *logStream, const bsl::string_view& attributeName, FormattingMode formattingMode, bool isNextAttributeFirst, const EncoderOptions& options); // Construct an 'Encoder_AttributeDispatcher' object having the // specified 'formatter', 'logStream', 'attributeName', // 'formattingMode', 'isNextAttributeFirst', and 'options' attributes. // MANIPULATORS int operator()(const bsl::vector<char>& attribute, bdlat_TypeCategory::Array category); template <class TYPE> int operator()(const TYPE& attribute, bdlat_TypeCategory::Array category); template <class TYPE> int operator()(const TYPE& attribute, bdlat_TypeCategory::CustomizedType category); template <class TYPE> int operator()(const TYPE& attribute, bdlat_TypeCategory::DynamicType category); template <class TYPE> int operator()(const TYPE& attribute, bdlat_TypeCategory::NullableValue category); template <class TYPE, class CATEGORY> int operator()(const TYPE& attribute, CATEGORY category); // Encode the JSON representation of the specified 'attribute', having // the name equal to the 'name' of the 'attributeInfo' attribute of // this object, to the JSON 'formatter' attribute of this object // according to the 'formattingMode' attribute of this object. If the // 'isNextAttributeFirst' attribute of this object is 'true', then the // representation of the attribute contains no leading sequence // delimiter, and does contain such a delimiter otherwise. The // 'options' attribute of this object configures aspects of the JSON // representation of the 'attribute'. If this operation is not // successful, load an unspecified, human-readable description of the // error condition to the 'logStream' attribute of this object. Return // 0 on success, and a non-zero value otherwise. The behavior is // undefined unless the 'attribute' satisfies both the static and // dynamic requirements of the 'bdlat' type-category concept // corresponding to the specified 'category'. See the component-level // documentation of {'baljsn_encoderoptions'} for a description of the // effects, if any, of each option in the 'options' on the JSON // representation of the 'attribute'. See the package-level // documentation of {'bdlat'} for an introduction to the requirements // of 'bdlat' type-category concepts. template <class TYPE> int operator()(const TYPE&, bslmf::Nil); // The behavior of this function is undefined. // ACCESSORS bool isNextAttributeFirst() const; // Return the value of the 'isNextAttributeFirst' attribute of this // object. }; // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // ------------- // class Encoder // ------------- // PRIVATE MANIPULATORS inline bsl::ostream& Encoder::logStream() { return d_logStream; } // CREATORS inline Encoder::Encoder(bslma::Allocator *basicAllocator) : d_logStream(basicAllocator) { } // MANIPULATORS template <class TYPE> inline int Encoder::encode(bsl::streambuf *streamBuf, const TYPE& value) { const EncoderOptions options; return encode(streamBuf, value, options); } template <class TYPE> inline int Encoder::encode(bsl::ostream& stream, const TYPE& value) { const EncoderOptions options; return encode(stream, value, options); } template <class TYPE> int Encoder::encode(bsl::streambuf *streamBuf, const TYPE& value, const EncoderOptions& options) { BSLS_ASSERT(streamBuf); d_logStream.clear(); d_logStream.str(""); bdlat_TypeCategory::Value category = bdlat_TypeCategoryFunctions::select(value); if (bdlat_TypeCategory::e_SEQUENCE_CATEGORY != category && bdlat_TypeCategory::e_CHOICE_CATEGORY != category && bdlat_TypeCategory::e_ARRAY_CATEGORY != category) { logStream() << "Encoded object must be a Sequence, Choice, or Array type." << bsl::endl; return -1; // RETURN } bsl::ostream outputStream(streamBuf); Encoder_EncodeImplUtil::openDocument(&outputStream, options); const int rc = Encoder_EncodeImplUtil::encode(&d_logStream, &outputStream, value, options); if (0 != rc) { streamBuf->pubsync(); return rc; // RETURN } Encoder_EncodeImplUtil::closeDocument(&outputStream, options); if (!outputStream) { logStream() << "An error occurred when writing to the supplied output stream" " or stream buffer." << bsl::endl; streamBuf->pubsync(); return -1; // RETURN } streamBuf->pubsync(); return 0; } template <class TYPE> int Encoder::encode(bsl::streambuf *streamBuf, const TYPE& value, const EncoderOptions *options) { EncoderOptions localOpts; return encode(streamBuf, value, options ? *options : localOpts); } template <class TYPE> int Encoder::encode(bsl::ostream& stream, const TYPE& value, const EncoderOptions& options) { if (!stream.good()) { logStream() << "Invalid stream." << bsl::endl; return -1; // RETURN } const int rc = this->encode(stream.rdbuf(), value, options); if (rc) { stream.setstate(bsl::ios_base::failbit); return rc; // RETURN } return 0; } template <class TYPE> inline int Encoder::encode(bsl::ostream& stream, const TYPE& value, const EncoderOptions *options) { EncoderOptions localOpts; return encode(stream, value, options ? *options : localOpts); } // ACCESSORS inline bsl::string Encoder::loggedMessages() const { return d_logStream.str(); } // ----------------------------- // struct Encoder_EncodeImplUtil // ----------------------------- // CLASS METHODS // Document Encoding Functions inline void Encoder_EncodeImplUtil::openDocument(bsl::ostream *outputStream, const EncoderOptions& options) { if (baljsn::EncoderOptions::e_PRETTY == options.encodingStyle()) { bdlb::Print::indent(*outputStream, options.initialIndentLevel(), options.spacesPerLevel()); } } inline void Encoder_EncodeImplUtil::closeDocument( bsl::ostream *outputStream, const baljsn::EncoderOptions& options) { if (baljsn::EncoderOptions::e_PRETTY == options.encodingStyle()) { (*outputStream) << '\n'; } } // Value Encoding template <class TYPE> inline int Encoder_EncodeImplUtil::encode(bsl::ostream *jsonStream, const TYPE& value, const EncoderOptions& options) { bdlsb::MemOutStreamBuf logStreamBuf; bsl::ostream logStream(&logStreamBuf); return encode(&logStream, jsonStream, value, options); } template <class TYPE> inline int Encoder_EncodeImplUtil::encode(bsl::ostream *logStream, bsl::ostream *jsonStream, const TYPE& value, const EncoderOptions& options) { static const FormattingMode s_MODE = bdlat_FormattingMode::e_DEFAULT; static const bool s_FIRST_MEMBER_FLAG = false; baljsn::Formatter formatter( *jsonStream, baljsn::EncoderOptions::e_PRETTY == options.encodingStyle(), options.initialIndentLevel(), options.spacesPerLevel()); bool isValueEmpty = false; int rc = encode(&isValueEmpty, &formatter, logStream, value, s_MODE, options, s_FIRST_MEMBER_FLAG); if (0 != formatter.nestingDepth()) { *logStream << "Encoding failed leaving an unclosed element (rc = " << rc << ")\n"; } return rc; } template <class TYPE> int Encoder_EncodeImplUtil::encode(bool *isValueEmpty, Formatter *formatter, bsl::ostream *logStream, const TYPE& value, FormattingMode formattingMode, const EncoderOptions& options, bool isFirstMember) { Encoder_ValueDispatcher visitor(formatter, logStream, formattingMode, isFirstMember, options); typedef typename bdlat_TypeCategory::Select<TYPE>::Type Category; int rc = visitor(value, Category()); if (0 != rc) { return rc; // RETURN } *isValueEmpty = visitor.isNextObjectFirst(); return 0; } // Validation template <class TYPE> int Encoder_EncodeImplUtil::validate(bsl::ostream *logStream, const TYPE& value, bdlat_TypeCategory::Choice) { return validateChoice(logStream, value); } template <class TYPE, class CATEGORY> inline int Encoder_EncodeImplUtil::validate(bsl::ostream *, const TYPE&, CATEGORY) { return 0; } template <class TYPE> int Encoder_EncodeImplUtil::validateChoice(bsl::ostream *logStream, const TYPE& value) { if (bdlat_ChoiceFunctions::k_UNDEFINED_SELECTION_ID == bdlat_ChoiceFunctions::selectionId(value)) { (*logStream) << "Undefined selection for Choice object" << bsl::endl; return -1; // RETURN } return 0; } // Encoding Values That Have Specific Type Categories template <class TYPE> inline int Encoder_EncodeImplUtil::encodeSimpleValue(Formatter *formatter, const TYPE& value, const EncoderOptions& options) { return formatter->putValue(value, &options); } // Encoding Value Prefixes and Suffixes inline void Encoder_EncodeImplUtil::encodeObjectPrefix(bool *isPrefixEmpty, Formatter *formatter, FormattingMode formattingMode) { if (bdlat_FormattingMode::e_UNTAGGED & formattingMode) { *isPrefixEmpty = true; return; // RETURN } formatter->openObject(); *isPrefixEmpty = false; } inline void Encoder_EncodeImplUtil::encodeObjectSuffix(bool *isSuffixEmpty, Formatter *formatter, FormattingMode formattingMode) { if (bdlat_FormattingMode::e_UNTAGGED & formattingMode) { *isSuffixEmpty = true; return; // RETURN } formatter->closeObject(); *isSuffixEmpty = false; } // Encoding Arrays That Have Specific Shapes inline void Encoder_EncodeImplUtil::encodeEmptyArray(Formatter *formatter) { formatter->openArray(true); formatter->closeArray(true); } template <class TYPE> int Encoder_EncodeImplUtil::encodeNonEmptyArray( Formatter *formatter, bsl::ostream *logStream, const TYPE& value, const EncoderOptions& options) { const int size = static_cast<int>(bdlat_ArrayFunctions::size(value)); BSLS_ASSERT(0 < size); formatter->openArray(); Encoder_ElementVisitor visitor(formatter, logStream, true, options); for (int index = 0; index != size; ++index) { int rc = bdlat_ArrayFunctions::accessElement(value, visitor, index); if (0 != rc) { return rc; // RETURN } } formatter->closeArray(); return 0; } // Encoding Generalized Members template <class TYPE> int Encoder_EncodeImplUtil::encodeMember( bool *isMemberEmpty, Formatter *formatter, bsl::ostream *logStream, const bsl::string_view& memberName, const TYPE& member, FormattingMode formattingMode, const EncoderOptions& options, bool isFirstMember, bdlat_TypeCategory::Array category) ///Implementation Note ///- - - - - - - - - - // This function purposefully ignores the 'EncodeEmptyArrays' option in the // specified 'options' and always encodes the value of the specified // 'member' array. The caller is responsible for checking the value of // this option and deciding whether to obey the option or not. Callers // that encode array-valued attributes of sequences must always obey the // option. Callers that encode array-valued selections of choices must // never obey the option, and must always encode the array value. { int rc = ThisUtil::validate(logStream, member, category); if (0 != rc) { return rc; // RETURN } rc = ThisUtil::encodeMemberPrefix(formatter, logStream, memberName, formattingMode, isFirstMember); if (0 != rc) { return rc; // RETURN } if (bdlat_ArrayFunctions::size(member) == 0) { ThisUtil::encodeEmptyArray(formatter); *isMemberEmpty = false; return 0; // RETURN } rc = ThisUtil::encodeNonEmptyArray(formatter, logStream, member, options); if (0 != rc) { (*logStream) << "Unable to encode value of element " << "named: '" << memberName << "'." << bsl::endl; return rc; // RETURN } *isMemberEmpty = false; return 0; } template <class TYPE, class OTHER_CATEGORY> int Encoder_EncodeImplUtil::encodeMember( bool *isMemberEmpty, Formatter *formatter, bsl::ostream *logStream, const bsl::string_view& memberName, const TYPE& member, FormattingMode formattingMode, const EncoderOptions& options, bool isFirstMember, OTHER_CATEGORY category) { int rc = ThisUtil::validate(logStream, member, category); if (0 != rc) { return rc; // RETURN } bool isPrefixEmpty = false; rc = ThisUtil::encodeMemberPrefix(&isPrefixEmpty, formatter, logStream, memberName, formattingMode, isFirstMember); if (0 != rc) { return rc; // RETURN } bool isValueEmpty = false; rc = ThisUtil::encode(&isValueEmpty, formatter, logStream, member, formattingMode, options, !isPrefixEmpty || isFirstMember); if (0 != rc) { (*logStream) << "Unable to encode value of element " << "named: '" << memberName << "'." << bsl::endl; return rc; // RETURN } BSLS_ASSERT(!isValueEmpty || isPrefixEmpty); // If the value is empty then the prefix is empty. Otherwise, this // function would produce invalid JSON because it would emit a member name // token and a colon token, but no member value. *isMemberEmpty = isFirstMember && isValueEmpty; return 0; } inline int Encoder_EncodeImplUtil::encodeMemberPrefix( Formatter *formatter, bsl::ostream *logStream, const bsl::string_view& memberName, bool isFirstMember) { if (!isFirstMember) { formatter->closeMember(); } int rc = formatter->openMember(memberName); if (0 != rc) { (*logStream) << "Unable to encode element name: '" << memberName << "'." << bsl::endl; return rc; // RETURN } return 0; } inline int Encoder_EncodeImplUtil::encodeMemberPrefix( Formatter *formatter, bsl::ostream *logStream, const bsl::string_view& memberName, FormattingMode formattingMode, bool isFirstMember) { if (bdlat_FormattingMode::e_UNTAGGED & formattingMode) { return 0; // RETURN } return ThisUtil::encodeMemberPrefix(formatter, logStream, memberName, isFirstMember); } inline int Encoder_EncodeImplUtil::encodeMemberPrefix( bool *isPrefixEmpty, Formatter *formatter, bsl::ostream *logStream, const bsl::string_view& memberName, FormattingMode formattingMode, bool isFirstMember) { if (bdlat_FormattingMode::e_UNTAGGED & formattingMode) { *isPrefixEmpty = true; return 0; // RETURN } int rc = ThisUtil::encodeMemberPrefix(formatter, logStream, memberName, isFirstMember); if (0 != rc) { return rc; // RETURN } *isPrefixEmpty = false; return 0; } // ------------------------------ // struct Encoder_ValueDispatcher // ------------------------------ // CREATORS inline Encoder_ValueDispatcher::Encoder_ValueDispatcher( Formatter *formatter, bsl::ostream *logStream, FormattingMode formattingMode, bool isNextObjectFirst, const EncoderOptions& options) : d_formatter_p(formatter) , d_logStream_p(logStream) , d_options_p(&options) , d_formattingMode(formattingMode) , d_isNextObjectFirst(isNextObjectFirst) { } // ACCESSORS inline int Encoder_ValueDispatcher::operator()(const bsl::vector<char>& value, bdlat_TypeCategory::Array) { d_isNextObjectFirst = false; return Encoder_EncodeImplUtil::encodeCharArray(d_formatter_p, value, *d_options_p); } template <class TYPE> inline int Encoder_ValueDispatcher::operator()(const TYPE& value, bdlat_TypeCategory::Array) { const bool arrayIsEmpty = (0 == bdlat_ArrayFunctions::size(value)); if (arrayIsEmpty && !d_options_p->encodeEmptyArrays()) { d_isNextObjectFirst = true; return 0; // RETURN } if (arrayIsEmpty && d_options_p->encodeEmptyArrays()) { Encoder_EncodeImplUtil::encodeEmptyArray(d_formatter_p); d_isNextObjectFirst = false; return 0; // RETURN } int rc = Encoder_EncodeImplUtil::encodeNonEmptyArray(d_formatter_p, d_logStream_p, value, *d_options_p); if (0 != rc) { return rc; // RETURN } d_isNextObjectFirst = false; return 0; } template <class TYPE> inline int Encoder_ValueDispatcher::operator()(const TYPE& value, bdlat_TypeCategory::Choice) { int rc = Encoder_EncodeImplUtil::validateChoice(d_logStream_p, value); if (0 != rc) { return rc; // RETURN } bool isPrefixEmpty = false; Encoder_EncodeImplUtil::encodeObjectPrefix(&isPrefixEmpty, d_formatter_p, d_formattingMode); Encoder_SelectionVisitor visitor(d_formatter_p, d_logStream_p, !isPrefixEmpty || d_isNextObjectFirst, *d_options_p); rc = bdlat_ChoiceFunctions::accessSelection(value, visitor); if (0 != rc) { return rc; // RETURN } const bool isSelectionEmpty = visitor.isNextObjectFirst(); bool isSuffixEmpty = false; Encoder_EncodeImplUtil::encodeObjectSuffix(&isSuffixEmpty, d_formatter_p, d_formattingMode); d_isNextObjectFirst = isPrefixEmpty && isSelectionEmpty && isSuffixEmpty; return 0; } template <class TYPE> inline int Encoder_ValueDispatcher::operator()( const TYPE& value, bdlat_TypeCategory::CustomizedType) { return Encoder_EncodeImplUtil::encode( &d_isNextObjectFirst, d_formatter_p, d_logStream_p, bdlat_CustomizedTypeFunctions::convertToBaseType(value), d_formattingMode, *d_options_p, d_isNextObjectFirst); } template <class TYPE> inline int Encoder_ValueDispatcher::operator()(const TYPE& value, bdlat_TypeCategory::DynamicType) { return bdlat_TypeCategoryUtil::accessByCategory(value, *this); } template <class TYPE> inline int Encoder_ValueDispatcher::operator()(const TYPE& value, bdlat_TypeCategory::Enumeration) { bsl::string valueString; bdlat_EnumFunctions::toString(&valueString, value); d_isNextObjectFirst = false; return Encoder_EncodeImplUtil::encodeSimpleValue(d_formatter_p, valueString, *d_options_p); } template <class TYPE> inline int Encoder_ValueDispatcher::operator()( const TYPE& value, bdlat_TypeCategory::NullableValue) { if (bdlat_NullableValueFunctions::isNull(value)) { d_formatter_p->putNullValue(); d_isNextObjectFirst = false; return 0; // RETURN } Encoder_ValueDispatcher visitor(d_formatter_p, d_logStream_p, d_formattingMode, d_isNextObjectFirst, *d_options_p); int rc = bdlat::NullableValueUtil::accessValueByCategory(value, visitor); if (0 != rc) { return rc; // RETURN } d_isNextObjectFirst = visitor.isNextObjectFirst(); return 0; } template <class TYPE> inline int Encoder_ValueDispatcher::operator()(const TYPE& value, bdlat_TypeCategory::Sequence) { bool isPrefixEmpty = false; Encoder_EncodeImplUtil::encodeObjectPrefix(&isPrefixEmpty, d_formatter_p, d_formattingMode); Encoder_AttributeVisitor visitor(d_formatter_p, d_logStream_p, !isPrefixEmpty || d_isNextObjectFirst, *d_options_p); int rc = bdlat_SequenceFunctions::accessAttributes(value, visitor); if (0 != rc) { return rc; // RETURN } const bool isAttributeEmpty = visitor.isNextAttributeFirst(); bool isSuffixEmpty = false; Encoder_EncodeImplUtil::encodeObjectSuffix(&isSuffixEmpty, d_formatter_p, d_formattingMode); d_isNextObjectFirst = isPrefixEmpty && isAttributeEmpty && isSuffixEmpty; return 0; } template <class TYPE> inline int Encoder_ValueDispatcher::operator()(const TYPE& value, bdlat_TypeCategory::Simple) { d_isNextObjectFirst = false; return Encoder_EncodeImplUtil::encodeSimpleValue(d_formatter_p, value, *d_options_p); } template <class TYPE> inline int Encoder_ValueDispatcher::operator()(const TYPE&, bslmf::Nil) { BSLS_ASSERT_OPT(!"Unreachable"); return -1; } // ACCESSORS inline bool Encoder_ValueDispatcher::isNextObjectFirst() const { return d_isNextObjectFirst; } // ---------------------------- // class Encoder_ElementVisitor // ---------------------------- // CREATORS inline Encoder_ElementVisitor::Encoder_ElementVisitor( Formatter *formatter, bsl::ostream *logStream, bool isNextElementFirst, const EncoderOptions& options) : d_formatter_p(formatter) , d_logStream_p(logStream) , d_options_p(&options) , d_isNextElementFirst(isNextElementFirst) { } // MANIPULATORS template <class TYPE> inline int Encoder_ElementVisitor::operator()(const TYPE& element) { Encoder_ElementDispatcher dispatcher(d_formatter_p, d_logStream_p, d_isNextElementFirst, *d_options_p); typedef typename bdlat_TypeCategory::Select<TYPE>::Type Category; int rc = dispatcher(element, Category()); if (0 != rc) { return rc; // RETURN } d_isNextElementFirst = dispatcher.isNextElementFirst(); return 0; } // ACCESSORS inline bool Encoder_ElementVisitor::isNextElementFirst() const { return d_isNextElementFirst; } // ------------------------------- // class Encoder_ElementDispatcher // ------------------------------- // CREATORS inline Encoder_ElementDispatcher::Encoder_ElementDispatcher( Formatter *formatter, bsl::ostream *logStream, bool isNextElementFirst, const EncoderOptions& options) : d_formatter_p(formatter) , d_logStream_p(logStream) , d_options_p(&options) , d_isNextElementFirst(isNextElementFirst) { } // MANIPULATORS inline int Encoder_ElementDispatcher::operator()(const bsl::vector<char>& element, bdlat_TypeCategory::Array) { if (!d_isNextElementFirst) { d_formatter_p->addArrayElementSeparator(); } int rc = Encoder_EncodeImplUtil::encodeCharArray(d_formatter_p, element, *d_options_p); if (0 != rc) { return rc; // RETURN } d_isNextElementFirst = false; return 0; } template <class TYPE> int Encoder_ElementDispatcher::operator()(const TYPE& element, bdlat_TypeCategory::Array) { const bool arrayIsEmpty = (0 == bdlat_ArrayFunctions::size(element)); if (arrayIsEmpty && !d_options_p->encodeEmptyArrays()) { return 0; // RETURN } if (!d_isNextElementFirst) { d_formatter_p->addArrayElementSeparator(); } if (arrayIsEmpty && d_options_p->encodeEmptyArrays()) { Encoder_EncodeImplUtil::encodeEmptyArray(d_formatter_p); d_isNextElementFirst = false; return 0; // RETURN } int rc = Encoder_EncodeImplUtil::encodeNonEmptyArray(d_formatter_p, d_logStream_p, element, *d_options_p); if (0 != rc) { return rc; // RETURN } d_isNextElementFirst = false; return 0; } template <class TYPE> int Encoder_ElementDispatcher::operator()(const TYPE& element, bdlat_TypeCategory::Choice) { int rc = Encoder_EncodeImplUtil::validateChoice(d_logStream_p, element); if (0 != rc) { return rc; // RETURN } if (!d_isNextElementFirst) { d_formatter_p->addArrayElementSeparator(); } d_formatter_p->openObject(); Encoder_SelectionVisitor visitor(d_formatter_p, d_logStream_p, true, *d_options_p); rc = bdlat_ChoiceFunctions::accessSelection(element, visitor); if (0 != rc) { return rc; // RETURN } d_formatter_p->closeObject(); d_isNextElementFirst = false; return 0; } template <class TYPE> int Encoder_ElementDispatcher::operator()( const TYPE& element, bdlat_TypeCategory::CustomizedType) { return bdlat_TypeCategoryUtil::accessByCategory( bdlat_CustomizedTypeFunctions::convertToBaseType(element), *this); } template <class TYPE> int Encoder_ElementDispatcher::operator()( const TYPE& element, bdlat_TypeCategory::DynamicType) { return bdlat_TypeCategoryUtil::accessByCategory(element, *this); } template <class TYPE> int Encoder_ElementDispatcher::operator()( const TYPE& element, bdlat_TypeCategory::Enumeration) { if (!d_isNextElementFirst) { d_formatter_p->addArrayElementSeparator(); } bsl::string valueString; bdlat_EnumFunctions::toString(&valueString, element); d_isNextElementFirst = false; return Encoder_EncodeImplUtil::encodeSimpleValue(d_formatter_p, valueString, *d_options_p); } template <class TYPE> int Encoder_ElementDispatcher::operator()( const TYPE& element, bdlat_TypeCategory::NullableValue) { const bool elementIsNull = bdlat_NullableValueFunctions::isNull(element); if (elementIsNull) { if (!d_isNextElementFirst) { d_formatter_p->addArrayElementSeparator(); } d_formatter_p->putNullValue(); d_isNextElementFirst = false; return 0; // RETURN } Encoder_ElementVisitor visitor(d_formatter_p, d_logStream_p, d_isNextElementFirst, *d_options_p); int rc = bdlat_NullableValueFunctions::accessValue(element, visitor); if (0 != rc) { return rc; // RETURN } d_isNextElementFirst = visitor.isNextElementFirst(); return 0; } template <class TYPE> int Encoder_ElementDispatcher::operator()(const TYPE& element, bdlat_TypeCategory::Sequence) { if (!d_isNextElementFirst) { d_formatter_p->addArrayElementSeparator(); } d_formatter_p->openObject(); Encoder_AttributeVisitor visitor(d_formatter_p, d_logStream_p, true, *d_options_p); int rc = bdlat_SequenceFunctions::accessAttributes(element, visitor); if (0 != rc) { return rc; // RETURN } d_formatter_p->closeObject(); d_isNextElementFirst = false; return 0; } template <class TYPE> int Encoder_ElementDispatcher::operator()(const TYPE& element, bdlat_TypeCategory::Simple) { if (!d_isNextElementFirst) { d_formatter_p->addArrayElementSeparator(); } d_isNextElementFirst = false; return Encoder_EncodeImplUtil::encodeSimpleValue(d_formatter_p, element, *d_options_p); } template <class TYPE> int Encoder_ElementDispatcher::operator()(const TYPE&, bslmf::Nil) { BSLS_ASSERT_OPT(!"Unreachable"); return -1; } // ACCESSORS inline bool Encoder_ElementDispatcher::isNextElementFirst() const { return d_isNextElementFirst; } // ------------------------------ // class Encoder_SelectionVisitor // ------------------------------ // CREATORS inline Encoder_SelectionVisitor::Encoder_SelectionVisitor( Formatter *formatter, bsl::ostream *logStream, bool isNextObjectFirst, const EncoderOptions& options) : d_formatter_p(formatter) , d_logStream_p(logStream) , d_isNextObjectFirst(isNextObjectFirst) , d_options_p(&options) { } // MANIPULATORS template <class TYPE, class SELECTION_INFO> inline int Encoder_SelectionVisitor::operator()(const TYPE& selection, const SELECTION_INFO& selectionInfo) { Encoder_SelectionDispatcher dispatcher(d_formatter_p, d_logStream_p, selectionInfo.name(), selectionInfo.formattingMode(), d_isNextObjectFirst, *d_options_p); typedef typename bdlat_TypeCategory::Select<TYPE>::Type Category; int rc = dispatcher(selection, Category()); if (0 != rc) { return rc; // RETURN } d_isNextObjectFirst = dispatcher.isNextObjectFirst(); return 0; } // ACCESSORS inline bool Encoder_SelectionVisitor::isNextObjectFirst() const { return d_isNextObjectFirst; } // --------------------------------- // class Encoder_SelectionDispatcher // --------------------------------- // CREATORS inline Encoder_SelectionDispatcher::Encoder_SelectionDispatcher( Formatter *formatter, bsl::ostream *logStream, const bsl::string_view& selectionName, FormattingMode formattingMode, bool isNextObjectFirst, const EncoderOptions& options) : d_formatter_p(formatter) , d_logStream_p(logStream) , d_options_p(&options) , d_selectionName(selectionName) , d_formattingMode(formattingMode) , d_isNextObjectFirst(isNextObjectFirst) { } // MANIPULATORS template <class TYPE> inline int Encoder_SelectionDispatcher::operator()( const TYPE& selection, bdlat_TypeCategory::CustomizedType) { return bdlat_TypeCategoryUtil::accessByCategory( bdlat_CustomizedTypeFunctions::convertToBaseType(selection), *this); } template <class TYPE> inline int Encoder_SelectionDispatcher::operator()( const TYPE& selection, bdlat_TypeCategory::DynamicType) { return bdlat_TypeCategoryUtil::accessByCategory(selection, *this); } template <class TYPE, class CATEGORY> inline int Encoder_SelectionDispatcher::operator()(const TYPE& selection, CATEGORY category) { return Encoder_EncodeImplUtil::encodeMember(&d_isNextObjectFirst, d_formatter_p, d_logStream_p, d_selectionName.data(), selection, d_formattingMode, *d_options_p, d_isNextObjectFirst, category); } template <class TYPE> inline int Encoder_SelectionDispatcher::operator()(const TYPE&, bslmf::Nil) { BSLS_ASSERT_OPT(!"Reachable"); return -1; } // ACCESSORS inline bool Encoder_SelectionDispatcher::isNextObjectFirst() const { return d_isNextObjectFirst; } // ------------------------------ // class Encoder_AttributeVisitor // ------------------------------ // CREATORS inline Encoder_AttributeVisitor::Encoder_AttributeVisitor( Formatter *formatter, bsl::ostream *logStream, bool isNextAttributeFirst, const EncoderOptions& options) : d_formatter_p(formatter) , d_logStream_p(logStream) , d_isNextAttributeFirst(isNextAttributeFirst) , d_options_p(&options) { } // MANIPULATORS template <class TYPE, class ATTRIBUTE_INFO> int Encoder_AttributeVisitor::operator()(const TYPE& attribute, const ATTRIBUTE_INFO& attributeInfo) { Encoder_AttributeDispatcher dispatcher(d_formatter_p, d_logStream_p, attributeInfo.name(), attributeInfo.formattingMode(), d_isNextAttributeFirst, *d_options_p); typedef typename bdlat_TypeCategory::Select<TYPE>::Type Category; int rc = dispatcher(attribute, Category()); if (0 != rc) { return -rc; // RETURN } d_isNextAttributeFirst = dispatcher.isNextAttributeFirst(); return 0; } // ACCESSORS inline bool Encoder_AttributeVisitor::isNextAttributeFirst() const { return d_isNextAttributeFirst; } // --------------------------------- // class Encoder_AttributeDispatcher // --------------------------------- // CREATORS inline Encoder_AttributeDispatcher::Encoder_AttributeDispatcher( Formatter *formatter, bsl::ostream *logStream, const bsl::string_view& attributeName, FormattingMode formattingMode, bool isNextAttributeFirst, const EncoderOptions& options) : d_formatter_p(formatter) , d_logStream_p(logStream) , d_options_p(&options) , d_attributeName(attributeName) , d_formattingMode(formattingMode) , d_isNextAttributeFirst(isNextAttributeFirst) { } // MANIPULATORS inline int Encoder_AttributeDispatcher::operator()( const bsl::vector<char>& attribute, bdlat_TypeCategory::Array category) { return Encoder_EncodeImplUtil::encodeMember(&d_isNextAttributeFirst, d_formatter_p, d_logStream_p, d_attributeName.data(), attribute, d_formattingMode, *d_options_p, d_isNextAttributeFirst, category); } template <class TYPE> inline int Encoder_AttributeDispatcher::operator()( const TYPE& attribute, bdlat_TypeCategory::Array category) { const bool isArrayEmpty = (0 == bdlat_ArrayFunctions::size(attribute)); if (!d_options_p->encodeEmptyArrays() && isArrayEmpty) { return 0; // RETURN } return Encoder_EncodeImplUtil::encodeMember(&d_isNextAttributeFirst, d_formatter_p, d_logStream_p, d_attributeName.data(), attribute, d_formattingMode, *d_options_p, d_isNextAttributeFirst, category); } template <class TYPE> inline int Encoder_AttributeDispatcher::operator()( const TYPE& attribute, bdlat_TypeCategory::CustomizedType) { return bdlat_TypeCategoryUtil::accessByCategory( bdlat_CustomizedTypeFunctions::convertToBaseType(attribute), *this); } template <class TYPE> inline int Encoder_AttributeDispatcher::operator()( const TYPE& attribute, bdlat_TypeCategory::DynamicType) { return bdlat_TypeCategoryUtil::accessByCategory(attribute, *this); } template <class TYPE> inline int Encoder_AttributeDispatcher::operator()( const TYPE& attribute, bdlat_TypeCategory::NullableValue category) { if (bdlat_NullableValueFunctions::isNull(attribute) && !d_options_p->encodeNullElements()) { return 0; // RETURN } return Encoder_EncodeImplUtil::encodeMember(&d_isNextAttributeFirst, d_formatter_p, d_logStream_p, d_attributeName.data(), attribute, d_formattingMode, *d_options_p, d_isNextAttributeFirst, category); } template <class TYPE, class CATEGORY> inline int Encoder_AttributeDispatcher::operator()(const TYPE& attribute, CATEGORY category) { return Encoder_EncodeImplUtil::encodeMember(&d_isNextAttributeFirst, d_formatter_p, d_logStream_p, d_attributeName.data(), attribute, d_formattingMode, *d_options_p, d_isNextAttributeFirst, category); } template <class TYPE> inline int Encoder_AttributeDispatcher::operator()(const TYPE&, bslmf::Nil) { BSLS_ASSERT_OPT(!"Unreachable"); return -1; } // ACCESSORS inline bool Encoder_AttributeDispatcher::isNextAttributeFirst() const { return d_isNextAttributeFirst; } // The 'Encoder_Formatter' 'class' has been replaced by the 'baljsn::Formatter' // 'class' in the 'baljsn_formatter' component. Clients should use that // 'class' instead. The following 'class' definition is provided for // backwards-compatibility for users that have written code using this // component-private 'class'. // ======================= // class Encoder_Formatter // ======================= class Encoder_Formatter { // This class implements a formatter providing operations for rending JSON // text elements to an output stream (supplied at construction) according // to a set of formatting options (also supplied at construction). This is // a component-private class and should not be used outside of this // component. // // DEPRECATED: Use 'baljsn::Formatter' instead. // DATA bsl::ostream& d_outputStream; // stream for output (held, not owned) bool d_usePrettyStyle; // encoding style int d_indentLevel; // initial indent level int d_spacesPerLevel; // spaces per level bool d_isArrayElement; // is current element part of an array public: // CREATORS Encoder_Formatter(bsl::ostream& stream, const EncoderOptions& options); // Create a 'Encoder_Formatter' object using the specified 'stream' and // 'options'. //! ~Encoder_Formatter() = default; // Destroy this object. // MANIPULATORS void openObject(); // Print onto the stream supplied at construction the sequence of // characters designating the start of an object. void closeObject(); // Print onto the stream supplied at construction the sequence of // characters designating the end of an object. void openArray(bool formatAsEmptyArrayFlag = false); // Print onto the stream supplied at construction the sequence of // characters designating the start of an array. Optionally specify // 'formatAsEmptyArrayFlag' denoting if the array being opened should // be formatted as an empty array. If 'formatAsEmptyArrayFlag' is not // specified then the array being opened is formatted as an array // having elements. Note that the formatting (and as a consequence the // 'formatAsEmptyArrayFlag') is relevant only if this formatter encodes // in the pretty style and is ignored otherwise. void closeArray(bool formatAsEmptyArrayFlag = false); // Print onto the stream supplied at construction the sequence of // characters designating the end of an array. Optionally specify // 'formatAsEmptyArrayFlag' denoting if the array being closed should // be formatted as an empty array. If 'formatAsEmptyArrayFlag' is not // specified then the array being closed is formatted as an array // having elements. Note that the formatting (and as a consequence the // 'formatAsEmptyArrayFlag') is relevant only if this formatter encodes // in the pretty style and is ignored otherwise. void indent(); // Print onto the stream supplied at construction the sequence of // whitespace characters for the proper indentation of an element given // the encoding options supplied at construction. int openElement(const bsl::string& name); // Print onto the stream supplied at construction the sequence of // characters designating the start of an element having the specified // 'name'. Return 0 on success and a non-zero value otherwise. void closeElement(); // Print onto the stream supplied at construction the sequence of // characters designating the end of an element. void openDocument(); // Print onto the stream supplied at construction the sequence of // characters designating the start of the document. void closeDocument(); // Print onto the stream supplied at construction the sequence of // characters designating the end of the document. void setIsArrayElement(bool isArrayElement); // Set the flag denoting if the current element refers to an array // element to the specified 'isArrayElement'. // ACCESSORS bool isArrayElement() const; // Return the value of the flag denoting if the current element refers // to an array element. }; // ----------------------- // class Encoder_Formatter // ----------------------- // MANIPULATORS inline void Encoder_Formatter::setIsArrayElement(bool isArrayElement) { d_isArrayElement = isArrayElement; } // ACCESSORS inline bool Encoder_Formatter::isArrayElement() const { return d_isArrayElement; } } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2015 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------