// baljsn_parserutil.h -*-C++-*- #ifndef INCLUDED_BALJSN_PARSERUTIL #define INCLUDED_BALJSN_PARSERUTIL #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a utility for decoding JSON data into simple types. // //@CLASSES: // baljsn::ParserUtil: utility for parsing JSON data into simple types // //@SEE_ALSO: baljsn_decoder, baljsn_printutil // //@DESCRIPTION: This component provides a 'struct' of utility functions, // 'baljsn::ParserUtil', for decoding data in the JSON format into a 'bdeat' // Simple type. The primary method is 'getValue', which decodes into a // specified object and is overloaded for all 'bdeat' Simple types. // // Refer to the details of the JSON encoding format supported by this utility // in the package documentation file (doc/baljsn.txt). // ///Usage ///----- // This section illustrates intended use of this component. // ///Example 1: Decoding into a Simple 'struct' from JSON data ///--------------------------------------------------------- // Suppose we want to de-serialize some JSON data into an object. // // First, we define a struct, 'Employee', to contain the data: //.. // struct Employee { // bsl::string d_name; // bdlt::Date d_date; // int d_age; // }; //.. // Then, we create an 'Employee' object: //.. // Employee employee; //.. // Next, we specify the string values in JSON format used to represent the // object data. Note that the birth date is specified in the ISO 8601 format: //.. // const char *name = "\"John Smith\""; // const char *date = "\"1985-06-24\""; // const char *age = "21"; // // const bsl::string_view nameRef(name); // const bsl::string_view dateRef(date); // const bsl::string_view ageRef(age); //.. // Now, we use the created string refs to populate the employee object: //.. // assert(0 == baljsn::ParserUtil::getValue(&employee.d_name, nameRef)); // assert(0 == baljsn::ParserUtil::getValue(&employee.d_date, dateRef)); // assert(0 == baljsn::ParserUtil::getValue(&employee.d_age, ageRef)); //.. // Finally, we will verify that the values are as expected: //.. // assert("John Smith" == employee.d_name); // assert(bdlt::Date(1985, 06, 24) == employee.d_date); // assert(21 == employee.d_age); //.. #include <balscm_version.h> #include <bdljsn_stringutil.h> #include <bdlb_variant.h> #include <bdldfp_decimal.h> #include <bdlt_iso8601util.h> #include <bsls_assert.h> #include <bsls_types.h> #include <bsl_limits.h> #include <bsl_cstring.h> #include <bsl_string.h> #include <bsl_string_view.h> #include <bsl_vector.h> #include <string> #include <vector> namespace BloombergLP { namespace baljsn { // ================= // struct ParserUtil // ================= struct ParserUtil { //This class provides utility functions for decoding data in the JSON //format into a 'bdeat' Simple type. The primary method is 'getValue', //which decodes into a specified object and is overloaded for all 'bdeat' //Simple types. private: // PRIVATE CLASS METHODS template <class TYPE> static int getDateAndTimeValue(TYPE *value, const bsl::string_view& data); // Load into the specified 'value' the date or time value represented // as a string in the ISO 8601 format in the specified 'data'. Return // 0 on success and a non-zero value otherwise. Note that 'TYPE' is // expected to be one of 'bdlt::Date', 'bdlt::Time', bdlt::Datetime', // 'bdlt::DateTz', 'bdlt::TimeTz', 'bdlt::DatetimeTz', // 'bdlb::Variant2<bdlt::Date, bdlt::DateTz>', // 'bdlb::Variant2<bdlt::Time, bdlt::TimeTz>' or // 'bdlb::Variant2<bdlt::Datetime, bdlt::DatetimeTz>'. template <class TYPE> static int getIntegralValue(TYPE *value, bsl::string_view data); // Load into the specified 'value' the integer value in the specified // 'data'. Note that this operation follows the more permissive syntax // specified for 'bdlb::NumericParseUtil', allowing things like leading // '0' digits which the JSON spec does not permit. Return 0 on success // and a non-zero value otherwise. The behavior is undefined unless // 'true == is_integral<TYPE>::value && is_signed<TYPE>::value', e.g, // 'TYPE' is expected to be a *signed* integral type. Note that // although 'data' is passed by value instead of 'const'-reference, // there is no effect on usage. template <class TYPE> static int getUnsignedIntegralValue(TYPE *value, const bsl::string_view& data); // Load into the specified 'value' the unsigned integer value in the // specified 'data'. Note that this operation follows the more // permissive syntax specified for 'bdlb::NumericParseUtil', allowing // things like leading '0' digits which the JSON spec does not permit. // Return 0 on success and a non-zero value otherwise. The behavior is // undefined unless // 'true == is_integral<TYPE>::value && !is_signed<TYPE>::value', e.g, // 'TYPE' is expected to be an *unsigned* integral type. static int getUint64(bsls::Types::Uint64 *value, const bsl::string_view& data); // Load into the specified 'value' the value in the specified 'data'. // Note that this operation follows the more permissive syntax // specified for 'bdlb::NumericParseUtil', allowing things like leading // '0' digits which the JSON spec does not permit. Return 0 on success // and a non-zero value otherwise. public: // TYPES typedef bdlb::Variant2<bdlt::Date, bdlt::DateTz> DateOrDateTz; // 'DateOrDateTz' is a convenient alias for // 'bdlb::Variant2<Date, DateTz>'. typedef bdlb::Variant2<bdlt::Time, bdlt::TimeTz> TimeOrTimeTz; // 'TimeOrTimeTz' is a convenient alias for // 'bdlb::Variant2<Time, TimeTz>'. typedef bdlb::Variant2<bdlt::Datetime, bdlt::DatetimeTz> DatetimeOrDatetimeTz; // 'DatetimeOrDatetimeTz' is a convenient alias for // 'bdlb::Variant2<Datetime, DatetimeTz>'. // CLASS METHODS static int getQuotedString(bsl::string *value, const bsl::string_view& data); // Load into the specified 'value' the string value in the specified // 'data'. The string must be begin and end in '"' characters which // are not part of the resulting 'value'. Return 0 on success and a // non-zero value otherwise. static int getUnquotedString(bsl::string *value, const bsl::string_view& data); // Load into the specified 'value' the string value in the specified // 'data'. Return 0 on success and a non-zero value otherwise. static int getValue(bool *value, const bsl::string_view& data); static int getValue(char *value, const bsl::string_view& data); static int getValue(unsigned char *value, const bsl::string_view& data); static int getValue(signed char *value, const bsl::string_view& data); static int getValue(short *value, const bsl::string_view& data); static int getValue(unsigned short *value, const bsl::string_view& data); static int getValue(int *value, const bsl::string_view& data); static int getValue(unsigned int *value, const bsl::string_view& data); static int getValue(bsls::Types::Int64 *value, const bsl::string_view& data); static int getValue(bsls::Types::Uint64 *value, const bsl::string_view& data); static int getValue(float *value, const bsl::string_view& data); static int getValue(double *value, const bsl::string_view& data); static int getValue(bdldfp::Decimal64 *value, const bsl::string_view& data); static int getValue(bdlt::Date *value, const bsl::string_view& data); static int getValue(bdlt::Datetime *value, const bsl::string_view& data); static int getValue(bdlt::DatetimeTz *value, const bsl::string_view& data); static int getValue(bdlt::DateTz *value, const bsl::string_view& data); static int getValue(bdlt::Time *value, const bsl::string_view& data); static int getValue(bdlt::TimeTz *value, const bsl::string_view& data); static int getValue(DateOrDateTz *value, const bsl::string_view& data); static int getValue(TimeOrTimeTz *value, const bsl::string_view& data); static int getValue(DatetimeOrDatetimeTz *value, const bsl::string_view& data); static int getValue(bsl::vector<char> *value, const bsl::string_view& data); // Load into the specified 'value' the characters read from the // specified 'data'. Return 0 on success or a non-zero value on // failure. static int getValue(bsl::string *value, const bsl::string_view& data); // Load into the specified 'value' the string value in the specified // 'data'. The string must be begin and end in '"' characters which // are not part of the resulting 'value'. Return 0 on success and a // non-zero value otherwise. }; // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // ----------------- // struct ParserUtil // ----------------- // CLASS METHODS inline int ParserUtil::getQuotedString(bsl::string *value, const bsl::string_view& data) { return bdljsn::StringUtil::readString( value, data, bdljsn::StringUtil::e_ACCEPT_CAPITAL_UNICODE_ESCAPE); } template <class TYPE> int ParserUtil::getUnsignedIntegralValue(TYPE *value, const bsl::string_view& data) { BSLS_ASSERT(value); if (0 == data.length()) { return -1; // RETURN } bsls::Types::Uint64 tmp; int rc = getUint64(&tmp, data); if (rc) { return -1; // RETURN } if (tmp > static_cast<bsls::Types::Uint64>((bsl::numeric_limits<TYPE>::max)())) { return -1; // RETURN } *value = static_cast<TYPE>(tmp); return 0; } template <class TYPE> int ParserUtil::getIntegralValue(TYPE *value, bsl::string_view data) { // Note that we take 'data' by value because we may want to modify it. if (0 == data.length()) { return -1; // RETURN } bool isNegative; if ('-' == data[0]) { isNegative = true; data.remove_prefix(1); } else { isNegative = false; } bsls::Types::Uint64 tmp; const int rc = getUint64(&tmp, data); if (rc) { return -1; // RETURN } bsls::Types::Uint64 maxValue = static_cast<bsls::Types::Uint64>((bsl::numeric_limits<TYPE>::max)()); if (isNegative && tmp <= maxValue + 1) { *value = static_cast<TYPE>(tmp * -1); } else if (tmp <= maxValue) { *value = static_cast<TYPE>(tmp); } else { return -1; // RETURN } return 0; } template <class TYPE> int ParserUtil::getDateAndTimeValue(TYPE *value, const bsl::string_view& data) { enum { k_STRING_LENGTH_WITH_QUOTES = 2 }; if (data.length() < k_STRING_LENGTH_WITH_QUOTES || '"' != *data.begin() || '"' != *(data.end() - 1)) { return -1; // RETURN } return bdlt::Iso8601Util::parse( value, data.data() + 1, static_cast<int>(data.length() - k_STRING_LENGTH_WITH_QUOTES)); } inline int ParserUtil::getValue(char *value, const bsl::string_view& data) { signed char tmp; // Note that 'char' is unsigned on IBM. const int rc = getIntegralValue(&tmp, data); if (!rc) { *value = tmp; } return rc; } inline int ParserUtil::getValue(unsigned char *value, const bsl::string_view& data) { return getUnsignedIntegralValue(value, data); } inline int ParserUtil::getValue(signed char *value, const bsl::string_view& data) { return getValue((char *) value, data); } inline int ParserUtil::getValue(short *value, const bsl::string_view& data) { return getIntegralValue(value, data); } inline int ParserUtil::getValue(unsigned short *value, const bsl::string_view& data) { return getUnsignedIntegralValue(value, data); } inline int ParserUtil::getValue(int *value, const bsl::string_view& data) { return getIntegralValue(value, data); } inline int ParserUtil::getValue(unsigned int *value, const bsl::string_view& data) { return getUnsignedIntegralValue(value, data); } inline int ParserUtil::getValue(bsls::Types::Int64 *value, const bsl::string_view& data) { return getIntegralValue(value, data); } inline int ParserUtil::getValue(bsls::Types::Uint64 *value, const bsl::string_view& data) { return getUnsignedIntegralValue(value, data); } inline int ParserUtil::getValue(float *value, const bsl::string_view& data) { double tmp; const int rc = getValue(&tmp, data); if (!rc) { *value = static_cast<float>(tmp); } return rc; } inline int ParserUtil::getValue(bsl::string *value, const bsl::string_view& data) { return bdljsn::StringUtil::readString( value, data, bdljsn::StringUtil::e_ACCEPT_CAPITAL_UNICODE_ESCAPE); } inline int ParserUtil::getValue(bdlt::Date *value, const bsl::string_view& data) { return getDateAndTimeValue(value, data); } inline int ParserUtil::getValue(bdlt::Datetime *value, const bsl::string_view& data) { return getDateAndTimeValue(value, data); } inline int ParserUtil::getValue(bdlt::DatetimeTz *value, const bsl::string_view& data) { return getDateAndTimeValue(value, data); } inline int ParserUtil::getValue(bdlt::DateTz *value, const bsl::string_view& data) { return getDateAndTimeValue(value, data); } inline int ParserUtil::getValue(bdlt::Time *value, const bsl::string_view& data) { return getDateAndTimeValue(value, data); } inline int ParserUtil::getValue(bdlt::TimeTz *value, const bsl::string_view& data) { return getDateAndTimeValue(value, data); } inline int ParserUtil::getValue(DateOrDateTz *value, const bsl::string_view& data) { return getDateAndTimeValue(value, data); } inline int ParserUtil::getValue(TimeOrTimeTz *value, const bsl::string_view& data) { return getDateAndTimeValue(value, data); } inline int ParserUtil::getValue(DatetimeOrDatetimeTz *value, const bsl::string_view& data) { return getDateAndTimeValue(value, data); } } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2018 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 ----------------------------------