Basic JSON serializer

pull/2/head
Harry Roberts 12 years ago
parent 457fa1100f
commit 23572fa680
  1. 3
      .gitignore
  2. 11
      Makefile
  3. 145
      parson.c
  4. 23
      parson.h
  5. 43
      tests.c

3
.gitignore vendored

@ -1 +1,2 @@
.DS_Store .DS_Store
test

@ -0,0 +1,11 @@
CFLAGS = -O0 -ggdb -Wall -Wextra -std=c89
all: test
.PHONY: test
test: tests.c parson.c
$(CC) $(CFLAGS) -o $@ tests.c parson.c
./$@
clean:
rm -f test *.o

@ -1,17 +1,17 @@
/* /*
Parson ( http://kgabis.github.com/parson/ ) Parson ( http://kgabis.github.com/parson/ )
Copyright (c) 2012 Krzysztof Gabis Copyright (c) 2012 Krzysztof Gabis
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software. all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -27,6 +27,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <assert.h>
#define ERROR 0 #define ERROR 0
#define SUCCESS 1 #define SUCCESS 1
@ -647,3 +648,141 @@ void json_value_free(JSON_Value *value) {
} }
parson_free(value); parson_free(value);
} }
/* -------------------------------------------------------------------------- */
struct _serialize_str {
char *str;
size_t len;
};
/*
* Callback which appends data to the string
*/
void _serialize_str_cb( const char *str, size_t len, void *data ) {
struct _serialize_str *self = (struct _serialize_str*)data;
if( self->str == NULL ) {
self->str = parson_malloc(len + 1);
assert( self->str != NULL );
strncpy( self->str, str, len );
self->str[ len ] = 0;
self->len = len;
}
else {
self->str = parson_realloc( self->str, self->len + len + 1 );
assert( self->str != NULL );
strncpy( self->str + (self->len), str, len);
self->str[ self->len + len ] = 0;
self->len += len;
}
}
static void
json_serialize_string( const char *str, json_print_cb cb, void *arg ) {
cb("\"", 1, arg);
/* TODO: handle unicode encoding of string?
* XXX: need to be aware of unicode escape sequences so that we don't mess up
*/
cb(str, strlen(str), arg);
cb("\"", 1, arg);
}
static void
json_serialize_number( const JSON_Value *value, json_print_cb cb, void *arg ) {
char buf[64];
size_t len = sprintf(buf, "%.16g", json_value_get_number(value));
cb(buf, len, arg);
}
static void
json_serialize_object( const JSON_Value *value, json_print_cb cb, void *arg ) {
int first = 1;
size_t idx;
JSON_Object* obj = json_value_get_object(value);
const char *name = NULL;
JSON_Value *obj_value = json_object_get_value(obj, name);
cb("{", 1, arg);
for( idx = 0; idx < json_object_get_count(obj); idx++ ) {
name = json_object_get_name(obj, idx);
if( first ) {
first = 0;
}
else {
cb(",", 1, arg);
}
json_serialize_string( name, cb, arg );
cb(":", 1, arg);
json_serialize_cb(obj_value, cb, arg);
}
cb("}", 1, arg);
}
static void
json_serialize_array( const JSON_Value *value, json_print_cb cb, void *arg ) {
int first = 1;
size_t idx;
JSON_Array *array = json_value_get_array(value);
cb("[", 1, arg);
for( idx = 0; idx < json_array_get_count(array); idx++ ) {
if( first ) {
first = 0;
}
else {
cb(",", 1, arg);
}
json_serialize_cb( json_array_get_value(array, idx), cb, arg );
}
cb("]", 1, arg);
}
void
json_serialize_cb( const JSON_Value *value, json_print_cb cb, void *arg ) {
assert( json_value_get_type(value) != JSONError );
switch( json_value_get_type(value) ) {
case JSONNull:
cb( "null", 4, arg );
break;
case JSONString:
json_serialize_string( json_value_get_string(value), cb, arg );
break;
case JSONNumber:
json_serialize_number(value, cb, arg);
break;
case JSONObject:
json_serialize_object(value, cb, arg);
break;
case JSONArray:
json_serialize_array(value, cb, arg);
break;
case JSONBoolean:
if( json_value_get_boolean(value) ) {
cb("true", 4, arg);
}
else {
cb("false", 5, arg);
}
break;
default:
printf("Got unknown type!\n");
/* XXX: this shouldn't ever happen! */
break;
}
}
char *
json_serialize( const JSON_Value *value ) {
struct _serialize_str str;
str.str = NULL;
str.len = 0;
json_serialize_cb( value, &_serialize_str_cb, &str );
return str.str;
}

@ -1,17 +1,17 @@
/* /*
Parson ( http://kgabis.github.com/parson/ ) Parson ( http://kgabis.github.com/parson/ )
Copyright (c) 2012 Krzysztof Gabis Copyright (c) 2012 Krzysztof Gabis
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software. all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -27,10 +27,10 @@
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
{ {
#endif #endif
#include <stddef.h> /* size_t */ #include <stddef.h> /* size_t */
/* Types and enums */ /* Types and enums */
typedef struct json_object_t JSON_Object; typedef struct json_object_t JSON_Object;
typedef struct json_array_t JSON_Array; typedef struct json_array_t JSON_Array;
@ -74,7 +74,7 @@ int json_object_dotget_boolean(const JSON_Object *object, const char *
/* Functions to get available names */ /* Functions to get available names */
size_t json_object_get_count(const JSON_Object *object); size_t json_object_get_count(const JSON_Object *object);
const char * json_object_get_name (const JSON_Object *object, size_t index); const char * json_object_get_name (const JSON_Object *object, size_t index);
/* JSON Array */ /* JSON Array */
JSON_Value * json_array_get_value (const JSON_Array *array, size_t index); 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); const char * json_array_get_string (const JSON_Array *array, size_t index);
@ -92,7 +92,12 @@ const char * json_value_get_string (const JSON_Value *value);
double json_value_get_number (const JSON_Value *value); double json_value_get_number (const JSON_Value *value);
int json_value_get_boolean(const JSON_Value *value); int json_value_get_boolean(const JSON_Value *value);
void json_value_free (JSON_Value *value); void json_value_free (JSON_Value *value);
/* Functions for serializing JSON */
typedef void (*json_print_cb)(const char *str, size_t len, void *data);
void json_serialize_cb( const JSON_Value *value, json_print_cb cb, void *arg );
char *json_serialize( const JSON_Value *value );
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

@ -1,17 +1,17 @@
/* /*
Parson ( http://kgabis.github.com/parson/ ) Parson ( http://kgabis.github.com/parson/ )
Copyright (c) 2012 Krzysztof Gabis Copyright (c) 2012 Krzysztof Gabis
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software. all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -31,10 +31,12 @@
if(A){puts(" OK");tests_passed++;} \ if(A){puts(" OK");tests_passed++;} \
else{puts(" FAIL");tests_failed++;} 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)
#define RESERIALIZE(A) (strcmp(json_serialize(json_parse_string(A)), A) == 0)
void test_suite_1(void); void test_suite_1(void);
void test_suite_2(void); void test_suite_2(void);
void test_suite_3(void); void test_suite_3(void);
void test_suite_4(void);
void print_commits_info(const char *username, const char *repo); void print_commits_info(const char *username, const char *repo);
@ -47,6 +49,7 @@ int main(int argc, const char * argv[]) {
test_suite_1(); test_suite_1();
test_suite_2(); test_suite_2();
test_suite_3(); test_suite_3();
test_suite_4();
printf("Tests failed: %d\n", tests_failed); printf("Tests failed: %d\n", tests_failed);
printf("Tests passed: %d\n", tests_passed); printf("Tests passed: %d\n", tests_passed);
return 0; return 0;
@ -68,7 +71,7 @@ void test_suite_2(void) {
JSON_Value *root_value; JSON_Value *root_value;
JSON_Object *object; JSON_Object *object;
JSON_Array *array; JSON_Array *array;
int i; size_t i;
const char *filename = "tests/test_2.txt"; const char *filename = "tests/test_2.txt";
printf("Testing %s:\n", filename); printf("Testing %s:\n", filename);
root_value = json_parse_file(filename); root_value = json_parse_file(filename);
@ -83,7 +86,7 @@ void test_suite_2(void) {
TEST(json_object_get_boolean(object, "boolean true") == 1); TEST(json_object_get_boolean(object, "boolean true") == 1);
TEST(json_object_get_boolean(object, "boolean false") == 0); TEST(json_object_get_boolean(object, "boolean false") == 0);
TEST(json_value_get_type(json_object_get_value(object, "null")) == JSONNull); TEST(json_value_get_type(json_object_get_value(object, "null")) == JSONNull);
array = json_object_get_array(object, "string array"); array = json_object_get_array(object, "string array");
if (array != NULL && 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, 0), "lorem"));
@ -91,7 +94,7 @@ void test_suite_2(void) {
} else { } else {
tests_failed++; tests_failed++;
} }
array = json_object_get_array(object, "x^2 array"); array = json_object_get_array(object, "x^2 array");
if (array != NULL) { if (array != NULL) {
for (i = 0; i < json_array_get_count(array); i++) { for (i = 0; i < json_array_get_count(array); i++) {
@ -100,19 +103,19 @@ void test_suite_2(void) {
} else { } else {
tests_failed++; tests_failed++;
} }
TEST(json_object_get_array(object, "non existent array") == NULL); TEST(json_object_get_array(object, "non existent array") == NULL);
TEST(STREQ(json_object_dotget_string(object, "object.nested string"), "str")); TEST(STREQ(json_object_dotget_string(object, "object.nested string"), "str"));
TEST(json_object_dotget_boolean(object, "object.nested true") == 1); TEST(json_object_dotget_boolean(object, "object.nested true") == 1);
TEST(json_object_dotget_boolean(object, "object.nested false") == 0); TEST(json_object_dotget_boolean(object, "object.nested false") == 0);
TEST(json_object_dotget_value(object, "object.nested null") != NULL); TEST(json_object_dotget_value(object, "object.nested null") != NULL);
TEST(json_object_dotget_number(object, "object.nested number") == 123); TEST(json_object_dotget_number(object, "object.nested number") == 123);
TEST(json_object_dotget_value(object, "should.be.null") == NULL); TEST(json_object_dotget_value(object, "should.be.null") == NULL);
TEST(json_object_dotget_value(object, "should.be.null.") == NULL); TEST(json_object_dotget_value(object, "should.be.null.") == NULL);
TEST(json_object_dotget_value(object, ".") == NULL); TEST(json_object_dotget_value(object, ".") == NULL);
TEST(json_object_dotget_value(object, "") == NULL); TEST(json_object_dotget_value(object, "") == NULL);
array = json_object_dotget_array(object, "object.nested array"); array = json_object_dotget_array(object, "object.nested array");
if (array != NULL && 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, 0), "lorem"));
@ -120,7 +123,7 @@ void test_suite_2(void) {
} else { } else {
tests_failed++; tests_failed++;
} }
TEST(json_object_dotget_boolean(object, "nested true")); TEST(json_object_dotget_boolean(object, "nested true"));
json_value_free(root_value); json_value_free(root_value);
} }
@ -169,30 +172,36 @@ void test_suite_3(void) {
TEST(json_parse_string("[-07.0]") == NULL); TEST(json_parse_string("[-07.0]") == NULL);
} }
/* Test serialization */
void test_suite_4( void ) {
TEST(RESERIALIZE("[1]"));
TEST(RESERIALIZE("[\"hello\"]"));
}
void print_commits_info(const char *username, const char *repo) { void print_commits_info(const char *username, const char *repo) {
JSON_Value *root_value; JSON_Value *root_value;
JSON_Array *commits; JSON_Array *commits;
JSON_Object *commit; JSON_Object *commit;
int i; size_t i;
char curl_command[512]; char curl_command[512];
char cleanup_command[256]; char cleanup_command[256];
char output_filename[] = "commits.json"; char output_filename[] = "commits.json";
/* it ain't pretty, but it's not a libcurl tutorial */ /* it ain't pretty, but it's not a libcurl tutorial */
sprintf(curl_command, sprintf(curl_command,
"curl -s \"https://api.github.com/repos/%s/%s/commits\" > %s", "curl -s \"https://api.github.com/repos/%s/%s/commits\" > %s",
username, repo, output_filename); username, repo, output_filename);
sprintf(cleanup_command, "rm -f %s", output_filename); sprintf(cleanup_command, "rm -f %s", output_filename);
system(curl_command); system(curl_command);
/* parsing json and validating output */ /* parsing json and validating output */
root_value = json_parse_file(output_filename); root_value = json_parse_file(output_filename);
if (json_value_get_type(root_value) != JSONArray) { if (json_value_get_type(root_value) != JSONArray) {
system(cleanup_command); system(cleanup_command);
return; return;
} }
/* getting array from root value and printing commit info */ /* getting array from root value and printing commit info */
commits = json_value_get_array(root_value); commits = json_value_get_array(root_value);
printf("%-10.10s %-10.10s %s\n", "Date", "SHA", "Author"); printf("%-10.10s %-10.10s %s\n", "Date", "SHA", "Author");
@ -203,7 +212,7 @@ void print_commits_info(const char *username, const char *repo) {
json_object_get_string(commit, "sha"), json_object_get_string(commit, "sha"),
json_object_dotget_string(commit, "commit.author.name")); json_object_dotget_string(commit, "commit.author.name"));
} }
/* cleanup code */ /* cleanup code */
json_value_free(root_value); json_value_free(root_value);
system(cleanup_command); system(cleanup_command);

Loading…
Cancel
Save