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)
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
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)
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:
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
/*** 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)
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);
}
lex_scan(lex, error);
- value = parse_value(lex, error);
+ value = parse_value(lex, flags, error);
if(!value) {
jsonp_free(key);
goto 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)
return array;
while(lex->token) {
- json_t *elem = parse_value(lex, error);
+ json_t *elem = parse_value(lex, flags, error);
if(!elem)
goto 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;
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:
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 != '{') {
return NULL;
}
- return parse_value(lex, error);
+ return parse_value(lex, flags, error);
}
typedef struct
json_t *result;
string_data_t stream_data;
- (void)flags; /* unused */
-
stream_data.data = string;
stream_data.pos = 0;
jsonp_error_init(error, "<string>");
- result = parse_json(&lex, error);
+ result = parse_json(&lex, flags, error);
if(!result)
goto out;
json_t *result;
buffer_data_t stream_data;
- (void)flags; /* unused */
-
stream_data.data = buffer;
stream_data.pos = 0;
stream_data.len = buflen;
jsonp_error_init(error, "<buffer>");
- result = parse_json(&lex, error);
+ result = parse_json(&lex, flags, error);
if(!result)
goto out;
lex_t lex;
const char *source;
json_t *result;
- (void)flags; /* unused */
if(lex_init(&lex, (get_func)fgetc, input))
return NULL;
jsonp_error_init(error, source);
- result = parse_json(&lex, error);
+ result = parse_json(&lex, flags, error);
if(!result)
goto out;
#include <string.h>
#include "util.h"
-int main()
+static void file_not_found()
{
json_t *json;
json_error_t error;
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;
}