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:
parent
080325e566
commit
d5adf4e291
104
parson.c
104
parson.c
@ -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));
|
||||
}
|
||||
|
13
parson.h
13
parson.h
@ -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
97
tests.c
@ -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);
|
||||
|
@ -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
34
tests/test_2_comments.txt
Normal 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"
|
||||
}
|
||||
/**/
|
||||
//
|
Loading…
x
Reference in New Issue
Block a user