Add JSON_REJECT_DUPLICATES decoding flag
authorPetri Lehtinen <petri@digip.org>
Sun, 15 May 2011 10:57:48 +0000 (13:57 +0300)
committerPetri Lehtinen <petri@digip.org>
Sun, 15 May 2011 10:57:49 +0000 (13:57 +0300)
With this flag, a decoding error is issued if any JSON object in the
input contains duplicate keys.

Fixes GH-3.

doc/apiref.rst
src/jansson.h
src/load.c
test/suites/api/test_load.c

index ea1877d..6ab7a6f 100644 (file)
@@ -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:
index 2a0860e..9bdbb11 100644 (file)
@@ -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
index 9933583..ba00386 100644 (file)
@@ -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;
 
index ecb46d8..83143fb 100644 (file)
@@ -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;
 }