2 * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
3 * Copyright (c) 2010 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
5 * Jansson is free software; you can redistribute it and/or modify
6 * it under the terms of the MIT license. See LICENSE for details.
14 #include "jansson_private.h"
16 json_t *json_pack(json_error_t *error, const char *fmt, ...) {
17 int fmt_length = strlen(fmt);
20 /* Keep a stack of containers (lists and objects) */
22 json_t **stack = NULL;
24 /* Keep a list of objects we create in case of error */
26 json_t **free_list = NULL;
28 json_t *cur = NULL; /* Current container */
29 json_t *root = NULL; /* root object */
32 char *key = NULL; /* Current key in an object */
37 /* Allocation provisioned for worst case */
38 stack = calloc(fmt_length, sizeof(json_t *));
39 free_list = calloc(fmt_length, sizeof(json_t *));
41 jsonp_error_init(error, "");
43 if(!stack || !free_list)
53 case ' ': /* Whitespace */
56 case ',': /* Element spacer */
59 jsonp_error_set(error, line, -1,
60 "Unexpected COMMA precedes root element!");
67 jsonp_error_set(error, line, -1,
68 "Unexpected COMMA outside a list or object!");
75 jsonp_error_set(error, line, -1,
76 "Expected KEY, got COMMA!");
82 case ':': /* Key/value separator */
85 jsonp_error_set(error, line, -1,
86 "Got key/value separator without "
87 "a key preceding it!");
92 if(!json_is_object(cur))
94 jsonp_error_set(error, line, -1,
95 "Got a key/value separator "
96 "(':') outside an object!");
103 case ']': /* Close array or object */
108 jsonp_error_set(error, line, -1,
109 "OBJECT or ARRAY ended with an "
110 "incomplete key/value pair!");
117 jsonp_error_set(error, line, -1,
118 "Too many close-brackets '%c'", *fmt);
123 if(*fmt == ']' && !json_is_array(cur))
125 jsonp_error_set(error, line, -1,
126 "Stray close-array ']' character");
131 if(*fmt == '}' && !json_is_object(cur))
133 jsonp_error_set(error, line, -1,
134 "Stray close-object '}' character");
139 cur = stack[--depth];
150 case 's': /* string */
151 s = va_arg(ap, char*);
155 jsonp_error_set(error, line, -1,
156 "Refusing to handle a NULL string");
161 if(json_is_object(cur) && !key)
168 obj = json_string(s);
175 case 'b': /* boolean */
176 obj = va_arg(ap, int) ?
177 json_true() : json_false();
180 case 'i': /* integer */
181 obj = json_integer(va_arg(ap, int));
184 case 'f': /* double-precision float */
185 obj = json_real(va_arg(ap, double));
188 case 'O': /* a json_t object; increments refcount */
189 obj = va_arg(ap, json_t *);
193 case 'o': /* a json_t object; doesn't increment refcount */
194 obj = va_arg(ap, json_t *);
197 obj_common: free_list[free_count++] = obj;
199 /* Root this object to its parent */
200 if(json_is_object(cur)) {
203 jsonp_error_set(error, line, -1,
204 "Expected key, got identifier '%c'!", *fmt);
209 json_object_set_new(cur, key, obj);
212 else if(json_is_array(cur))
214 json_array_append_new(cur, obj);
223 jsonp_error_set(error, line, -1,
224 "Can't figure out where to attach "
225 "'%c' object!", *fmt);
230 /* If it was a container ('[' or '{'), descend on the stack */
231 if(json_is_array(obj) || json_is_object(obj))
233 stack[depth++] = cur;
244 jsonp_error_set(error, line, -1,
245 "Missing object or array close-brackets in format string");
250 /* Success: don't free everything we just built! */
255 json_decref(free_list[--free_count]);
266 int json_unpack(json_t *root, json_error_t *error, const char *fmt, ...) {
269 int rv=0; /* Return value */
270 int line = 1; /* Line number */
272 /* Keep a stack of containers (lists and objects) */
277 char *key = NULL; /* Current key in an object */
279 json_t *cur = NULL; /* Current container */
282 int fmt_length = strlen(fmt);
284 jsonp_error_init(error, "");
286 /* Allocation provisioned for worst case */
287 stack = calloc(fmt_length, sizeof(json_t *));
290 jsonp_error_set(error, line, -1, "Out of memory!");
295 /* Even if we're successful, we need to know if the number of
296 * arguments provided matches the number of JSON objects.
297 * We can do this by counting the elements in every array or
298 * object we open up, and decrementing the count as we visit
307 case ' ': /* Whitespace */
310 case '\n': /* Line break */
314 case ',': /* Element spacer */
318 jsonp_error_set(error, line, -1,
319 "Unexpected COMMA outside a list or object!");
326 jsonp_error_set(error, line, -1,
327 "Expected KEY, got COMMA!");
333 case ':': /* Key/value separator */
334 if(!json_is_object(cur) || !key)
336 jsonp_error_set(error, line, -1, "Unexpected ':'");
349 else if(json_is_object(cur))
353 jsonp_error_set(error, line, -1,
354 "Objects can't be keys");
358 obj = json_object_get(cur, key);
362 else if(json_is_array(cur))
364 obj = json_array_get(cur, array_index);
373 /* Make sure we got what we expected */
374 if(*fmt=='{' && !json_is_object(obj))
380 if(*fmt=='[' && !json_is_array(obj))
386 unvisited += json_is_object(obj) ?
387 json_object_size(obj) :
388 json_array_size(obj);
391 stack[depth++] = cur;
402 if(json_is_array(cur) && *fmt!=']')
404 jsonp_error_set(error, line, -1, "Missing ']'");
409 if(json_is_object(cur) && *fmt!='}')
411 jsonp_error_set(error, line, -1, "Missing '}'");
418 jsonp_error_set(error, line, -1, "Unexpected '%c'", *fmt);
425 jsonp_error_set(error, line, -1, "Unexpected '%c'", *fmt);
430 cur = stack[--depth];
435 if(!key && json_is_object(cur))
437 /* constant string for key */
438 key = va_arg(ap, char*);
443 case 'i': /* integer */
444 case 'f': /* double-precision float */
445 case 'O': /* a json_t object; increments refcount */
446 case 'o': /* a json_t object; borrowed reference */
447 case 'b': /* boolean */
455 else if(json_is_object(cur))
459 jsonp_error_set(error, line, -1,
460 "Only strings may be used as keys!");
465 obj = json_object_get(cur, key);
469 else if(json_is_array(cur))
471 obj = json_array_get(cur, array_index);
477 jsonp_error_set(error, line, -1,
478 "Unsure how to retrieve JSON object '%c'",
487 if(!json_is_string(obj))
489 jsonp_error_set(error, line, -1,
490 "Type mismatch! Object wasn't a string.");
494 *va_arg(ap, const char**) = json_string_value(obj);
498 if(!json_is_integer(obj))
500 jsonp_error_set(error, line, -1,
501 "Type mismatch! Object wasn't an integer.");
505 *va_arg(ap, int*) = json_integer_value(obj);
509 if(!json_is_boolean(obj))
511 jsonp_error_set(error, line, -1,
512 "Type mismatch! Object wasn't a boolean.");
516 *va_arg(ap, int*) = json_is_true(obj);
520 if(!json_is_number(obj))
522 jsonp_error_set(error, line, -1,
523 "Type mismatch! Object wasn't a real.");
527 *va_arg(ap, double*) = json_number_value(obj);
535 *va_arg(ap, json_t**) = obj;
539 /* Don't actually assign anything; we're just happy
540 * the null turned up as promised in the format
545 jsonp_error_set(error, line, -1,
546 "Unknown format character '%c'", *fmt);
554 /* Return 0 if everything was matched; otherwise the number of JSON
555 * objects we didn't get to. */
567 /* vim: ts=4:expandtab:sw=4