Merged changes from parson-devel (serialization, copying, comparing, validation, creating values).

pull/18/head
Krzysztof Gabis 10 years ago
parent 55856a2eab
commit 638190d6a0
  1. 76
      README.md
  2. 1076
      parson.c
  3. 131
      parson.h
  4. 154
      tests.c
  5. 0
      tests/test_1_1.txt
  6. 0
      tests/test_1_2.txt
  7. 0
      tests/test_1_3.txt
  8. 0
      tests/test_2.txt
  9. 0
      tests/test_2_comments.txt
  10. 14
      tests/test_5.txt

@ -1,6 +1,5 @@
##About
Parson is a lighweight [json](http://json.org) parser and reader written in C.
If you need serialization and json value building in code, use [parson-devel](https://github.com/kgabis/parson-devel), which has extended API, but may change in future.
Parson is a lighweight [json](http://json.org) library written in C.
##Features
* Full JSON support
@ -11,7 +10,7 @@ If you need serialization and json value building in code, use [parson-devel](ht
* Test suites
##Installation
Run the following code:
Run:
```
git clone https://github.com/kgabis/parson.git
```
@ -19,8 +18,9 @@ and copy parson.h and parson.c to you source code tree.
Run ```make test``` to compile and run tests.
##Example
Here is a function, which prints basic commit info (date, sha and author) from a github repository. It's also included in tests.c file, you can just uncomment and run it.
##Examples
###Parsing JSON
Here is a function, which prints basic commit info (date, sha and author) from a github repository.
```c
void print_commits_info(const char *username, const char *repo) {
JSON_Value *root_value;
@ -75,5 +75,71 @@ Date SHA Author
...
```
###Persistence
In this example I'm using parson to save user information to a file and then load it and validate later.
```c
void persistence_example(void) {
JSON_Value *schema = json_parse_string("{\"name\":\"\"}");
JSON_Value *user_data = json_parse_file("user_data.json");
char buf[256];
const char *name = NULL;
if (!user_data || !json_validate(schema, user_data) == JSONSuccess) {
puts("Enter your name:");
scanf("%s", buf);
user_data = json_value_init_object();
json_object_set_string(json_object(user_data), "name", buf);
json_serialize_to_file(user_data, "user_data.json");
}
name = json_object_get_string(json_object(user_data), "name");
printf("Hello, %s.", name);
json_value_free(schema);
json_value_free(user_data);
return;
}
```
###Serialization
Creating JSON values is very simple thanks to the dot notation.
Object hierarchy is automatically created when addressing specific fields.
In the following example I create a simple JSON value containing basic information about a person.
```c
void serialization_example(void) {
JSON_Value *root_value = json_value_init_object();
JSON_Object *root_object = json_value_get_object(root_value);
char *serialized_string = NULL;
json_object_set_string(root_object, "name", "John Smith");
json_object_set_number(root_object, "age", 25);
json_object_dotset_string(root_object, "address.city", "Cupertino");
json_object_dotset_value(root_object, "contact.emails", json_parse_string("[\"email@example.com\",\"email2@example.com\"]"));
serialized_string = json_serialize_to_string(root_value);
puts(serialized_string);
json_free_serialized_string(serialized_string);
}
```
Created value (after formatting outside parson):
```
{
"name":"John Smith",
"age":25,
"address":{
"city":"Cupertino"
},
"contact":{
"emails":[
"email@example.com",
"email2@example.com"
]
}
}
```
##Contributing
I will always merge *working* bug fixes. However, if you want to add something to the API,
I *won't* merge it without prior discussion.
Remember to follow parson's code style and write appropriate tests.
##License
[The MIT License (MIT)](http://opensource.org/licenses/mit-license.php)

1076
parson.c

File diff suppressed because it is too large Load Diff

@ -36,38 +36,68 @@ typedef struct json_object_t JSON_Object;
typedef struct json_array_t JSON_Array;
typedef struct json_value_t JSON_Value;
typedef enum json_value_type {
JSONError = 0,
enum json_value_type {
JSONError = -1,
JSONNull = 1,
JSONString = 2,
JSONNumber = 3,
JSONObject = 4,
JSONArray = 5,
JSONBoolean = 6
} JSON_Value_Type;
};
typedef int JSON_Value_Type;
enum json_result_t {
JSONSuccess = 0,
JSONFailure = -1
};
typedef int JSON_Status;
/* Parses first JSON value in a file, returns NULL in case of error */
JSON_Value * json_parse_file(const char *filename);
JSON_Value * json_parse_file(const char *filename);
/* Parses first JSON value in a file and ignores comments (/ * * / and //),
returns NULL in case of error */
JSON_Value * json_parse_file_with_comments(const char *filename);
JSON_Value * json_parse_file_with_comments(const char *filename);
/* Parses first JSON value in a string, returns NULL in case of error */
JSON_Value * json_parse_string(const char *string);
JSON_Value * json_parse_string(const char *string);
/* Parses first JSON value in a string and ignores comments (/ * * / and //),
returns NULL in case of error */
JSON_Value * json_parse_string_with_comments(const char *string);
JSON_Value * json_parse_string_with_comments(const char *string);
/* Serialization */
size_t json_serialization_size(const JSON_Value *value);
JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes);
JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename);
char * json_serialize_to_string(const JSON_Value *value);
void json_free_serialized_string(char *string); /* frees string from json_serialize_to_string */
/* Comparing */
int json_value_equals(const JSON_Value *a, const JSON_Value *b);
/* Validation
This is *NOT* JSON Schema. It validates json by checking if object have identically
named fields with matching types.
For example schema {"name":"", "age":0} will validate
{"name":"Joe", "age":25} and {"name":"Joe", "age":25, "gender":"m"},
but not {"name":"Joe"} or {"name":"Joe", "age":"Cucumber"}.
In case of arrays, only first value in schema is checked against all values in tested array.
Empty objects ({}) validate all objects, empty arrays ([]) validate all arrays,
null validates values of every type.
*/
JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value);
/* JSON Object */
/*
* JSON Object
*/
JSON_Value * json_object_get_value (const JSON_Object *object, const char *name);
const char * json_object_get_string (const JSON_Object *object, const char *name);
JSON_Object * json_object_get_object (const JSON_Object *object, const char *name);
JSON_Array * json_object_get_array (const JSON_Object *object, const char *name);
double json_object_get_number (const JSON_Object *object, const char *name);
int json_object_get_boolean(const JSON_Object *object, const char *name);
double json_object_get_number (const JSON_Object *object, const char *name); /* returns 0 on fail */
int json_object_get_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */
/* dotget functions enable addressing values with dot notation in nested objects,
just like in structs or c++/java/c# objects (e.g. objectA.objectB.value).
@ -77,30 +107,95 @@ JSON_Value * json_object_dotget_value (const JSON_Object *object, const char *
const char * json_object_dotget_string (const JSON_Object *object, const char *name);
JSON_Object * json_object_dotget_object (const JSON_Object *object, const char *name);
JSON_Array * json_object_dotget_array (const JSON_Object *object, const char *name);
double json_object_dotget_number (const JSON_Object *object, const char *name);
int json_object_dotget_boolean(const JSON_Object *object, const char *name);
double json_object_dotget_number (const JSON_Object *object, const char *name); /* returns 0 on fail */
int json_object_dotget_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */
/* Functions to get available names */
size_t json_object_get_count(const JSON_Object *object);
const char * json_object_get_name (const JSON_Object *object, size_t index);
/* JSON Array */
/* Creates new name-value pair or frees and replaces old value with new one. */
JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value);
JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string);
JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number);
JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean);
JSON_Status json_object_set_null(JSON_Object *object, const char *name);
/* Works like dotget functions, but creates whole hierarchy if necessary. */
JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value);
JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string);
JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number);
JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean);
JSON_Status json_object_dotset_null(JSON_Object *object, const char *name);
/* Frees and removes name-value pair */
JSON_Status json_object_remove(JSON_Object *object, const char *name);
/* Works like dotget function, but removes name-value pair only on exact match. */
JSON_Status json_object_dotremove(JSON_Object *object, const char *key);
/* Removes all name-value pairs in object */
JSON_Status json_object_clear(JSON_Object *object);
/*
*JSON Array
*/
JSON_Value * json_array_get_value (const JSON_Array *array, size_t index);
const char * json_array_get_string (const JSON_Array *array, size_t index);
JSON_Object * json_array_get_object (const JSON_Array *array, size_t index);
JSON_Array * json_array_get_array (const JSON_Array *array, size_t index);
double json_array_get_number (const JSON_Array *array, size_t index);
int json_array_get_boolean(const JSON_Array *array, size_t index);
double json_array_get_number (const JSON_Array *array, size_t index); /* returns 0 on fail */
int json_array_get_boolean(const JSON_Array *array, size_t index); /* returns -1 on fail */
size_t json_array_get_count (const JSON_Array *array);
/* Frees and removes value at given index, does nothing and returns JSONFailure if index doesn't exist.
* Order of values in array may change during execution. */
JSON_Status json_array_remove(JSON_Array *array, size_t i);
/* Frees and removes from array value at given index and replaces it with given one.
* Does nothing and returns JSONFailure if index doesn't exist. */
JSON_Status json_array_replace_value(JSON_Array *array, size_t i, JSON_Value *value);
JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string);
JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number);
JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean);
JSON_Status json_array_replace_null(JSON_Array *array, size_t i);
/* Frees and removes all values from array */
JSON_Status json_array_clear(JSON_Array *array);
/* Appends new value at the end of array. */
JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value);
JSON_Status json_array_append_string(JSON_Array *array, const char *string);
JSON_Status json_array_append_number(JSON_Array *array, double number);
JSON_Status json_array_append_boolean(JSON_Array *array, int boolean);
JSON_Status json_array_append_null(JSON_Array *array);
/*
*JSON Value
*/
JSON_Value * json_value_init_object (void);
JSON_Value * json_value_init_array (void);
JSON_Value * json_value_init_string (const char *string); /* copies passed string */
JSON_Value * json_value_init_number (double number);
JSON_Value * json_value_init_boolean(int boolean);
JSON_Value * json_value_init_null (void);
JSON_Value * json_value_deep_copy (const JSON_Value *value);
void json_value_free (JSON_Value *value);
/* JSON Value */
JSON_Value_Type json_value_get_type (const JSON_Value *value);
JSON_Object * json_value_get_object (const JSON_Value *value);
JSON_Array * json_value_get_array (const JSON_Value *value);
const char * json_value_get_string (const JSON_Value *value);
double json_value_get_number (const JSON_Value *value);
int json_value_get_boolean(const JSON_Value *value);
void json_value_free (JSON_Value *value);
/* Same as above, but shorter */
JSON_Value_Type json_type (const JSON_Value *value);
JSON_Object * json_object (const JSON_Value *value);
JSON_Array * json_array (const JSON_Value *value);
const char * json_string (const JSON_Value *value);
double json_number (const JSON_Value *value);
int json_boolean(const JSON_Value *value);
#ifdef __cplusplus
}

@ -20,6 +20,9 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "parson.h"
@ -30,53 +33,68 @@
#define TEST(A) printf("%-72s-",#A); \
if(A){puts(" OK");tests_passed++;} \
else{puts(" FAIL");tests_failed++;}
#define STREQ(A, B) (A && B ? strcmp(A, B) == 0 : 0)
#define STREQ(A, B) ((A) && (B) ? strcmp((A), (B)) == 0 : 0)
void test_suite_1(void);
void test_suite_2(JSON_Value *value);
void test_suite_1(void); /* Test 3 files from json.org + serialization*/
void test_suite_2(JSON_Value *value); /* Test correctness of parsed values */
void test_suite_2_no_comments(void);
void test_suite_2_with_comments(void);
void test_suite_3(void);
void test_suite_3(void); /* Test incorrect values */
void test_suite_4(void); /* Test deep copy funtion */
void test_suite_5(void); /* Test building json values from scratch */
void test_suite_6(void); /* Test value comparing verification */
void test_suite_7(void); /* Test schema validation */
void test_suite_8(void); /* Test serialization to file */
char *read_file(const char *filename);
void print_commits_info(const char *username, const char *repo);
void persistence_example(void);
void serialization_example(void);
static int tests_passed;
static int tests_failed;
int main() {
/* Example function from readme file: */
/* Example functions from readme file: */
/* print_commits_info("torvalds", "linux"); */
/* serialization_example(); */
/* persistence_example(); */
test_suite_1();
test_suite_2_no_comments();
test_suite_2_with_comments();
test_suite_3();
test_suite_4();
test_suite_5();
test_suite_6();
test_suite_7();
test_suite_8();
printf("Tests failed: %d\n", tests_failed);
printf("Tests passed: %d\n", tests_passed);
return 0;
}
/* 3 test files from json.org */
void test_suite_1(void) {
JSON_Value *val;
TEST((val = json_parse_file("tests/test_1_1.txt")) != NULL);
TEST(json_value_equals(json_parse_string(json_serialize_to_string(val)), val));
if (val) { json_value_free(val); }
TEST((val = json_parse_file("tests/test_1_2.txt")) != NULL);
TEST(json_value_equals(json_parse_string(json_serialize_to_string(val)), val));
if (val) { json_value_free(val); }
TEST((val = json_parse_file("tests/test_1_3.txt")) != NULL);
TEST(json_value_equals(json_parse_string(json_serialize_to_string(val)), val));
if (val) { json_value_free(val); }
TEST((val = json_parse_file_with_comments("tests/test_1_1.txt")) != NULL);
TEST(json_value_equals(json_parse_string(json_serialize_to_string(val)), val));
if (val) { json_value_free(val); }
TEST((val = json_parse_file_with_comments("tests/test_1_2.txt")) != NULL);
TEST(json_value_equals(json_parse_string(json_serialize_to_string(val)), val));
if (val) { json_value_free(val); }
TEST((val = json_parse_file_with_comments("tests/test_1_3.txt")) != NULL);
TEST(json_value_equals(json_parse_string(json_serialize_to_string(val)), val));
if (val) { json_value_free(val); }
}
/* Testing correctness of parsed values */
void test_suite_2(JSON_Value *root_value) {
JSON_Object *root_object;
JSON_Array *array;
@ -125,11 +143,11 @@ void test_suite_2(JSON_Value *root_value) {
TEST(json_object_dotget_value(root_object, "") == NULL);
array = json_object_dotget_array(root_object, "object.nested array");
TEST(array != NULL);
TEST(json_array_get_count(array) > 1);
if (array != NULL && json_array_get_count(array) > 1) {
TEST(STREQ(json_array_get_string(array, 0), "lorem"));
TEST(STREQ(json_array_get_string(array, 1), "ipsum"));
} else {
tests_failed++;
}
TEST(json_object_dotget_boolean(root_object, "nested true"));
@ -142,22 +160,21 @@ void test_suite_2(JSON_Value *root_value) {
void test_suite_2_no_comments(void) {
const char *filename = "tests/test_2.txt";
JSON_Value *root_value = NULL;
printf("Testing %s:\n", filename);
root_value = json_parse_file(filename);
test_suite_2(root_value);
TEST(json_value_equals(root_value, json_parse_string(json_serialize_to_string(root_value))));
json_value_free(root_value);
}
void test_suite_2_with_comments(void) {
const char *filename = "tests/test_2_comments.txt";
JSON_Value *root_value = NULL;
printf("Testing %s:\n", filename);
root_value = json_parse_file_with_comments(filename);
test_suite_2(root_value);
TEST(json_value_equals(root_value, json_parse_string(json_serialize_to_string(root_value))));
json_value_free(root_value);
}
/* Testing values, on which parsing should fail */
void test_suite_3(void) {
char nested_20x[] = "[[[[[[[[[[[[[[[[[[[[\"hi\"]]]]]]]]]]]]]]]]]]]]";
puts("Testing invalid strings:");
@ -205,6 +222,80 @@ void test_suite_3(void) {
TEST(json_parse_string("[\"\\uDF67\\uD834\"]") == NULL); /* wrong order surrogate pair */
}
void test_suite_4() {
const char *filename = "tests/test_2.txt";
JSON_Value *a = NULL, *a_copy = NULL;
printf("Testing %s:\n", filename);
a = json_parse_file(filename);
TEST(json_value_equals(a, a)); /* test equality test */
a_copy = json_value_deep_copy(a);
TEST(a_copy != NULL);
TEST(json_value_equals(a, a_copy));
}
void test_suite_5(void) {
JSON_Value *val_from_file = json_parse_file("tests/test_5.txt");
JSON_Value *val = json_value_init_object();
JSON_Object *obj = json_value_get_object(val);
TEST(json_object_set_string(obj, "first", "John") == JSONSuccess);
TEST(json_object_set_string(obj, "last", "Doe") == JSONSuccess);
TEST(json_object_set_number(obj, "age", 25) == JSONSuccess);
TEST(json_object_set_boolean(obj, "registered", 1) == JSONSuccess);
TEST(json_object_set_value(obj, "interests", json_value_init_array()) == JSONSuccess);
TEST(json_array_append_string(json_object_get_array(obj, "interests"), "Writing") == JSONSuccess);
TEST(json_array_append_string(json_object_get_array(obj, "interests"), "Mountain Biking") == JSONSuccess);
TEST(json_array_replace_string(json_object_get_array(obj, "interests"), 0, "Reading") == JSONSuccess);
TEST(json_object_dotset_string(obj, "favorites.color", "blue") == JSONSuccess);
TEST(json_object_dotset_string(obj, "favorites.sport", "running") == JSONSuccess);
TEST(json_object_dotset_string(obj, "favorites.fruit", "apple") == JSONSuccess);
TEST(json_object_dotremove(obj, "favorites.fruit") == JSONSuccess);
TEST(json_object_set_string(obj, "utf string", "\\u006corem\\u0020ipsum") == JSONSuccess);
TEST(json_object_set_string(obj, "utf-8 string", "あいうえお") == JSONSuccess);
TEST(json_object_set_string(obj, "surrogate string", "lorem\\uD834\\uDD1Eipsum\\uD834\\uDF67lorem") == JSONSuccess);
TEST(json_value_equals(val_from_file, val));
}
void test_suite_6(void) {
const char *filename = "tests/test_2.txt";
JSON_Value *a = NULL;
JSON_Value *b = NULL;
a = json_parse_file(filename);
b = json_parse_file(filename);
TEST(json_value_equals(a, b));
json_object_set_string(json_object(a), "string", "eki");
TEST(!json_value_equals(a, b));
a = json_value_deep_copy(b);
TEST(json_value_equals(a, b));
json_array_append_number(json_object_get_array(json_object(b), "string array"), 1337);
TEST(!json_value_equals(a, b));
}
void test_suite_7(void) {
JSON_Value *val_from_file = json_parse_file("tests/test_5.txt");
JSON_Value *schema = json_value_init_object();
JSON_Object *schema_obj = json_value_get_object(schema);
json_object_set_string(schema_obj, "first", "");
json_object_set_string(schema_obj, "last", "");
json_object_set_number(schema_obj, "age", 0);
json_object_set_null(schema_obj, "favorites");
TEST(json_validate(schema, val_from_file) == JSONSuccess);
json_object_set_string(schema_obj, "age", "");
TEST(json_validate(schema, val_from_file) == JSONFailure);
}
void test_suite_8(void) {
const char *filename = "tests/test_2.txt";
const char *temp_filename = "tests/test_2_serialized.txt";
JSON_Value *a = NULL;
JSON_Value *b = NULL;
a = json_parse_file(filename);
TEST(json_serialize_to_file(a, temp_filename) == JSONSuccess);
b = json_parse_file(temp_filename);
TEST(json_value_equals(a, b));
remove(temp_filename);
}
void print_commits_info(const char *username, const char *repo) {
JSON_Value *root_value;
JSON_Array *commits;
@ -244,3 +335,36 @@ void print_commits_info(const char *username, const char *repo) {
json_value_free(root_value);
system(cleanup_command);
}
void persistence_example(void) {
JSON_Value *schema = json_parse_string("{\"name\":\"\"}");
JSON_Value *user_data = json_parse_file("user_data.json");
char buf[256];
const char *name = NULL;
if (!user_data || json_validate(schema, user_data) == JSONSuccess) {
puts("Enter your name:");
scanf("%s", buf);
user_data = json_value_init_object();
json_object_set_string(json_object(user_data), "name", buf);
json_serialize_to_file(user_data, "user_data.json");
}
name = json_object_get_string(json_object(user_data), "name");
printf("Hello, %s.", name);
json_value_free(schema);
json_value_free(user_data);
return;
}
void serialization_example(void) {
JSON_Value *root_value = json_value_init_object();
JSON_Object *root_object = json_value_get_object(root_value);
char *serialized_string = NULL;
json_object_set_string(root_object, "name", "John Smith");
json_object_set_number(root_object, "age", 25);
json_object_dotset_string(root_object, "address.city", "Cupertino");
json_object_dotset_value(root_object, "contact.emails",
json_parse_string("[\"email@example.com\", \"email2@example.com\"]"));
serialized_string = json_serialize_to_string(root_value);
puts(serialized_string);
json_free_serialized_string(serialized_string);
}

@ -0,0 +1,14 @@
{
"first": "John",
"last": "Doe",
"age": 25,
"registered": true,
"interests": [ "Reading", "Mountain Biking" ],
"favorites": {
"color": "blue",
"sport": "running"
},
"utf string" : "\u006corem\u0020ipsum",
"utf-8 string": "あいうえお",
"surrogate string": "lorem\uD834\uDD1Eipsum\uD834\uDF67lorem"
}
Loading…
Cancel
Save