diff --git a/doc/apiref.rst b/doc/apiref.rst index 198c0c2..d92fb3b 100644 --- a/doc/apiref.rst +++ b/doc/apiref.rst @@ -950,6 +950,16 @@ macros can be ORed together to obtain *flags*. .. versionadded:: 2.1 +``JSON_DECODE_INT_AS_REAL`` + JSON defines only one number type. Jansson distinguishes between + ints and reals. For more information see :ref:`real-vs-integer`. + With this flag enabled the decoder interprets all numbers as real + values. Integers that do not have an exact double representation + will silently result in a loss of precision. Integers that cause + a double overflow will cause an error. + + .. versionadded:: 2.5 + Each function also takes an optional :type:`json_error_t` parameter that is filled with error information if decoding fails. It's also updated on success; the number of bytes of input read is written to diff --git a/doc/conformance.rst b/doc/conformance.rst index 6f4c929..19f603d 100644 --- a/doc/conformance.rst +++ b/doc/conformance.rst @@ -38,6 +38,8 @@ strings. Numbers ======= +.. _real-vs-integer: + Real vs. Integer ---------------- @@ -51,7 +53,8 @@ A JSON number is considered to be a real number if its lexical representation includes one of ``e``, ``E``, or ``.``; regardless if its actual numeric value is a true integer (e.g., all of ``1E6``, ``3.0``, ``400E-2``, and ``3.14E3`` are mathematical integers, but -will be treated as real values). +will be treated as real values). With the ``JSON_DECODE_INT_AS_REAL`` +decoder flag set all numbers are interpreted as real. All other JSON numbers are considered integers. diff --git a/src/jansson.h b/src/jansson.h index 77cc5ba..d441fcf 100644 --- a/src/jansson.h +++ b/src/jansson.h @@ -236,9 +236,10 @@ json_t *json_deep_copy(json_t *value); /* decoding */ -#define JSON_REJECT_DUPLICATES 0x1 -#define JSON_DISABLE_EOF_CHECK 0x2 -#define JSON_DECODE_ANY 0x4 +#define JSON_REJECT_DUPLICATES 0x1 +#define JSON_DISABLE_EOF_CHECK 0x2 +#define JSON_DECODE_ANY 0x4 +#define JSON_DECODE_INT_AS_REAL 0x8 typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data); diff --git a/src/load.c b/src/load.c index 1eb3be4..b671892 100644 --- a/src/load.c +++ b/src/load.c @@ -775,6 +775,7 @@ error: static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error) { json_t *json; + double value; switch(lex->token) { case TOKEN_STRING: { @@ -783,7 +784,15 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error) } case TOKEN_INTEGER: { - json = json_integer(lex->value.integer); + if (flags & JSON_DECODE_INT_AS_REAL) { + if(jsonp_strtod(&lex->saved_text, &value)) { + error_set(error, lex, "real number overflow"); + return NULL; + } + json = json_real(value); + } else { + json = json_integer(lex->value.integer); + } break; } diff --git a/test/suites/api/test_load.c b/test/suites/api/test_load.c index 30ba65c..6eddb34 100644 --- a/test/suites/api/test_load.c +++ b/test/suites/api/test_load.c @@ -87,6 +87,28 @@ static void decode_any() json_decref(json); } +static void decode_int_as_real() +{ + json_t *json; + json_error_t error; + + // This number cannot be represented exactly by a double + const char *imprecise = "9007199254740993"; + json_int_t expected = 9007199254740992ll; + + json = json_loads("42", JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY, &error); + if (!json || !json_is_real(json) || json_real_value(json) != 42.0) + fail("json_load decode int as real failed - int"); + json_decref(json); + + // Tests that large numbers are handled correctly + json = json_loads(imprecise, JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY, + &error); + if (!json || !json_is_real(json) || expected != json_real_value(json)) + fail("json_load decode int as real failed - expected imprecision"); + json_decref(json); +} + static void load_wrong_args() { json_t *json; @@ -132,6 +154,7 @@ static void run_tests() reject_duplicates(); disable_eof_check(); decode_any(); + decode_int_as_real(); load_wrong_args(); position(); }