Quick Links: |
Provide a value-semantic type representing a JSON number. More...
Namespaces | |
namespace | bdljsn |
bdljsn::JsonNumber | value-semantic type representing a JSON number |
bdljsn::JsonNumber
, that represents a JSON number. The value of a bdljsn::JsonNumber
object is set at construction using a string representation of the JSON number (see JSON Textual Specification) or from one of several C++ arithmetic types (see Supported Conversions). bdljsn::JsonNumber
objects. For such operations, the value of a bdljsn::JsonNumber
object can be converted to any of those supported types, though the conversion may not be exact. bdlsn::JsonNumber
equality operation returns true
if the string representation of the number (returned by the value
accessor method) is the same, even where the two strings represent the same number (e.g., "10" and "1e1"). This definition of equality reflects the fact that the JSON textual representation for the two JsonNumber
objects will be different. The function isEqual
is provided for (a more expensive) numeric equality comparison. /^-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][-+]?[0-9]+)?\z/
\n
. 1 2.1 -3 4e1 5.1e+2 6.12e-3 7e+04 -8.1e+005
bdljsn::JsonNumber
object is determined by the input string and that is not canonical. Thus, unequal strings lead to unequal bdljsn::JsonNumber
objects even if their numeric values (e.g., asInt
) are equal: assert(bdljsn::JsonNumber("1") != bdljsn::JsonNumber("1.0"); assert(bdljsn::JsonNumber("1").asInt() == bdljsn::JsonNumber("1.0") .asInt();
bdljsn::JsonNumber
object can be converted to an assortment of useful types: int
unsigned int
bsls::Types::Int64
bsls::Types::Uint64
float
double
bdldfp::Decimal64
asInt
and asDouble
) This component provides explicit conversion operations for floating point types (float
, double
, Decimal64
) on platforms where explicit conversions are supported. bdlsjn::JsonNumber
to another representation may result in a value that is not the same as the original bdljsn::JsonNumber
. Either the bdljsn::JsonNumber
may represent a numeric value outside of the representable range of the requested type (i.e., it is too large, or too small), or the value may not be representable exactly. bdljsn::JsonNumber
conversions will return the closest approximation of the bdljsn::JsonNumber
, even when a non-zero status is returned indicating an inexact conversion. bdljsn::JsonNumber
is outside of the representable range (-INF, +INF)
. bdljsn::JsonNumber
is very often not an error. bdldfp::Decimal64
, the function asDecimal64Exact
returns additional status indicating whether the conversion is exact. An exact conversion for a Decimal64
is one that preserves all the significant digits resulting in a decimal representation having the same numerical value as the original JSON text. Note that asDecimal64Exact
has very similar performance to asDecimal64
(i.e., there is not a notable performance penalty to determining this property). asDecimal64Exact
will return bdljsn::JsonNumber::k_NOT_EXACT
if the input is 0 with an exponent outside of the range ([-398, 369]
). For example, 0e-400
. This reflects the behavior of the underlying 3rd party implementation. Please contact BDE if this is a concern. bdljsn::JsonNumber::isValidNumber
function; otherwise, one might try to create a bdljsn::JsonNumber
object from an invalid specification and that leads to undefined behavior. struct { const char *d_text_p; const char *d_description_p; bool d_expected; } USER_INPUT[] = { // VALUE DESCRIPTION EXP // ---------------------- -------------------------------------- --- // Invalid Input (that is valid in other contexts). { "1.", "Not uncommon way to write '1'." , 0 } , { "1,000", "No commas allowed" , 0 } , { "01", "Leading '0', disallowed by JSON." , 0 } , { "", "0 per 'atoi', disallowed by JSON." , 0 } , { "Hello, world!", "0 per 'atoi', disallowed by JSON." , 0 } , { "NaN", "invalid number" , 0 } , { "INF", "invalid number" , 0 } , { "-INF", "invalid number" , 0 } , { "+INF", "invalid number" , 0 } // Valid input (some surprising) , { "1234567890", "Integral value" , 1 } , { "1234567890.123456", "Non-integral value" , 1 } , { "1234567890.1234567", "Beyond Decimal64 precision" , 1 } , { "-9223372036854775809", "INT64_MIN, underflow, but valid JSON", 1 } , { "1.5e27", "INT64_MAX, overflow, but valid JSON", 1 } , { "999999999999999999999999999999999999999999999999999999999999" "e" "999999999999999999999999999999999999999999999999999999999999", "astronomic value" , 1 } }; const bsl::size_t NUM_USER_INPUT = sizeof USER_INPUT / sizeof *USER_INPUT;
bdljsn::JsonNumber
object and add that object to a vector for later processing. bsl::vector<bdljsn::JsonNumber> userInput; // when valid input for (bsl::size_t ti = 0; ti < NUM_USER_INPUT; ++ti) { const char *TEXT = USER_INPUT[ti].d_text_p; const char *DESC = USER_INPUT[ti].d_description_p; (void) DESC; const bool EXP = USER_INPUT[ti].d_expected; const bool isValid = bdljsn::JsonNumber::isValidNumber(TEXT); assert(EXP == isValid); if (isValid) { userInput.push_back(bdljsn::JsonNumber(TEXT)); } }
assert(6 == userInput.size());
bdljsn::JsonNumber
objects can validly hold values numeric values that cannot be converted to any of the supported types (e.g., the "astronomic value") for arithmetic operations. Applications that accept arbitrary bdljsn::JsonNumber
objects should be prepared to categorize the contained value and adapt their handling accordingly. In practice, applications may have some assurances of the contents of received bdljsn::JsonNumber
objects. Here, we intentionally avoid such assumptions to explore the wide range of variations that can arise. "OK":
"NG":
for
loop) for examining our input, the same userInput
vector created in Example 1: for (bsl::size_t i = 0; i < userInput.size(); ++i) { const bdljsn::JsonNumber obj = userInput[i];
if (obj.isIntegral()) { bsl::cout << "Integral: ";
bslsl::Type::Int64
is as large a number as we can accept. bsls::Types::Int64 value; int rc = obj.asInt64(&value); switch (rc) { case 0: { bsl::cout << value << " : OK to USE" << bsl::endl; } break; case bdljsn::JsonNumber::k_OVERFLOW: { bsl::cout << obj.value() << ": NG too large" << bsl::endl; } break; case bdljsn::JsonNumber::k_UNDERFLOW: { bsl::cout << obj.value() << ": NG too small" << bsl::endl; } break; case bdljsn::JsonNumber::k_NOT_INTEGRAL: { assert(!"reached"); } break; }
bdldfp::Decimal64
in this example -- and further categorize it as exact/inexact, too large/small. } else { bsl::cout << "Not-Integral: "; bdldfp::Decimal64 value; int rc = obj.asDecimal64Exact(&value); switch (rc) { case 0: { bsl::cout << value << " : exact: OK to USE"; } break; case bdljsn::JsonNumber::k_INEXACT: { bsl::cout << value << ": inexact: USE approximation"; } break; case bdljsn::JsonNumber::k_NOT_INTEGRAL: { assert(!"reached"); } break; } const bdldfp::Decimal64 INF = bsl::numeric_limits<bdldfp::Decimal64>::infinity(); if ( INF == value) { bsl::cout << ": NG too large" << bsl::endl; } else if (-INF == value) { bsl::cout << ": NG too small" << bsl::endl; } else { bsl::cout << bsl::endl; } } }
Integral: 1234567890 : OK to USE Not-Integral: 1234567890.123456 : exact: OK to USE Not-Integral: 1234567890.123457: inexact: USE approximation Integral: -9223372036854775809: NG too small Integral: 1.5e27: NG too large Integral: 999999999999999999999999999999999999999999999999999999999999e9999 99999999999999999999999999999999999999999999999999999999: NG too large