Skip to content

Commit

Permalink
Merge branch '1.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
akheron committed May 20, 2010
2 parents 782acfe + 978a47e commit dec3ad4
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 21 deletions.
28 changes: 25 additions & 3 deletions doc/apiref.rst
Expand Up @@ -154,9 +154,31 @@ Normally, all functions accepting a JSON value as an argument will
manage the reference, i.e. increase and decrease the reference count
as needed. However, some functions **steal** the reference, i.e. they
have the same result as if the user called :cfunc:`json_decref()` on
the argument right after calling the function. These are usually
convenience functions for adding new references to containers and not
to worry about the reference count.
the argument right after calling the function. These functions are
suffixed with ``_new`` or have ``_new_`` somewhere in their name.

For example, the following code creates a new JSON array and appends
an integer to it::

json_t *array, *integer;

array = json_array();
integer = json_integer(42);

json_array_append(array, integer);
json_decref(integer);

Note how the caller has to release the reference to the integer value
by calling :cfunc:`json_decref()`. By using a reference stealing
function :cfunc:`json_array_append_new()` instead of
:cfunc:`json_array_append()`, the code becomes much simpler::

json_t *array = json_array();
json_array_append_new(array, json_integer(42));

In this case, the user doesn't have to explicitly release the
reference to the integer value, as :cfunc:`json_array_append_new()`
steals the reference when appending the value to the array.

In the following sections it is clearly documented whether a function
will return a new or borrowed reference or steal a reference to its
Expand Down
48 changes: 30 additions & 18 deletions src/dump.c
Expand Up @@ -231,38 +231,44 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
/* detect circular references */
array = json_to_array(json);
if(array->visited)
return -1;
goto array_error;
array->visited = 1;

n = json_array_size(json);

if(dump("[", 1, data))
return -1;
if(n == 0)
goto array_error;
if(n == 0) {
array->visited = 0;
return dump("]", 1, data);
}
if(dump_indent(flags, depth + 1, 0, dump, data))
return -1;
goto array_error;

for(i = 0; i < n; ++i) {
if(do_dump(json_array_get(json, i), flags, depth + 1,
dump, data))
return -1;
goto array_error;

if(i < n - 1)
{
if(dump(",", 1, data) ||
dump_indent(flags, depth + 1, 1, dump, data))
return -1;
goto array_error;
}
else
{
if(dump_indent(flags, depth, 0, dump, data))
return -1;
goto array_error;
}
}

array->visited = 0;
return dump("]", 1, data);

array_error:
array->visited = 0;
return -1;
}

case JSON_OBJECT:
Expand All @@ -284,17 +290,19 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
/* detect circular references */
object = json_to_object(json);
if(object->visited)
return -1;
goto object_error;
object->visited = 1;

iter = json_object_iter((json_t *)json);

if(dump("{", 1, data))
return -1;
if(!iter)
goto object_error;
if(!iter) {
object->visited = 0;
return dump("}", 1, data);
}
if(dump_indent(flags, depth + 1, 0, dump, data))
return -1;
goto object_error;

if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER)
{
Expand All @@ -306,7 +314,7 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
size = json_object_size(json);
keys = malloc(size * sizeof(object_key_t *));
if(!keys)
return -1;
goto object_error;

i = 0;
while(iter)
Expand Down Expand Up @@ -338,7 +346,7 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
do_dump(value, flags, depth + 1, dump, data))
{
free(keys);
return -1;
goto object_error;
}

if(i < size - 1)
Expand All @@ -347,15 +355,15 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
dump_indent(flags, depth + 1, 1, dump, data))
{
free(keys);
return -1;
goto object_error;
}
}
else
{
if(dump_indent(flags, depth, 0, dump, data))
{
free(keys);
return -1;
goto object_error;
}
}
}
Expand All @@ -374,18 +382,18 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
if(dump(separator, separator_length, data) ||
do_dump(json_object_iter_value(iter), flags, depth + 1,
dump, data))
return -1;
goto object_error;

if(next)
{
if(dump(",", 1, data) ||
dump_indent(flags, depth + 1, 1, dump, data))
return -1;
goto object_error;
}
else
{
if(dump_indent(flags, depth, 0, dump, data))
return -1;
goto object_error;
}

iter = next;
Expand All @@ -394,6 +402,10 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,

object->visited = 0;
return dump("}", 1, data);

object_error:
object->visited = 0;
return -1;
}

default:
Expand Down
1 change: 1 addition & 0 deletions test/.gitignore
Expand Up @@ -3,6 +3,7 @@ bin/json_process
suites/api/test_array
suites/api/test_equal
suites/api/test_copy
suites/api/test_dump
suites/api/test_load
suites/api/test_number
suites/api/test_object
Expand Down
2 changes: 2 additions & 0 deletions test/suites/api/Makefile.am
Expand Up @@ -4,6 +4,7 @@ check_PROGRAMS = \
test_array \
test_equal \
test_copy \
test_dump \
test_load \
test_simple \
test_number \
Expand All @@ -12,6 +13,7 @@ check_PROGRAMS = \

test_array_SOURCES = test_array.c util.h
test_copy_SOURCES = test_copy.c util.h
test_dump_SOURCES = test_dump.c util.h
test_load_SOURCES = test_load.c util.h
test_simple_SOURCES = test_simple.c util.h
test_number_SOURCES = test_number.c util.h
Expand Down
89 changes: 89 additions & 0 deletions test/suites/api/test_dump.c
@@ -0,0 +1,89 @@
/*
* Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/

#include <jansson.h>
#include <string.h>
#include "util.h"

int main()
{
json_t *json;
char *result;

/* Encode an empty object/array, add an item, encode again */

json = json_object();
result = json_dumps(json, 0);
if(!result || strcmp(result, "{}"))
fail("json_dumps failed");

json_object_set_new(json, "foo", json_integer(5));
result = json_dumps(json, 0);
if(!result || strcmp(result, "{\"foo\": 5}"))
fail("json_dumps failed");

json_decref(json);

json = json_array();
result = json_dumps(json, 0);
if(!result || strcmp(result, "[]"))
fail("json_dumps failed");
free(result);

json_array_append_new(json, json_integer(5));
result = json_dumps(json, 0);
if(!result || strcmp(result, "[5]"))
fail("json_dumps failed");
free(result);

json_decref(json);

/* Construct a JSON object/array with a circular reference:
object: {"a": {"b": {"c": <circular reference to $.a>}}}
array: [[[<circular reference to the $[0] array>]]]
Encode it, remove the circular reference and encode again.
*/
json = json_object();
json_object_set_new(json, "a", json_object());
json_object_set_new(json_object_get(json, "a"), "b", json_object());
json_object_set(json_object_get(json_object_get(json, "a"), "b"), "c",
json_object_get(json, "a"));

if(json_dumps(json, 0))
fail("json_dumps encoded a circular reference!");

json_object_del(json_object_get(json_object_get(json, "a"), "b"), "c");

result = json_dumps(json, 0);
if(!result || strcmp(result, "{\"a\": {\"b\": {}}}"))
fail("json_dumps failed!");
free(result);

json_decref(json);

json = json_array();
json_array_append_new(json, json_array());
json_array_append_new(json_array_get(json, 0), json_array());
json_array_append(json_array_get(json_array_get(json, 0), 0),
json_array_get(json, 0));

if(json_dumps(json, 0))
fail("json_dumps encoded a circular reference!");

json_array_remove(json_array_get(json_array_get(json, 0), 0), 0);

result = json_dumps(json, 0);
if(!result || strcmp(result, "[[[]]]"))
fail("json_dumps failed!");
free(result);

json_decref(json);

return 0;
}

0 comments on commit dec3ad4

Please sign in to comment.