strbuffer.h \
utf.c \
utf.h \
- value.c
+ value.c \
+ variadic.c
libjansson_la_LDFLAGS = \
-export-symbols-regex '^json_' \
-version-info 3:0:3
}
+/* error reporting */
+
+typedef struct json_error_t json_error_t;
+
+const char *json_error_msg(const json_error_t *error);
+int json_error_line(const json_error_t *error);
+
+
/* getters, setters, manipulation */
size_t json_object_size(const json_t *object);
int json_integer_set(json_t *integer, json_int_t value);
int json_real_set(json_t *real, double value);
+json_t *json_pack(json_error_t **error, const char *fmt, ...);
+int json_unpack(json_t *root, json_error_t **error, const char *fmt, ...);
/* equality */
json_t *json_deep_copy(json_t *value);
-/* error reporting */
-
-typedef struct json_error_t json_error_t;
-
-const char *json_error_msg(const json_error_t *error);
-int json_error_line(const json_error_t *error);
-
-
/* loading, printing */
json_t *json_loads(const char *input, size_t flags, json_error_t **error);
const object_key_t *jsonp_object_iter_fullkey(void *iter);
+#define JSON_ERROR_MSG_LENGTH 160
+
+struct json_error_t {
+ char msg[JSON_ERROR_MSG_LENGTH];
+ int line;
+};
+
#endif
/*** error reporting ***/
-#define JSON_ERROR_MSG_LENGTH 160
-
-struct json_error_t {
- char msg[JSON_ERROR_MSG_LENGTH];
- int line;
-};
-
const char *json_error_msg(const json_error_t *error)
{
return error ? error->msg : NULL;
--- /dev/null
+/*
+ * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2010 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
+ *
+ * Jansson is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ */
+
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+
+#include <jansson.h>
+#include "jansson_private.h"
+
+static void error_init(json_error_t **error)
+{
+ if(error)
+ *error = NULL;
+}
+
+static void error_set(json_error_t **error, const int line, const char *msg, ...)
+{
+ va_list ap;
+
+ if(!error || *error)
+ return;
+
+ *error = calloc(1, sizeof(json_error_t));
+ if(!*error)
+ return;
+
+ va_start(ap, msg);
+ vsnprintf((*error)->msg, JSON_ERROR_MSG_LENGTH, msg, ap);
+ va_end(ap);
+
+ va_start(ap, msg);
+ vfprintf(stderr, msg, ap);
+ va_end(ap);
+
+ (*error)->line = line;
+}
+
+json_t *json_pack(json_error_t **error, const char *fmt, ...) {
+ int fmt_length = strlen(fmt);
+ va_list ap;
+
+ /* Keep a stack of containers (lists and objects) */
+ int depth = 0;
+ json_t **stack = NULL;
+
+ /* Keep a list of objects we create in case of error */
+ int free_count = 0;
+ json_t **free_list = NULL;
+
+ json_t *cur = NULL; /* Current container */
+ json_t *root = NULL; /* root object */
+ json_t *obj = NULL;
+
+ char *key = NULL; /* Current key in an object */
+ char *s;
+
+ int line = 1;
+
+ /* Allocation provisioned for worst case */
+ stack = calloc(fmt_length, sizeof(json_t *));
+ free_list = calloc(fmt_length, sizeof(json_t *));
+
+ error_init(error);
+
+ if(!stack || !free_list)
+ goto out;
+
+ va_start(ap, fmt);
+ while(*fmt) {
+ switch(*fmt) {
+ case '\n':
+ line++;
+ break;
+
+ case ' ': /* Whitespace */
+ break;
+
+ case ',': /* Element spacer */
+ if(!root)
+ {
+ error_set(error, line,
+ "Unexpected COMMA precedes root element!");
+ root = NULL;
+ goto out;
+ }
+
+ if(!cur)
+ {
+ error_set(error, line,
+ "Unexpected COMMA outside a list or object!");
+ root = NULL;
+ goto out;
+ }
+
+ if(key)
+ {
+ error_set(error, line, "Expected KEY, got COMMA!");
+ root = NULL;
+ goto out;
+ }
+ break;
+
+ case ':': /* Key/value separator */
+ if(!key)
+ {
+ error_set(error, line, "Got key/value separator without "
+ "a key preceding it!");
+ root = NULL;
+ goto out;
+ }
+
+ if(!json_is_object(cur))
+ {
+ error_set(error, line, "Got a key/value separator "
+ "(':') outside an object!");
+ root = NULL;
+ goto out;
+ }
+
+ break;
+
+ case ']': /* Close array or object */
+ case '}':
+
+ if(key)
+ {
+ error_set(error, line, "OBJECT or ARRAY ended with an "
+ "incomplete key/value pair!");
+ root = NULL;
+ goto out;
+ }
+
+ if(depth <= 0)
+ {
+ error_set(error, line,
+ "Too many close-brackets '%c'", *fmt);
+ root = NULL;
+ goto out;
+ }
+
+ if(*fmt == ']' && !json_is_array(cur))
+ {
+ error_set(error, line,
+ "Stray close-array ']' character");
+ root = NULL;
+ goto out;
+ }
+
+ if(*fmt == '}' && !json_is_object(cur))
+ {
+ error_set(error, line,
+ "Stray close-object '}' character");
+ root = NULL;
+ goto out;
+ }
+
+ cur = stack[--depth];
+ break;
+
+ case '[':
+ obj = json_array();
+ goto obj_common;
+
+ case '{':
+ obj = json_object();
+ goto obj_common;
+
+ case 's': /* string */
+ s = va_arg(ap, char*);
+
+ if(!s)
+ {
+ error_set(error, line,
+ "Refusing to handle a NULL string");
+ root = NULL;
+ goto out;
+ }
+
+ if(json_is_object(cur) && !key)
+ {
+ /* It's a key */
+ key = s;
+ break;
+ }
+
+ obj = json_string(s);
+ goto obj_common;
+
+ case 'n': /* null */
+ obj = json_null();
+ goto obj_common;
+
+ case 'b': /* boolean */
+ obj = va_arg(ap, int) ?
+ json_true() : json_false();
+ goto obj_common;
+
+ case 'i': /* integer */
+ obj = json_integer(va_arg(ap, int));
+ goto obj_common;
+
+ case 'f': /* double-precision float */
+ obj = json_real(va_arg(ap, double));
+ goto obj_common;
+
+ case 'O': /* a json_t object; increments refcount */
+ obj = va_arg(ap, json_t *);
+ json_incref(obj);
+ goto obj_common;
+
+ case 'o': /* a json_t object; doesn't increment refcount */
+ obj = va_arg(ap, json_t *);
+ goto obj_common;
+
+obj_common: free_list[free_count++] = obj;
+
+ /* Root this object to its parent */
+ if(json_is_object(cur)) {
+ if(!key)
+ {
+ error_set(error, line,
+ "Expected key, got identifier '%c'!", *fmt);
+ root = NULL;
+ goto out;
+ }
+
+ json_object_set_new(cur, key, obj);
+ key = NULL;
+ }
+ else if(json_is_array(cur))
+ {
+ json_array_append_new(cur, obj);
+ }
+ else if(!root)
+ {
+ printf("Rooting\n");
+ root = obj;
+ }
+ else
+ {
+ error_set(error, line, "Can't figure out where to attach "
+ "'%c' object!", *fmt);
+ root = NULL;
+ goto out;
+ }
+
+ /* If it was a container ('[' or '{'), descend on the stack */
+ if(json_is_array(obj) || json_is_object(obj))
+ {
+ stack[depth++] = cur;
+ cur = obj;
+ }
+
+ break;
+ }
+ fmt++;
+ }
+ va_end(ap);
+
+ if(depth != 0) {
+ error_set(error, line,
+ "Missing object or array close-brackets in format string");
+ root = NULL;
+ goto out;
+ }
+
+ /* Success: don't free everything we just built! */
+ free_count = 0;
+
+out:
+ while(free_count)
+ json_decref(free_list[--free_count]);
+
+ if(free_list)
+ free(free_list);
+
+ if(stack)
+ free(stack);
+
+ return(root);
+}
+
+int json_unpack(json_t *root, json_error_t **error, const char *fmt, ...) {
+ va_list ap;
+
+ int rv=0; /* Return value */
+ int line = 1; /* Line number */
+
+ /* Keep a stack of containers (lists and objects) */
+ int depth = 0;
+ json_t **stack;
+
+ int array_index = 0;
+ char *key = NULL; /* Current key in an object */
+
+ json_t *cur = NULL; /* Current container */
+ json_t *obj = NULL;
+
+ int fmt_length = strlen(fmt);
+
+ error_init(error);
+
+ /* Allocation provisioned for worst case */
+ stack = calloc(fmt_length, sizeof(json_t *));
+ if(!stack)
+ {
+ error_set(error, line, "Out of memory!");
+ rv = -1;
+ goto out;
+ }
+
+ /* Even if we're successful, we need to know if the number of
+ * arguments provided matches the number of JSON objects.
+ * We can do this by counting the elements in every array or
+ * object we open up, and decrementing the count as we visit
+ * their children. */
+ int unvisited = 0;
+
+ va_start(ap, fmt);
+ while(*fmt)
+ {
+ switch(*fmt)
+ {
+ case ' ': /* Whitespace */
+ break;
+
+ case '\n': /* Line break */
+ line++;
+ break;
+
+ case ',': /* Element spacer */
+
+ if(!cur)
+ {
+ error_set(error, line,
+ "Unexpected COMMA outside a list or object!");
+ rv = -1;
+ goto out;
+ }
+
+ if(key)
+ {
+ error_set(error, line, "Expected KEY, got COMMA!");
+ rv = -1;
+ goto out;
+ }
+ break;
+
+ case ':': /* Key/value separator */
+ if(!json_is_object(cur) || !key)
+ {
+ error_set(error, line, "Unexpected ':'");
+ rv = -1;
+ goto out;
+ }
+ break;
+
+ case '[':
+ case '{':
+ /* Fetch object */
+ if(!cur)
+ {
+ obj = root;
+ }
+ else if(json_is_object(cur))
+ {
+ if(!key)
+ {
+ error_set(error, line, "Objects can't be keys");
+ rv = -1;
+ goto out;
+ }
+ obj = json_object_get(cur, key);
+ unvisited--;
+ key = NULL;
+ }
+ else if(json_is_array(cur))
+ {
+ obj = json_array_get(cur, array_index);
+ unvisited--;
+ array_index++;
+ }
+ else
+ {
+ assert(0);
+ }
+
+ /* Make sure we got what we expected */
+ if(*fmt=='{' && !json_is_object(obj))
+ {
+ rv = -2;
+ goto out;
+ }
+
+ if(*fmt=='[' && !json_is_array(obj))
+ {
+ rv = -2;
+ goto out;
+ }
+
+ unvisited += json_is_object(obj) ?
+ json_object_size(obj) :
+ json_array_size(obj);
+
+ /* Descend */
+ stack[depth++] = cur;
+ cur = obj;
+
+ key = NULL;
+
+ break;
+
+
+ case ']':
+ case '}':
+
+ if(json_is_array(cur) && *fmt!=']')
+ {
+ error_set(error, line, "Missing ']'");
+ rv = -1;
+ goto out;
+ }
+
+ if(json_is_object(cur) && *fmt!='}')
+ {
+ error_set(error, line, "Missing '}'");
+ rv = -1;
+ goto out;
+ }
+
+ if(key)
+ {
+ error_set(error, line, "Unexpected '%c'", *fmt);
+ rv = -1;
+ goto out;
+ }
+
+ if(depth <= 0)
+ {
+ error_set(error, line, "Unexpected '%c'", *fmt);
+ rv = -1;
+ goto out;
+ }
+
+ cur = stack[--depth];
+
+ break;
+
+ case 's':
+ if(!key && json_is_object(cur))
+ {
+ /* constant string for key */
+ key = va_arg(ap, char*);
+ break;
+ }
+ /* fall through */
+
+ case 'i': /* integer */
+ case 'f': /* double-precision float */
+ case 'O': /* a json_t object; increments refcount */
+ case 'o': /* a json_t object; borrowed reference */
+ case 'b': /* boolean */
+ case 'n': /* null */
+
+ /* Fetch object */
+ if(!cur)
+ {
+ obj = root;
+ }
+ else if(json_is_object(cur))
+ {
+ if(!key)
+ {
+ error_set(error, line,
+ "Only strings may be used as keys!");
+ rv = -1;
+ goto out;
+ }
+
+ obj = json_object_get(cur, key);
+ unvisited--;
+ key = NULL;
+ }
+ else if(json_is_array(cur))
+ {
+ obj = json_array_get(cur, array_index);
+ unvisited--;
+ array_index++;
+ }
+ else
+ {
+ error_set(error, line,
+ "Unsure how to retrieve JSON object '%c'",
+ *fmt);
+ rv = -1;
+ goto out;
+ }
+
+ switch(*fmt)
+ {
+ case 's':
+ if(!json_is_string(obj))
+ {
+ error_set(error, line,
+ "Type mismatch! Object wasn't a string.");
+ rv = -2;
+ goto out;
+ }
+ *va_arg(ap, const char**) = json_string_value(obj);
+ break;
+
+ case 'i':
+ if(!json_is_integer(obj))
+ {
+ error_set(error, line,
+ "Type mismatch! Object wasn't an integer.");
+ rv = -2;
+ goto out;
+ }
+ *va_arg(ap, int*) = json_integer_value(obj);
+ break;
+
+ case 'b':
+ if(!json_is_boolean(obj))
+ {
+ error_set(error, line,
+ "Type mismatch! Object wasn't a boolean.");
+ rv = -2;
+ goto out;
+ }
+ *va_arg(ap, int*) = json_is_true(obj);
+ break;
+
+ case 'f':
+ if(!json_is_number(obj))
+ {
+ error_set(error, line,
+ "Type mismatch! Object wasn't a real.");
+ rv = -2;
+ goto out;
+ }
+ *va_arg(ap, double*) = json_number_value(obj);
+ break;
+
+ case 'O':
+ json_incref(obj);
+ /* Fall through */
+
+ case 'o':
+ *va_arg(ap, json_t**) = obj;
+ break;
+
+ case 'n':
+ /* Don't actually assign anything; we're just happy
+ * the null turned up as promised in the format
+ * string. */
+ break;
+
+ default:
+ error_set(error, line,
+ "Unknown format character '%c'", *fmt);
+ rv = -1;
+ goto out;
+ }
+ }
+ fmt++;
+ }
+
+ /* Return 0 if everything was matched; otherwise the number of JSON
+ * objects we didn't get to. */
+ rv = unvisited;
+
+out:
+ va_end(ap);
+
+ if(stack)
+ free(stack);
+
+ return(rv);
+}
+
+/* vim: ts=4:expandtab:sw=4
+ */
suites/api/test_object
suites/api/test_simple
suites/api/test_cpp
+suites/api/test_pack
+suites/api/test_unpack
test_load \
test_simple \
test_number \
- test_object
+ test_object \
+ test_pack \
+ test_unpack
test_array_SOURCES = test_array.c util.h
test_copy_SOURCES = test_copy.c util.h
test_simple_SOURCES = test_simple.c util.h
test_number_SOURCES = test_number.c util.h
test_object_SOURCES = test_object.c util.h
+test_pack_SOURCES = test_pack.c util.h
+test_unpack_SOURCES = test_unpack.c util.h
AM_CPPFLAGS = -I$(top_srcdir)/src
AM_CFLAGS = -Wall -Werror
json_equal
json_copy
json_deep_copy
+json_pack
+json_unpack
EOF
# The list of functions are not exported in the library because they
--- /dev/null
+/*
+ * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2010 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
+ *
+ * Jansson is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ */
+
+#include <string.h>
+#include <jansson.h>
+#include <stdio.h>
+#include "util.h"
+
+int main()
+{
+ json_t *value;
+ int i;
+
+ /*
+ * Simple, valid json_pack cases
+ */
+
+ /* true */
+ value = json_pack(NULL, "b", 1);
+ if(!json_is_true(value))
+ fail("json_pack boolean failed");
+ if(value->refcount != (ssize_t)-1)
+ fail("json_pack boolean refcount failed");
+ json_decref(value);
+
+ /* false */
+ value = json_pack(NULL, "b", 0);
+ if(!json_is_false(value))
+ fail("json_pack boolean failed");
+ if(value->refcount != (ssize_t)-1)
+ fail("json_pack boolean refcount failed");
+ json_decref(value);
+
+ /* null */
+ value = json_pack(NULL, "n");
+ if(!json_is_null(value))
+ fail("json_pack null failed");
+ if(value->refcount != (ssize_t)-1)
+ fail("json_pack null refcount failed");
+ json_decref(value);
+
+ /* integer */
+ value = json_pack(NULL, "i", 1);
+ if(!json_is_integer(value) || json_integer_value(value) != 1)
+ fail("json_pack integer failed");
+ if(value->refcount != (ssize_t)1)
+ fail("json_pack integer refcount failed");
+ json_decref(value);
+
+
+ /* real */
+ value = json_pack(NULL, "f", 1.0);
+ if(!json_is_real(value) || json_real_value(value) != 1.0)
+ fail("json_pack real failed");
+ if(value->refcount != (ssize_t)1)
+ fail("json_pack real refcount failed");
+ json_decref(value);
+
+ /* string */
+ value = json_pack(NULL, "s", "test");
+ if(!json_is_string(value) || strcmp("test", json_string_value(value)))
+ fail("json_pack string failed");
+ if(value->refcount != (ssize_t)1)
+ fail("json_pack string refcount failed");
+ json_decref(value);
+
+ /* empty object */
+ value = json_pack(NULL, "{}", 1.0);
+ if(!json_is_object(value) || json_object_size(value) != 0)
+ fail("json_pack empty object failed");
+ if(value->refcount != (ssize_t)1)
+ fail("json_pack empty object refcount failed");
+ json_decref(value);
+
+ /* empty list */
+ value = json_pack(NULL, "[]", 1.0);
+ if(!json_is_array(value) || json_array_size(value) != 0)
+ fail("json_pack empty list failed");
+ if(value->refcount != (ssize_t)1)
+ fail("json_pack empty list failed");
+ json_decref(value);
+
+ /* non-incref'd object */
+ value = json_pack(NULL, "o", json_integer(1));
+ if(!json_is_integer(value) || json_integer_value(value) != 1)
+ fail("json_pack object failed");
+ if(value->refcount != (ssize_t)1)
+ fail("json_pack integer refcount failed");
+ json_decref(value);
+
+ /* incref'd object */
+ value = json_pack(NULL, "O", json_integer(1));
+ if(!json_is_integer(value) || json_integer_value(value) != 1)
+ fail("json_pack object failed");
+ if(value->refcount != (ssize_t)2)
+ fail("json_pack integer refcount failed");
+ json_decref(value);
+ json_decref(value);
+
+ /* simple object */
+ value = json_pack(NULL, "{s:[]}", "foo");
+ if(!json_is_object(value) || json_object_size(value) != 1)
+ fail("json_pack object failed");
+ if(!json_is_array(json_object_get(value, "foo")))
+ fail("json_pack object failed");
+ if(json_object_get(value, "foo")->refcount != (ssize_t)1)
+ fail("json_pack object refcount failed");
+ json_decref(value);
+
+ /* simple array */
+ value = json_pack(NULL, "[i,i,i]", 0, 1, 2);
+ if(!json_is_array(value) || json_array_size(value) != 3)
+ fail("json_pack object failed");
+ for(i=0; i<3; i++)
+ {
+ if(!json_is_integer(json_array_get(value, i)) ||
+ json_integer_value(json_array_get(value, i)) != i)
+
+ fail("json_pack integer array failed");
+ }
+ json_decref(value);
+
+ /*
+ * Invalid cases
+ */
+
+ /* mismatched open/close array/object */
+ if(json_pack(NULL, "[}"))
+ fail("json_pack failed to catch mismatched '}'");
+
+ if(json_pack(NULL, "{]"))
+ fail("json_pack failed to catch mismatched ']'");
+
+ /* missing close array */
+ if(json_pack(NULL, "["))
+ fail("json_pack failed to catch missing ']'");
+
+ /* missing close object */
+ if(json_pack(NULL, "{"))
+ fail("json_pack failed to catch missing '}'");
+
+ /* NULL string */
+ if(json_pack(NULL, "s", NULL))
+ fail("json_pack failed to catch null string");
+
+ return(0);
+}
+
+/* vim: ts=4:expandtab:sw=4
+ */
--- /dev/null
+/*
+ * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2010 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
+ *
+ * Jansson is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ */
+
+#include <string.h>
+#include <jansson.h>
+#include <stdio.h>
+#include "util.h"
+
+int main()
+{
+ json_t *j, *j2;
+ int i1, i2, i3;
+ int rv;
+ //void* v;
+ double f;
+ char *s;
+
+ /*
+ * Simple, valid json_pack cases
+ */
+
+ /* true */
+ rv = json_unpack(json_true(), NULL, "b", &i1);
+ if(rv || !i1)
+ fail("json_unpack boolean failed");
+
+ /* false */
+ rv = json_unpack(json_false(), NULL, "b", &i1);
+ if(rv || i1)
+ fail("json_unpack boolean failed");
+
+ /* null */
+ rv = json_unpack(json_null(), NULL, "n");
+ if(rv)
+ fail("json_unpack null failed");
+
+ /* integer */
+ j = json_integer(1);
+ rv = json_unpack(j, NULL, "i", &i1);
+ if(rv || i1 != 1)
+ fail("json_unpack integer failed");
+ json_decref(j);
+
+ /* real */
+ j = json_real(1.0);
+ rv = json_unpack(j, NULL, "f", &f);
+ if(rv || f != 1.0)
+ fail("json_unpack real failed");
+ json_decref(j);
+
+ /* string */
+ j = json_string("foo");
+ rv = json_unpack(j, NULL, "s", &s);
+ if(rv || strcmp(s, "foo"))
+ fail("json_unpack string failed");
+ json_decref(j);
+
+ /* empty object */
+ j = json_object();
+ rv = json_unpack(j, NULL, "{}");
+ if(rv)
+ fail("json_unpack empty object failed");
+ json_decref(j);
+
+ /* empty list */
+ j = json_array();
+ rv = json_unpack(j, NULL, "[]");
+ if(rv)
+ fail("json_unpack empty list failed");
+ json_decref(j);
+
+ /* non-incref'd object */
+ j = json_object();
+ rv = json_unpack(j, NULL, "o", &j2);
+ if(j2 != j || j->refcount != (ssize_t)1)
+ fail("json_unpack object failed");
+ json_decref(j);
+
+ /* incref'd object */
+ j = json_object();
+ rv = json_unpack(j, NULL, "O", &j2);
+ if(j2 != j || j->refcount != (ssize_t)2)
+ fail("json_unpack object failed");
+ json_decref(j);
+ json_decref(j);
+
+ /* simple object */
+ j = json_pack(NULL, "{s:i}", "foo", 1);
+ rv = json_unpack(j, NULL, "{s:i}", "foo", &i1);
+ if(rv || i1!=1)
+ fail("json_unpack simple object failed");
+ json_decref(j);
+
+ /* simple array */
+ j = json_pack(NULL, "[iii]", 1, 2, 3);
+ rv = json_unpack(j, NULL, "[i,i,i]", &i1, &i2, &i3);
+ if(rv || i1 != 1 || i2 != 2 || i3 != 3)
+ fail("json_unpack simple array failed");
+ json_decref(j);
+
+ return 0;
+}
+
+/* vim: ts=4:expandtab:sw=4
+ */