mirror of
https://github.com/akheron/jansson.git
synced 2025-04-07 06:25:02 +00:00
Add JSON_REJECT_DUPLICATES decoding flag
With this flag, a decoding error is issued if any JSON object in the input contains duplicate keys. Fixes GH-3.
This commit is contained in:
parent
92f6f0f22c
commit
49fc708d4c
4 changed files with 64 additions and 28 deletions
|
@ -780,14 +780,28 @@ See :ref:`rfc-conformance` for a discussion on Jansson's conformance
|
|||
to the JSON specification. It explains many design decisions that
|
||||
affect especially the behavior of the decoder.
|
||||
|
||||
Each function takes a *flags* parameter that can be used to control
|
||||
the behavior of the decoder. Its default value is 0. The following
|
||||
macros can be ORed together to obtain *flags*.
|
||||
|
||||
``JSON_REJECT_DUPLICATES``
|
||||
Issue a decoding error if any JSON object in the input text
|
||||
contains duplicate keys. Without this flag, the value of the last
|
||||
occurence of each key ends up in the result. Key equivalence is
|
||||
checked byte-by-byte, without special Unicode comparison
|
||||
algorithms.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
The following functions perform the actual JSON decoding.
|
||||
|
||||
.. function:: json_t *json_loads(const char *input, size_t flags, json_error_t *error)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Decodes the JSON string *input* and returns the array or object it
|
||||
contains, or *NULL* on error, in which case *error* is filled with
|
||||
information about the error. *flags* is currently unused, and
|
||||
should be set to 0.
|
||||
information about the error. *flags* is described above.
|
||||
|
||||
.. function:: json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error)
|
||||
|
||||
|
@ -797,8 +811,7 @@ affect especially the behavior of the decoder.
|
|||
returns the array or object it contains, or *NULL* on error, in
|
||||
which case *error* is filled with information about the error. This
|
||||
is similar to :func:`json_loads()` except that the string doesn't
|
||||
need to be null-terminated. *flags* is currently unused, and should
|
||||
be set to 0.
|
||||
need to be null-terminated. *flags* is described above.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
|
@ -808,8 +821,8 @@ affect especially the behavior of the decoder.
|
|||
|
||||
Decodes the JSON text in stream *input* and returns the array or
|
||||
object it contains, or *NULL* on error, in which case *error* is
|
||||
filled with information about the error. *flags* is currently
|
||||
unused, and should be set to 0.
|
||||
filled with information about the error. *flags* is described
|
||||
above.
|
||||
|
||||
.. function:: json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
|
||||
|
||||
|
@ -817,8 +830,8 @@ affect especially the behavior of the decoder.
|
|||
|
||||
Decodes the JSON text in file *path* and returns the array or
|
||||
object it contains, or *NULL* on error, in which case *error* is
|
||||
filled with information about the error. *flags* is currently
|
||||
unused, and should be set to 0.
|
||||
filled with information about the error. *flags* is described
|
||||
above.
|
||||
|
||||
|
||||
.. _apiref-pack:
|
||||
|
|
|
@ -214,13 +214,18 @@ json_t *json_copy(json_t *value);
|
|||
json_t *json_deep_copy(json_t *value);
|
||||
|
||||
|
||||
/* loading, printing */
|
||||
/* decoding */
|
||||
|
||||
#define JSON_REJECT_DUPLICATES 0x1
|
||||
|
||||
json_t *json_loads(const char *input, size_t flags, json_error_t *error);
|
||||
json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error);
|
||||
json_t *json_loadf(FILE *input, size_t flags, json_error_t *error);
|
||||
json_t *json_load_file(const char *path, size_t flags, json_error_t *error);
|
||||
|
||||
|
||||
/* encoding */
|
||||
|
||||
#define JSON_INDENT(n) (n & 0x1F)
|
||||
#define JSON_COMPACT 0x20
|
||||
#define JSON_ENSURE_ASCII 0x40
|
||||
|
|
39
src/load.c
39
src/load.c
|
@ -642,9 +642,9 @@ static void lex_close(lex_t *lex)
|
|||
|
||||
/*** parser ***/
|
||||
|
||||
static json_t *parse_value(lex_t *lex, json_error_t *error);
|
||||
static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error);
|
||||
|
||||
static json_t *parse_object(lex_t *lex, json_error_t *error)
|
||||
static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *object = json_object();
|
||||
if(!object)
|
||||
|
@ -667,6 +667,14 @@ static json_t *parse_object(lex_t *lex, json_error_t *error)
|
|||
if(!key)
|
||||
return NULL;
|
||||
|
||||
if(flags & JSON_REJECT_DUPLICATES) {
|
||||
if(json_object_get(object, key)) {
|
||||
jsonp_free(key);
|
||||
error_set(error, lex, "duplicate object key");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
lex_scan(lex, error);
|
||||
if(lex->token != ':') {
|
||||
jsonp_free(key);
|
||||
|
@ -675,7 +683,7 @@ static json_t *parse_object(lex_t *lex, json_error_t *error)
|
|||
}
|
||||
|
||||
lex_scan(lex, error);
|
||||
value = parse_value(lex, error);
|
||||
value = parse_value(lex, flags, error);
|
||||
if(!value) {
|
||||
jsonp_free(key);
|
||||
goto error;
|
||||
|
@ -709,7 +717,7 @@ error:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static json_t *parse_array(lex_t *lex, json_error_t *error)
|
||||
static json_t *parse_array(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *array = json_array();
|
||||
if(!array)
|
||||
|
@ -720,7 +728,7 @@ static json_t *parse_array(lex_t *lex, json_error_t *error)
|
|||
return array;
|
||||
|
||||
while(lex->token) {
|
||||
json_t *elem = parse_value(lex, error);
|
||||
json_t *elem = parse_value(lex, flags, error);
|
||||
if(!elem)
|
||||
goto error;
|
||||
|
||||
|
@ -749,7 +757,7 @@ error:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static json_t *parse_value(lex_t *lex, json_error_t *error)
|
||||
static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *json;
|
||||
|
||||
|
@ -782,11 +790,11 @@ static json_t *parse_value(lex_t *lex, json_error_t *error)
|
|||
break;
|
||||
|
||||
case '{':
|
||||
json = parse_object(lex, error);
|
||||
json = parse_object(lex, flags, error);
|
||||
break;
|
||||
|
||||
case '[':
|
||||
json = parse_array(lex, error);
|
||||
json = parse_array(lex, flags, error);
|
||||
break;
|
||||
|
||||
case TOKEN_INVALID:
|
||||
|
@ -804,7 +812,7 @@ static json_t *parse_value(lex_t *lex, json_error_t *error)
|
|||
return json;
|
||||
}
|
||||
|
||||
static json_t *parse_json(lex_t *lex, json_error_t *error)
|
||||
static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
lex_scan(lex, error);
|
||||
if(lex->token != '[' && lex->token != '{') {
|
||||
|
@ -812,7 +820,7 @@ static json_t *parse_json(lex_t *lex, json_error_t *error)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
return parse_value(lex, error);
|
||||
return parse_value(lex, flags, error);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
|
@ -841,8 +849,6 @@ json_t *json_loads(const char *string, size_t flags, json_error_t *error)
|
|||
json_t *result;
|
||||
string_data_t stream_data;
|
||||
|
||||
(void)flags; /* unused */
|
||||
|
||||
stream_data.data = string;
|
||||
stream_data.pos = 0;
|
||||
|
||||
|
@ -851,7 +857,7 @@ json_t *json_loads(const char *string, size_t flags, json_error_t *error)
|
|||
|
||||
jsonp_error_init(error, "<string>");
|
||||
|
||||
result = parse_json(&lex, error);
|
||||
result = parse_json(&lex, flags, error);
|
||||
if(!result)
|
||||
goto out;
|
||||
|
||||
|
@ -892,8 +898,6 @@ json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t
|
|||
json_t *result;
|
||||
buffer_data_t stream_data;
|
||||
|
||||
(void)flags; /* unused */
|
||||
|
||||
stream_data.data = buffer;
|
||||
stream_data.pos = 0;
|
||||
stream_data.len = buflen;
|
||||
|
@ -903,7 +907,7 @@ json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t
|
|||
|
||||
jsonp_error_init(error, "<buffer>");
|
||||
|
||||
result = parse_json(&lex, error);
|
||||
result = parse_json(&lex, flags, error);
|
||||
if(!result)
|
||||
goto out;
|
||||
|
||||
|
@ -924,7 +928,6 @@ json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
|
|||
lex_t lex;
|
||||
const char *source;
|
||||
json_t *result;
|
||||
(void)flags; /* unused */
|
||||
|
||||
if(lex_init(&lex, (get_func)fgetc, input))
|
||||
return NULL;
|
||||
|
@ -936,7 +939,7 @@ json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
|
|||
|
||||
jsonp_error_init(error, source);
|
||||
|
||||
result = parse_json(&lex, error);
|
||||
result = parse_json(&lex, flags, error);
|
||||
if(!result)
|
||||
goto out;
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include <string.h>
|
||||
#include "util.h"
|
||||
|
||||
int main()
|
||||
static void file_not_found()
|
||||
{
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
|
@ -21,6 +21,21 @@ int main()
|
|||
fail("json_load_file returned an invalid line number");
|
||||
if(strcmp(error.text, "unable to open /path/to/nonexistent/file.json: No such file or directory") != 0)
|
||||
fail("json_load_file returned an invalid error message");
|
||||
}
|
||||
|
||||
static void reject_duplicates()
|
||||
{
|
||||
json_error_t error;
|
||||
|
||||
if(json_loads("{\"foo\": 1, \"foo\": 2}", JSON_REJECT_DUPLICATES, &error))
|
||||
fail("json_loads did not detect a duplicate key");
|
||||
check_error("duplicate object key near '\"foo\"'", "<string>", 1, 16, 16);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
file_not_found();
|
||||
reject_duplicates();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue