1
0
mirror of https://github.com/kgabis/parson.git synced 2025-01-28 06:32:55 +08:00

Added functions to parse file with comments and new tests.

json_parse_value_with_comments and json_parse_string_with_comments replace comments with whitespaces before parsing. Supported comments are: /* */ and //
This commit is contained in:
Krzysztof Gabis 2013-11-30 20:22:16 +01:00
parent 080325e566
commit d5adf4e291
5 changed files with 206 additions and 53 deletions

104
parson.c
View File

@ -1,6 +1,6 @@
/*
Parson ( http://kgabis.github.com/parson/ )
Copyright (c) 2012 Krzysztof Gabis
Copyright (c) 2013 Krzysztof Gabis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -72,6 +72,8 @@ struct json_array_t {
};
/* Various */
static char * read_file(const char *filename);
static void remove_comments(char *string, const char *start_token, const char *end_token);
static int try_realloc(void **ptr, size_t new_size);
static char * parson_strndup(const char *string, size_t n);
static int is_utf(const unsigned char *string);
@ -136,6 +138,51 @@ static int is_decimal(const char *string, size_t length) {
return 1;
}
static char * read_file(const char * filename) {
FILE *fp = fopen(filename, "r");
size_t file_size;
char *file_contents;
if (!fp) { return NULL; }
fseek(fp, 0L, SEEK_END);
file_size = ftell(fp);
rewind(fp);
file_contents = (char*)parson_malloc(sizeof(char) * (file_size + 1));
if (!file_contents) { fclose(fp); return NULL; }
if (fread(file_contents, file_size, 1, fp) < 1) {
if (ferror(fp)) { fclose(fp); return NULL; }
}
fclose(fp);
file_contents[file_size] = '\0';
return file_contents;
}
static void remove_comments(char *string, const char *start_token, const char *end_token) {
int in_string = 0, escaped = 0, i;
char *ptr = NULL, current_char;
size_t start_token_len = strlen(start_token);
size_t end_token_len = strlen(end_token);
if (start_token_len == 0 || end_token_len == 0)
return;
while ((current_char = *string) != '\0') {
if (current_char == '\\' && !escaped) {
escaped = 1;
string++;
continue;
} else if (current_char == '\"' && !escaped) {
in_string = !in_string;
} else if (!in_string && strncmp(string, start_token, start_token_len) == 0) {
for(i = 0; i < start_token_len; i++) string[i] = ' ';
string = string + start_token_len;
ptr = strstr(string, end_token);
if (!ptr) return;
for (i = 0; i < (ptr - string) + end_token_len; i++) string[i] = ' ';
string = ptr + end_token_len - 1;
}
escaped = 0;
string++;
}
}
/* JSON Object */
static JSON_Object * json_object_init(void) {
JSON_Object *new_obj = (JSON_Object*)parson_malloc(sizeof(JSON_Object));
@ -312,7 +359,8 @@ static const char * get_processed_string(const char **string) {
unprocessed_ptr++;
if (!is_utf((const unsigned char*)unprocessed_ptr) ||
sscanf(unprocessed_ptr, "%4x", &utf_val) == EOF) {
parson_free(output); return NULL;
parson_free(output);
return NULL;
}
if (utf_val < 0x80) {
current_char = utf_val;
@ -490,32 +538,56 @@ static JSON_Value * parse_null_value(const char **string) {
/* Parser API */
JSON_Value * json_parse_file(const char *filename) {
FILE *fp = fopen(filename, "r");
size_t file_size;
char *file_contents;
JSON_Value *output_value;
if (!fp) { return NULL; }
fseek(fp, 0L, SEEK_END);
file_size = ftell(fp);
rewind(fp);
file_contents = (char*)parson_malloc(sizeof(char) * (file_size + 1));
if (!file_contents) { fclose(fp); return NULL; }
if (fread(file_contents, file_size, 1, fp) < 1) {
if (ferror(fp)) { fclose(fp); return NULL; }
char *file_contents = read_file(filename);
JSON_Value *output_value = NULL;
if (!file_contents) {
return NULL;
}
fclose(fp);
file_contents[file_size] = '\0';
output_value = json_parse_string(file_contents);
parson_free(file_contents);
return output_value;
}
JSON_Value * json_parse_file_with_comments(const char *filename) {
char *file_contents = read_file(filename);
JSON_Value *output_value = NULL;
if (!file_contents) {
return NULL;
}
output_value = json_parse_string_with_comments(file_contents);
parson_free(file_contents);
return output_value;
}
JSON_Value * json_parse_string(const char *string) {
if (!string || (*string != '{' && *string != '[')) { return NULL; }
return parse_value((const char**)&string, 0);
}
JSON_Value * json_parse_string_with_comments(const char *string) {
JSON_Value *result = NULL;
char *string_mutable_copy = NULL, *string_mutable_copy_ptr = NULL;
string_mutable_copy = parson_strndup(string, strlen(string));
if (!string_mutable_copy) {
return NULL;
}
remove_comments(string_mutable_copy, "/*", "*/");
remove_comments(string_mutable_copy, "//", "\n");
puts(string_mutable_copy);
string_mutable_copy_ptr = string_mutable_copy;
skip_whitespaces(&string_mutable_copy_ptr);
if (*string_mutable_copy_ptr != '{' && *string_mutable_copy_ptr != '[') {
parson_free(string_mutable_copy);
return NULL;
}
result = parse_value((const char**)&string_mutable_copy_ptr, 0);
parson_free(string_mutable_copy);
return result;
}
/* JSON Object API */
JSON_Value * json_object_get_value(const JSON_Object *object, const char *name) {
return json_object_nget_value(object, name, strlen(name));
}

View File

@ -1,6 +1,6 @@
/*
Parson ( http://kgabis.github.com/parson/ )
Copyright (c) 2012 Krzysztof Gabis
Copyright (c) 2013 Krzysztof Gabis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -31,6 +31,8 @@ extern "C"
#include <stddef.h> /* size_t */
#define PARSON_VERSION 20131130
/* Types and enums */
typedef struct json_object_t JSON_Object;
typedef struct json_array_t JSON_Array;
@ -46,12 +48,21 @@ typedef enum json_value_type {
JSONBoolean = 6
} JSON_Value_Type;
/* Parses first JSON value in a file, returns NULL in case of error */
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);
/* Parses first JSON value in a string, returns NULL in case of error */
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 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);

97
tests.c
View File

@ -1,6 +1,6 @@
/*
Parson ( http://kgabis.github.com/parson/ )
Copyright (c) 2012 Krzysztof Gabis
Copyright (c) 2013 Krzysztof Gabis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -32,10 +32,14 @@
else{puts(" FAIL");tests_failed++;}
#define STREQ(A, B) (A && B ? strcmp(A, B) == 0 : 0)
void test_suite_1(void);
void test_suite_2(void);
void test_suite_2(JSON_Value *value);
void test_suite_2_no_comments(void);
void test_suite_2_with_commnets(void);
void test_suite_3(void);
char *read_file(const char *filename);
void print_commits_info(const char *username, const char *repo);
static int tests_passed;
@ -45,7 +49,8 @@ int main() {
/* Example function from readme file: */
/* print_commits_info("torvalds", "linux"); */
test_suite_1();
test_suite_2();
test_suite_2_no_comments();
test_suite_2_with_commnets();
test_suite_3();
printf("Tests failed: %d\n", tests_failed);
printf("Tests passed: %d\n", tests_passed);
@ -61,31 +66,35 @@ void test_suite_1(void) {
if (val) { json_value_free(val); }
TEST((val = json_parse_file("tests/test_1_3.txt")) != NULL);
if (val) { json_value_free(val); }
TEST((val = json_parse_file_with_comments("tests/test_1_1.txt")) != NULL);
if (val) { json_value_free(val); }
TEST((val = json_parse_file_with_comments("tests/test_1_2.txt")) != NULL);
if (val) { json_value_free(val); }
TEST((val = json_parse_file_with_comments("tests/test_1_3.txt")) != NULL);
if (val) { json_value_free(val); }
}
/* Testing correctness of parsed values */
void test_suite_2(void) {
JSON_Value *root_value;
JSON_Object *object;
void test_suite_2(JSON_Value *root_value) {
JSON_Object *root_object;
JSON_Array *array;
size_t i;
const char *filename = "tests/test_2.txt";
printf("Testing %s:\n", filename);
root_value = json_parse_file(filename);
TEST(root_value);
TEST(json_value_get_type(root_value) == JSONObject);
object = json_value_get_object(root_value);
TEST(STREQ(json_object_get_string(object, "string"), "lorem ipsum"));
TEST(STREQ(json_object_get_string(object, "utf string"), "lorem ipsum"));
TEST(STREQ(json_object_get_string(object, "utf-8 string"), "あいうえお"));
TEST(json_object_get_number(object, "positive one") == 1.0);
TEST(json_object_get_number(object, "negative one") == -1.0);
TEST(json_object_get_number(object, "hard to parse number") == -0.000314);
TEST(json_object_get_boolean(object, "boolean true") == 1);
TEST(json_object_get_boolean(object, "boolean false") == 0);
TEST(json_value_get_type(json_object_get_value(object, "null")) == JSONNull);
root_object = json_value_get_object(root_value);
TEST(STREQ(json_object_get_string(root_object, "string"), "lorem ipsum"));
TEST(STREQ(json_object_get_string(root_object, "utf string"), "lorem ipsum"));
TEST(STREQ(json_object_get_string(root_object, "utf-8 string"), "あいうえお"));
TEST(json_object_get_number(root_object, "positive one") == 1.0);
TEST(json_object_get_number(root_object, "negative one") == -1.0);
TEST(json_object_get_number(root_object, "hard to parse number") == -0.000314);
TEST(json_object_get_boolean(root_object, "boolean true") == 1);
TEST(json_object_get_boolean(root_object, "boolean false") == 0);
TEST(json_value_get_type(json_object_get_value(root_object, "null")) == JSONNull);
array = json_object_get_array(object, "string array");
array = json_object_get_array(root_object, "string array");
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"));
@ -93,7 +102,7 @@ void test_suite_2(void) {
tests_failed++;
}
array = json_object_get_array(object, "x^2 array");
array = json_object_get_array(root_object, "x^2 array");
if (array != NULL) {
for (i = 0; i < json_array_get_count(array); i++) {
TEST(json_array_get_number(array, i) == (i * i));
@ -102,26 +111,46 @@ void test_suite_2(void) {
tests_failed++;
}
TEST(json_object_get_array(object, "non existent array") == NULL);
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 false") == 0);
TEST(json_object_dotget_value(object, "object.nested null") != NULL);
TEST(json_object_dotget_number(object, "object.nested number") == 123);
TEST(json_object_get_array(root_object, "non existent array") == NULL);
TEST(STREQ(json_object_dotget_string(root_object, "object.nested string"), "str"));
TEST(json_object_dotget_boolean(root_object, "object.nested true") == 1);
TEST(json_object_dotget_boolean(root_object, "object.nested false") == 0);
TEST(json_object_dotget_value(root_object, "object.nested null") != NULL);
TEST(json_object_dotget_number(root_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, ".") == NULL);
TEST(json_object_dotget_value(object, "") == NULL);
TEST(json_object_dotget_value(root_object, "should.be.null") == NULL);
TEST(json_object_dotget_value(root_object, "should.be.null.") == NULL);
TEST(json_object_dotget_value(root_object, ".") == NULL);
TEST(json_object_dotget_value(root_object, "") == NULL);
array = json_object_dotget_array(object, "object.nested array");
array = json_object_dotget_array(root_object, "object.nested array");
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(object, "nested true"));
TEST(json_object_dotget_boolean(root_object, "nested true"));
TEST(STREQ(json_object_get_string(root_object, "/**/"), "comment"));
TEST(STREQ(json_object_get_string(root_object, "//"), "comment"));
}
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);
json_value_free(root_value);
}
void test_suite_2_with_commnets(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);
json_value_free(root_value);
}
@ -132,6 +161,8 @@ void test_suite_3(void) {
TEST(json_parse_string(NULL) == NULL);
TEST(json_parse_string("") == NULL); /* empty string */
TEST(json_parse_string("[\"lorem\",]") == NULL);
TEST(json_parse_string("{\"lorem\":\"ipsum\",}") == NULL);
TEST(json_parse_string("{lorem:ipsum}") == NULL);
TEST(json_parse_string("[,]") == NULL);
TEST(json_parse_string("[,") == NULL);
TEST(json_parse_string("[") == NULL);

View File

@ -1,7 +1,7 @@
{
"string" : "lorem ipsum",
"utf string" : "\u006corem\u0020ipsum",
"utf-8 string": "あいうえお",
"utf-8 string": "あいうえお",
"positive one" : 1,
"negative one" : -1,
"pi" : 3.14,
@ -12,10 +12,15 @@
"string array" : ["lorem", "ipsum"],
"x^2 array" : [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100],
"object_empty" : { },
"/*" : null,
"object" : { "nested string" : "str",
"nested true" : true,
"nested false" : false,
"nested null" : null,
"nested number" : 123,
"nested array" : ["lorem", "ipsum"] }
"nested array" : ["lorem", "ipsum"] },
"*/" : null,
"/**/" : "comment",
"//" : "comment",
"#" : "comment"
}

34
tests/test_2_comments.txt Normal file
View File

@ -0,0 +1,34 @@
/*
*Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
*ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
*dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
*/
// Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
{ /* lorem ipsum */
"string" : "lorem ipsum", // lorem ipsum
"utf string" : "\u006corem\u0020ipsum", // lorem ipsum //
"utf-8 string": "あいうえお", // /* lorem ipsum */
"positive one" : 1,
"negative one" : -1,
"pi" : 3.14,
"hard to parse number" : -3.14e-4,
"boolean true" : true,
"boolean false" : false,
"null" : null,
"string array" : ["lorem",/*in array*/"ipsum"],
"x^2 array" : [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100],
/* "x^2 array" : [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100],
*/ "object_empty" : { },
"/*" : null,
"object" : { "nested string" : "str",
"nested true" : /* lorem ipsum */ true,
"nested false" : false,
"nested null" : null, // lorem ipsum
"nested number" : 123,
"nested array" : ["lorem", "ipsum"] },
"*/" : null,
"/**/" : "comment",
"//" : "comment"
}
/**/
//