* Add escaping of backslash to json output
* Add escaping of foward slash on tokenizing and output
* Changes to internal tokenizer from using recursion to
using a depth state structure to allow incremental parsing
git-svn-id: http://svn.metaparadigm.com/svn/json-c/trunk@14 327403b1-1117-474d-bef2-5cb71233fd97
This commit is contained in:
13
COPYING
13
COPYING
@@ -10,9 +10,10 @@ Software is furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
0.7
|
||||
* Add escaping of backslash to json output
|
||||
* Add escaping of foward slash on tokenizing and output
|
||||
* Changes to internal tokenizer from using recursion to
|
||||
using a depth state structure to allow incremental parsing
|
||||
|
||||
0.6
|
||||
* Fix bug in escaping of control characters
|
||||
Johan Bj<42>rklund, johbjo09 at kth dot se
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
CFLAGS = -Wall -std=gnu99 -D_GNU_SOURCE -D_REENTRANT
|
||||
AM_CFLAGS = -Wall -std=gnu99 -D_GNU_SOURCE -D_REENTRANT
|
||||
|
||||
lib_LTLIBRARIES = libjson.la
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: json_object.c,v 1.15 2006/01/30 23:07:57 mclark Exp $
|
||||
* $Id: json_object.c,v 1.17 2006/07/25 03:24:50 mclark Exp $
|
||||
*
|
||||
* Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd.
|
||||
* Michael Clark <michael@metaparadigm.com>
|
||||
@@ -93,6 +93,8 @@ static int json_escape_str(struct printbuf *pb, char *str)
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '"':
|
||||
case '\\':
|
||||
case '/':
|
||||
if(pos - start_offset > 0)
|
||||
printbuf_memappend(pb, str + start_offset, pos - start_offset);
|
||||
if(c == '\b') printbuf_memappend(pb, "\\b", 2);
|
||||
@@ -100,6 +102,8 @@ static int json_escape_str(struct printbuf *pb, char *str)
|
||||
else if(c == '\r') printbuf_memappend(pb, "\\r", 2);
|
||||
else if(c == '\t') printbuf_memappend(pb, "\\t", 2);
|
||||
else if(c == '"') printbuf_memappend(pb, "\\\"", 2);
|
||||
else if(c == '\\') printbuf_memappend(pb, "\\\\", 2);
|
||||
else if(c == '/') printbuf_memappend(pb, "\\/", 2);
|
||||
start_offset = ++pos;
|
||||
break;
|
||||
default:
|
||||
|
||||
375
json_tokener.c
375
json_tokener.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: json_tokener.c,v 1.19 2006/01/30 23:07:57 mclark Exp $
|
||||
* $Id: json_tokener.c,v 1.20 2006/07/25 03:24:50 mclark Exp $
|
||||
*
|
||||
* Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd.
|
||||
* Michael Clark <michael@metaparadigm.com>
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "json_object.h"
|
||||
#include "json_tokener.h"
|
||||
|
||||
|
||||
#if !HAVE_STRNCASECMP && defined(_MSC_VER)
|
||||
/* MSC has the version as _strnicmp */
|
||||
# define strncasecmp _strnicmp
|
||||
@@ -31,21 +32,76 @@
|
||||
#endif /* HAVE_STRNCASECMP */
|
||||
|
||||
|
||||
static struct json_object* json_tokener_do_parse(struct json_tokener *this);
|
||||
static const char* json_null_str = "null";
|
||||
static const char* json_true_str = "true";
|
||||
static const char* json_false_str = "false";
|
||||
|
||||
struct json_object* json_tokener_parse(char * s)
|
||||
const char* json_tokener_errors[] = {
|
||||
"success",
|
||||
"continue",
|
||||
"nesting to deep",
|
||||
"unexpected end of data",
|
||||
"unexpected character",
|
||||
"null expected",
|
||||
"boolean expected",
|
||||
"number expected",
|
||||
"array value separator ',' expected",
|
||||
"quoted object property name expected",
|
||||
"object property name separator ':' expected",
|
||||
"object value separator ',' expected",
|
||||
"invalid string sequence",
|
||||
"expected comment",
|
||||
};
|
||||
|
||||
|
||||
struct json_tokener* json_tokener_new()
|
||||
{
|
||||
struct json_tokener tok;
|
||||
struct json_tokener *tok = calloc(1, sizeof(struct json_tokener));
|
||||
tok->pb = printbuf_new();
|
||||
json_tokener_reset(tok);
|
||||
return tok;
|
||||
}
|
||||
|
||||
void json_tokener_free(struct json_tokener *tok)
|
||||
{
|
||||
json_tokener_reset(tok);
|
||||
if(tok) printbuf_free(tok->pb);
|
||||
free(tok);
|
||||
}
|
||||
|
||||
static void json_tokener_reset_level(struct json_tokener *tok, int depth)
|
||||
{
|
||||
tok->stack[depth].state = json_tokener_state_eatws;
|
||||
tok->stack[depth].saved_state = json_tokener_state_start;
|
||||
json_object_put(tok->stack[depth].current);
|
||||
tok->stack[depth].current = NULL;
|
||||
free(tok->stack[depth].obj_field_name);
|
||||
tok->stack[depth].obj_field_name = NULL;
|
||||
}
|
||||
|
||||
void json_tokener_reset(struct json_tokener *tok)
|
||||
{
|
||||
int i;
|
||||
for(i = tok->depth; i >= 0; i--)
|
||||
json_tokener_reset_level(tok, i);
|
||||
tok->depth = 0;
|
||||
tok->err = json_tokener_success;
|
||||
}
|
||||
|
||||
struct json_object* json_tokener_parse(char *str)
|
||||
{
|
||||
struct json_tokener* tok;
|
||||
struct json_object* obj;
|
||||
|
||||
tok.source = s;
|
||||
tok.pos = 0;
|
||||
tok.pb = printbuf_new();
|
||||
obj = json_tokener_do_parse(&tok);
|
||||
printbuf_free(tok.pb);
|
||||
tok = json_tokener_new();
|
||||
obj = json_tokener_parse_ex(tok, str, -1);
|
||||
if(tok->err != json_tokener_success)
|
||||
obj = error_ptr(-tok->err);
|
||||
json_tokener_free(tok);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
#if !HAVE_STRNDUP
|
||||
/* CAW: compliant version of strndup() */
|
||||
char* strndup(const char* str, size_t n)
|
||||
@@ -67,31 +123,45 @@ char* strndup(const char* str, size_t n)
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct json_object* json_tokener_do_parse(struct json_tokener *this)
|
||||
|
||||
#define state tok->stack[tok->depth].state
|
||||
#define saved_state tok->stack[tok->depth].saved_state
|
||||
#define current tok->stack[tok->depth].current
|
||||
#define obj_field_name tok->stack[tok->depth].obj_field_name
|
||||
|
||||
struct json_object* json_tokener_parse_ex(struct json_tokener *tok,
|
||||
char *str, int len)
|
||||
{
|
||||
enum json_tokener_state state, saved_state;
|
||||
enum json_tokener_error err = json_tokener_success;
|
||||
struct json_object *current = NULL, *obj;
|
||||
char *obj_field_name = NULL;
|
||||
char quote_char;
|
||||
int deemed_double, start_offset;
|
||||
struct json_object *obj = NULL;
|
||||
char c;
|
||||
|
||||
state = json_tokener_state_eatws;
|
||||
saved_state = json_tokener_state_start;
|
||||
tok->char_offset = 0;
|
||||
tok->err = json_tokener_success;
|
||||
|
||||
do {
|
||||
c = this->source[this->pos];
|
||||
if(tok->char_offset == len) {
|
||||
if(tok->depth == 0 && state == json_tokener_state_eatws &&
|
||||
saved_state == json_tokener_state_finish)
|
||||
tok->err = json_tokener_success;
|
||||
else
|
||||
tok->err = json_tokener_continue;
|
||||
goto out;
|
||||
}
|
||||
|
||||
c = *str;
|
||||
redo_char:
|
||||
switch(state) {
|
||||
|
||||
case json_tokener_state_eatws:
|
||||
if(isspace(c)) {
|
||||
this->pos++;
|
||||
/* okay */
|
||||
} else if(c == '/') {
|
||||
printbuf_reset(tok->pb);
|
||||
printbuf_memappend(tok->pb, &c, 1);
|
||||
state = json_tokener_state_comment_start;
|
||||
start_offset = this->pos++;
|
||||
} else {
|
||||
state = saved_state;
|
||||
goto redo_char;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -99,35 +169,34 @@ static struct json_object* json_tokener_do_parse(struct json_tokener *this)
|
||||
switch(c) {
|
||||
case '{':
|
||||
state = json_tokener_state_eatws;
|
||||
saved_state = json_tokener_state_object;
|
||||
saved_state = json_tokener_state_object_field_start;
|
||||
current = json_object_new_object();
|
||||
this->pos++;
|
||||
break;
|
||||
case '[':
|
||||
state = json_tokener_state_eatws;
|
||||
saved_state = json_tokener_state_array;
|
||||
current = json_object_new_array();
|
||||
this->pos++;
|
||||
break;
|
||||
case 'N':
|
||||
case 'n':
|
||||
state = json_tokener_state_null;
|
||||
start_offset = this->pos++;
|
||||
break;
|
||||
printbuf_reset(tok->pb);
|
||||
tok->st_pos = 0;
|
||||
goto redo_char;
|
||||
case '"':
|
||||
case '\'':
|
||||
quote_char = c;
|
||||
printbuf_reset(this->pb);
|
||||
state = json_tokener_state_string;
|
||||
start_offset = ++this->pos;
|
||||
printbuf_reset(tok->pb);
|
||||
tok->quote_char = c;
|
||||
break;
|
||||
case 'T':
|
||||
case 't':
|
||||
case 'F':
|
||||
case 'f':
|
||||
state = json_tokener_state_boolean;
|
||||
start_offset = this->pos++;
|
||||
break;
|
||||
printbuf_reset(tok->pb);
|
||||
tok->st_pos = 0;
|
||||
goto redo_char;
|
||||
#if defined(__GNUC__)
|
||||
case '0' ... '9':
|
||||
#else
|
||||
@@ -143,30 +212,38 @@ static struct json_object* json_tokener_do_parse(struct json_tokener *this)
|
||||
case '9':
|
||||
#endif
|
||||
case '-':
|
||||
deemed_double = 0;
|
||||
state = json_tokener_state_number;
|
||||
start_offset = this->pos++;
|
||||
break;
|
||||
printbuf_reset(tok->pb);
|
||||
tok->is_double = 0;
|
||||
goto redo_char;
|
||||
default:
|
||||
err = json_tokener_error_parse_unexpected;
|
||||
tok->err = json_tokener_error_parse_unexpected;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case json_tokener_state_finish:
|
||||
goto out;
|
||||
if(tok->depth == 0) goto out;
|
||||
obj = json_object_get(current);
|
||||
json_tokener_reset_level(tok, tok->depth);
|
||||
tok->depth--;
|
||||
goto redo_char;
|
||||
|
||||
case json_tokener_state_null:
|
||||
if(strncasecmp("null", this->source + start_offset,
|
||||
this->pos - start_offset))
|
||||
return error_ptr(-json_tokener_error_parse_null);
|
||||
if(this->pos - start_offset == 4) {
|
||||
printbuf_memappend(tok->pb, &c, 1);
|
||||
if(strncasecmp(json_null_str, tok->pb->buf,
|
||||
min(tok->st_pos+1, strlen(json_null_str))) == 0) {
|
||||
if(tok->st_pos == strlen(json_null_str)) {
|
||||
current = NULL;
|
||||
saved_state = json_tokener_state_finish;
|
||||
state = json_tokener_state_eatws;
|
||||
} else {
|
||||
this->pos++;
|
||||
goto redo_char;
|
||||
}
|
||||
} else {
|
||||
tok->err = json_tokener_error_parse_null;
|
||||
goto out;
|
||||
}
|
||||
tok->st_pos++;
|
||||
break;
|
||||
|
||||
case json_tokener_state_comment_start:
|
||||
@@ -175,291 +252,265 @@ static struct json_object* json_tokener_do_parse(struct json_tokener *this)
|
||||
} else if(c == '/') {
|
||||
state = json_tokener_state_comment_eol;
|
||||
} else {
|
||||
err = json_tokener_error_parse_comment;
|
||||
tok->err = json_tokener_error_parse_comment;
|
||||
goto out;
|
||||
}
|
||||
this->pos++;
|
||||
printbuf_memappend(tok->pb, &c, 1);
|
||||
break;
|
||||
|
||||
case json_tokener_state_comment:
|
||||
if(c == '*') state = json_tokener_state_comment_end;
|
||||
this->pos++;
|
||||
printbuf_memappend(tok->pb, &c, 1);
|
||||
break;
|
||||
|
||||
case json_tokener_state_comment_eol:
|
||||
if(c == '\n') {
|
||||
if(mc_get_debug()) {
|
||||
char *tmp = strndup(this->source + start_offset,
|
||||
this->pos - start_offset);
|
||||
mc_debug("json_tokener_comment: %s\n", tmp);
|
||||
free(tmp);
|
||||
}
|
||||
mc_debug("json_tokener_comment: %s\n", tok->pb->buf);
|
||||
state = json_tokener_state_eatws;
|
||||
} else {
|
||||
printbuf_memappend(tok->pb, &c, 1);
|
||||
}
|
||||
this->pos++;
|
||||
break;
|
||||
|
||||
case json_tokener_state_comment_end:
|
||||
printbuf_memappend(tok->pb, &c, 1);
|
||||
if(c == '/') {
|
||||
if(mc_get_debug()) {
|
||||
char *tmp = strndup(this->source + start_offset,
|
||||
this->pos - start_offset + 1);
|
||||
mc_debug("json_tokener_comment: %s\n", tmp);
|
||||
free(tmp);
|
||||
}
|
||||
mc_debug("json_tokener_comment: %s\n", tok->pb->buf);
|
||||
state = json_tokener_state_eatws;
|
||||
} else {
|
||||
state = json_tokener_state_comment;
|
||||
}
|
||||
this->pos++;
|
||||
break;
|
||||
|
||||
case json_tokener_state_string:
|
||||
if(c == quote_char) {
|
||||
printbuf_memappend(this->pb, this->source + start_offset,
|
||||
this->pos - start_offset);
|
||||
current = json_object_new_string(this->pb->buf);
|
||||
if(c == tok->quote_char) {
|
||||
current = json_object_new_string(tok->pb->buf);
|
||||
saved_state = json_tokener_state_finish;
|
||||
state = json_tokener_state_eatws;
|
||||
} else if(c == '\\') {
|
||||
saved_state = json_tokener_state_string;
|
||||
state = json_tokener_state_string_escape;
|
||||
} else {
|
||||
printbuf_memappend(tok->pb, &c, 1);
|
||||
}
|
||||
this->pos++;
|
||||
break;
|
||||
|
||||
case json_tokener_state_string_escape:
|
||||
switch(c) {
|
||||
case '"':
|
||||
case '\\':
|
||||
printbuf_memappend(this->pb, this->source + start_offset,
|
||||
this->pos - start_offset - 1);
|
||||
start_offset = this->pos++;
|
||||
case '/':
|
||||
printbuf_memappend(tok->pb, &c, 1);
|
||||
state = saved_state;
|
||||
break;
|
||||
case 'b':
|
||||
case 'n':
|
||||
case 'r':
|
||||
case 't':
|
||||
printbuf_memappend(this->pb, this->source + start_offset,
|
||||
this->pos - start_offset - 1);
|
||||
if(c == 'b') printbuf_memappend(this->pb, "\b", 1);
|
||||
else if(c == 'n') printbuf_memappend(this->pb, "\n", 1);
|
||||
else if(c == 'r') printbuf_memappend(this->pb, "\r", 1);
|
||||
else if(c == 't') printbuf_memappend(this->pb, "\t", 1);
|
||||
start_offset = ++this->pos;
|
||||
if(c == 'b') printbuf_memappend(tok->pb, "\b", 1);
|
||||
else if(c == 'n') printbuf_memappend(tok->pb, "\n", 1);
|
||||
else if(c == 'r') printbuf_memappend(tok->pb, "\r", 1);
|
||||
else if(c == 't') printbuf_memappend(tok->pb, "\t", 1);
|
||||
state = saved_state;
|
||||
break;
|
||||
case 'u':
|
||||
printbuf_memappend(this->pb, this->source + start_offset,
|
||||
this->pos - start_offset - 1);
|
||||
start_offset = ++this->pos;
|
||||
tok->ucs_char = 0;
|
||||
tok->st_pos = 0;
|
||||
state = json_tokener_state_escape_unicode;
|
||||
break;
|
||||
default:
|
||||
err = json_tokener_error_parse_string;
|
||||
tok->err = json_tokener_error_parse_string;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case json_tokener_state_escape_unicode:
|
||||
if(strchr(json_hex_chars, c)) {
|
||||
this->pos++;
|
||||
if(this->pos - start_offset == 4) {
|
||||
tok->ucs_char += ((unsigned int)hexdigit(c) << ((3-tok->st_pos++)*4));
|
||||
if(tok->st_pos == 4) {
|
||||
unsigned char utf_out[3];
|
||||
unsigned int ucs_char =
|
||||
(hexdigit(*(this->source + start_offset)) << 12) +
|
||||
(hexdigit(*(this->source + start_offset + 1)) << 8) +
|
||||
(hexdigit(*(this->source + start_offset + 2)) << 4) +
|
||||
hexdigit(*(this->source + start_offset + 3));
|
||||
if (ucs_char < 0x80) {
|
||||
utf_out[0] = ucs_char;
|
||||
printbuf_memappend(this->pb, (char*)utf_out, 1);
|
||||
} else if (ucs_char < 0x800) {
|
||||
utf_out[0] = 0xc0 | (ucs_char >> 6);
|
||||
utf_out[1] = 0x80 | (ucs_char & 0x3f);
|
||||
printbuf_memappend(this->pb, (char*)utf_out, 2);
|
||||
if (tok->ucs_char < 0x80) {
|
||||
utf_out[0] = tok->ucs_char;
|
||||
printbuf_memappend(tok->pb, (char*)utf_out, 1);
|
||||
} else if (tok->ucs_char < 0x800) {
|
||||
utf_out[0] = 0xc0 | (tok->ucs_char >> 6);
|
||||
utf_out[1] = 0x80 | (tok->ucs_char & 0x3f);
|
||||
printbuf_memappend(tok->pb, (char*)utf_out, 2);
|
||||
} else {
|
||||
utf_out[0] = 0xe0 | (ucs_char >> 12);
|
||||
utf_out[1] = 0x80 | ((ucs_char >> 6) & 0x3f);
|
||||
utf_out[2] = 0x80 | (ucs_char & 0x3f);
|
||||
printbuf_memappend(this->pb, (char*)utf_out, 3);
|
||||
utf_out[0] = 0xe0 | (tok->ucs_char >> 12);
|
||||
utf_out[1] = 0x80 | ((tok->ucs_char >> 6) & 0x3f);
|
||||
utf_out[2] = 0x80 | (tok->ucs_char & 0x3f);
|
||||
printbuf_memappend(tok->pb, (char*)utf_out, 3);
|
||||
}
|
||||
start_offset = this->pos;
|
||||
state = saved_state;
|
||||
}
|
||||
} else {
|
||||
err = json_tokener_error_parse_string;
|
||||
tok->err = json_tokener_error_parse_string;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case json_tokener_state_boolean:
|
||||
if(strncasecmp("true", this->source + start_offset,
|
||||
this->pos - start_offset) == 0) {
|
||||
if(this->pos - start_offset == 4) {
|
||||
printbuf_memappend(tok->pb, &c, 1);
|
||||
if(strncasecmp(json_true_str, tok->pb->buf,
|
||||
min(tok->st_pos+1, strlen(json_true_str))) == 0) {
|
||||
if(tok->st_pos == strlen(json_true_str)) {
|
||||
current = json_object_new_boolean(1);
|
||||
saved_state = json_tokener_state_finish;
|
||||
state = json_tokener_state_eatws;
|
||||
} else {
|
||||
this->pos++;
|
||||
goto redo_char;
|
||||
}
|
||||
} else if(strncasecmp("false", this->source + start_offset,
|
||||
this->pos - start_offset) == 0) {
|
||||
if(this->pos - start_offset == 5) {
|
||||
} else if(strncasecmp(json_false_str, tok->pb->buf,
|
||||
min(tok->st_pos+1, strlen(json_false_str))) == 0) {
|
||||
if(tok->st_pos == strlen(json_false_str)) {
|
||||
current = json_object_new_boolean(0);
|
||||
saved_state = json_tokener_state_finish;
|
||||
state = json_tokener_state_eatws;
|
||||
} else {
|
||||
this->pos++;
|
||||
goto redo_char;
|
||||
}
|
||||
} else {
|
||||
err = json_tokener_error_parse_boolean;
|
||||
tok->err = json_tokener_error_parse_boolean;
|
||||
goto out;
|
||||
}
|
||||
tok->st_pos++;
|
||||
break;
|
||||
|
||||
case json_tokener_state_number:
|
||||
if(!c || !strchr(json_number_chars, c)) {
|
||||
if(c && strchr(json_number_chars, c)) {
|
||||
printbuf_memappend(tok->pb, &c, 1);
|
||||
if(c == '.' || c == 'e') tok->is_double = 1;
|
||||
} else {
|
||||
int numi;
|
||||
double numd;
|
||||
char *tmp = strndup(this->source + start_offset,
|
||||
this->pos - start_offset);
|
||||
if(!deemed_double && sscanf(tmp, "%d", &numi) == 1) {
|
||||
if(!tok->is_double && sscanf(tok->pb->buf, "%d", &numi) == 1) {
|
||||
current = json_object_new_int(numi);
|
||||
} else if(deemed_double && sscanf(tmp, "%lf", &numd) == 1) {
|
||||
} else if(tok->is_double && sscanf(tok->pb->buf, "%lf", &numd) == 1) {
|
||||
current = json_object_new_double(numd);
|
||||
} else {
|
||||
free(tmp);
|
||||
err = json_tokener_error_parse_number;
|
||||
tok->err = json_tokener_error_parse_number;
|
||||
goto out;
|
||||
}
|
||||
free(tmp);
|
||||
saved_state = json_tokener_state_finish;
|
||||
state = json_tokener_state_eatws;
|
||||
} else {
|
||||
if(c == '.' || c == 'e') deemed_double = 1;
|
||||
this->pos++;
|
||||
goto redo_char;
|
||||
}
|
||||
break;
|
||||
|
||||
case json_tokener_state_array:
|
||||
if(c == ']') {
|
||||
this->pos++;
|
||||
saved_state = json_tokener_state_finish;
|
||||
state = json_tokener_state_eatws;
|
||||
} else {
|
||||
obj = json_tokener_do_parse(this);
|
||||
if(is_error(obj)) {
|
||||
err = -(enum json_tokener_error)obj;
|
||||
if(tok->depth >= JSON_TOKENER_MAX_DEPTH-1) {
|
||||
tok->err = json_tokener_error_depth;
|
||||
goto out;
|
||||
}
|
||||
state = json_tokener_state_array_add;
|
||||
tok->depth++;
|
||||
json_tokener_reset_level(tok, tok->depth);
|
||||
goto redo_char;
|
||||
}
|
||||
break;
|
||||
|
||||
case json_tokener_state_array_add:
|
||||
json_object_array_add(current, obj);
|
||||
saved_state = json_tokener_state_array_sep;
|
||||
state = json_tokener_state_eatws;
|
||||
}
|
||||
break;
|
||||
goto redo_char;
|
||||
|
||||
case json_tokener_state_array_sep:
|
||||
if(c == ']') {
|
||||
this->pos++;
|
||||
saved_state = json_tokener_state_finish;
|
||||
state = json_tokener_state_eatws;
|
||||
} else if(c == ',') {
|
||||
this->pos++;
|
||||
saved_state = json_tokener_state_array;
|
||||
state = json_tokener_state_eatws;
|
||||
} else {
|
||||
json_object_put(current);
|
||||
return error_ptr(-json_tokener_error_parse_array);
|
||||
tok->err = json_tokener_error_parse_array;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case json_tokener_state_object:
|
||||
state = json_tokener_state_object_field_start;
|
||||
start_offset = this->pos;
|
||||
break;
|
||||
|
||||
case json_tokener_state_object_field_start:
|
||||
if(c == '}') {
|
||||
this->pos++;
|
||||
saved_state = json_tokener_state_finish;
|
||||
state = json_tokener_state_eatws;
|
||||
} else if (c == '"' || c == '\'') {
|
||||
quote_char = c;
|
||||
printbuf_reset(this->pb);
|
||||
tok->quote_char = c;
|
||||
printbuf_reset(tok->pb);
|
||||
state = json_tokener_state_object_field;
|
||||
start_offset = ++this->pos;
|
||||
} else {
|
||||
err = json_tokener_error_parse_object;
|
||||
tok->err = json_tokener_error_parse_object_key_name;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case json_tokener_state_object_field:
|
||||
if(c == quote_char) {
|
||||
printbuf_memappend(this->pb, this->source + start_offset,
|
||||
this->pos - start_offset);
|
||||
obj_field_name = strdup(this->pb->buf);
|
||||
if(c == tok->quote_char) {
|
||||
obj_field_name = strdup(tok->pb->buf);
|
||||
saved_state = json_tokener_state_object_field_end;
|
||||
state = json_tokener_state_eatws;
|
||||
} else if(c == '\\') {
|
||||
saved_state = json_tokener_state_object_field;
|
||||
state = json_tokener_state_string_escape;
|
||||
} else {
|
||||
printbuf_memappend(tok->pb, &c, 1);
|
||||
}
|
||||
this->pos++;
|
||||
break;
|
||||
|
||||
case json_tokener_state_object_field_end:
|
||||
if(c == ':') {
|
||||
this->pos++;
|
||||
saved_state = json_tokener_state_object_value;
|
||||
state = json_tokener_state_eatws;
|
||||
} else {
|
||||
return error_ptr(-json_tokener_error_parse_object);
|
||||
tok->err = json_tokener_error_parse_object_key_sep;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case json_tokener_state_object_value:
|
||||
obj = json_tokener_do_parse(this);
|
||||
if(is_error(obj)) {
|
||||
err = -(enum json_tokener_error)obj;
|
||||
if(tok->depth >= JSON_TOKENER_MAX_DEPTH-1) {
|
||||
tok->err = json_tokener_error_depth;
|
||||
goto out;
|
||||
}
|
||||
state = json_tokener_state_object_value_add;
|
||||
tok->depth++;
|
||||
json_tokener_reset_level(tok, tok->depth);
|
||||
goto redo_char;
|
||||
|
||||
case json_tokener_state_object_value_add:
|
||||
json_object_object_add(current, obj_field_name, obj);
|
||||
free(obj_field_name);
|
||||
obj_field_name = NULL;
|
||||
saved_state = json_tokener_state_object_sep;
|
||||
state = json_tokener_state_eatws;
|
||||
break;
|
||||
goto redo_char;
|
||||
|
||||
case json_tokener_state_object_sep:
|
||||
if(c == '}') {
|
||||
this->pos++;
|
||||
saved_state = json_tokener_state_finish;
|
||||
state = json_tokener_state_eatws;
|
||||
} else if(c == ',') {
|
||||
this->pos++;
|
||||
saved_state = json_tokener_state_object;
|
||||
saved_state = json_tokener_state_object_field_start;
|
||||
state = json_tokener_state_eatws;
|
||||
} else {
|
||||
err = json_tokener_error_parse_object;
|
||||
tok->err = json_tokener_error_parse_object_value_sep;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
str++;
|
||||
tok->char_offset++;
|
||||
} while(c);
|
||||
|
||||
if(state != json_tokener_state_finish &&
|
||||
saved_state != json_tokener_state_finish)
|
||||
err = json_tokener_error_parse_eof;
|
||||
tok->err = json_tokener_error_parse_eof;
|
||||
|
||||
out:
|
||||
free(obj_field_name);
|
||||
if(err == json_tokener_success) return current;
|
||||
mc_debug("json_tokener_do_parse: error=%d state=%d char=%c\n",
|
||||
err, state, c);
|
||||
json_object_put(current);
|
||||
return error_ptr(-err);
|
||||
if(tok->err == json_tokener_success) return json_object_get(current);
|
||||
mc_debug("json_tokener_parse_ex: error %s at offset %d\n",
|
||||
json_tokener_errors[tok->err], tok->char_offset);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: json_tokener.h,v 1.9 2006/01/30 23:07:57 mclark Exp $
|
||||
* $Id: json_tokener.h,v 1.10 2006/07/25 03:24:50 mclark Exp $
|
||||
*
|
||||
* Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd.
|
||||
* Michael Clark <michael@metaparadigm.com>
|
||||
@@ -16,15 +16,19 @@
|
||||
|
||||
enum json_tokener_error {
|
||||
json_tokener_success,
|
||||
json_tokener_continue,
|
||||
json_tokener_error_depth,
|
||||
json_tokener_error_parse_eof,
|
||||
json_tokener_error_parse_unexpected,
|
||||
json_tokener_error_parse_null,
|
||||
json_tokener_error_parse_boolean,
|
||||
json_tokener_error_parse_number,
|
||||
json_tokener_error_parse_array,
|
||||
json_tokener_error_parse_object,
|
||||
json_tokener_error_parse_object_key_name,
|
||||
json_tokener_error_parse_object_key_sep,
|
||||
json_tokener_error_parse_object_value_sep,
|
||||
json_tokener_error_parse_string,
|
||||
json_tokener_error_parse_comment,
|
||||
json_tokener_error_parse_eof
|
||||
json_tokener_error_parse_comment
|
||||
};
|
||||
|
||||
enum json_tokener_state {
|
||||
@@ -42,22 +46,44 @@ enum json_tokener_state {
|
||||
json_tokener_state_boolean,
|
||||
json_tokener_state_number,
|
||||
json_tokener_state_array,
|
||||
json_tokener_state_array_add,
|
||||
json_tokener_state_array_sep,
|
||||
json_tokener_state_object,
|
||||
json_tokener_state_object_field_start,
|
||||
json_tokener_state_object_field,
|
||||
json_tokener_state_object_field_end,
|
||||
json_tokener_state_object_value,
|
||||
json_tokener_state_object_value_add,
|
||||
json_tokener_state_object_sep
|
||||
};
|
||||
|
||||
struct json_tokener
|
||||
struct json_tokener_srec
|
||||
{
|
||||
char *source;
|
||||
int pos;
|
||||
struct printbuf *pb;
|
||||
enum json_tokener_state state, saved_state;
|
||||
struct json_object *obj;
|
||||
struct json_object *current;
|
||||
char *obj_field_name;
|
||||
};
|
||||
|
||||
extern struct json_object* json_tokener_parse(char *s);
|
||||
#define JSON_TOKENER_MAX_DEPTH 32
|
||||
|
||||
struct json_tokener
|
||||
{
|
||||
char *str;
|
||||
struct printbuf *pb;
|
||||
int depth, is_double, st_pos, char_offset;
|
||||
enum json_tokener_error err;
|
||||
unsigned int ucs_char;
|
||||
char quote_char;
|
||||
struct json_tokener_srec stack[JSON_TOKENER_MAX_DEPTH];
|
||||
};
|
||||
|
||||
extern const char* json_tokener_errors[];
|
||||
|
||||
extern struct json_tokener* json_tokener_new();
|
||||
extern void json_tokener_free(struct json_tokener *tok);
|
||||
extern void json_tokener_reset(struct json_tokener *tok);
|
||||
extern struct json_object* json_tokener_parse(char *str);
|
||||
extern struct json_object* json_tokener_parse_ex(struct json_tokener *tok,
|
||||
char *str, int len);
|
||||
|
||||
#endif
|
||||
|
||||
26
test1.c
26
test1.c
@@ -6,15 +6,23 @@
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct json_tokener *tok;
|
||||
struct json_object *my_string, *my_int, *my_object, *my_array;
|
||||
struct json_object *new_obj;
|
||||
int i;
|
||||
|
||||
mc_set_debug(1);
|
||||
|
||||
my_string = json_object_new_string("\t");
|
||||
printf("my_string=%s\n", json_object_get_string(my_string));
|
||||
printf("my_string.to_string()=%s\n", json_object_to_json_string(my_string));
|
||||
json_object_put(my_string);
|
||||
|
||||
my_string = json_object_new_string("\\");
|
||||
printf("my_string=%s\n", json_object_get_string(my_string));
|
||||
printf("my_string.to_string()=%s\n", json_object_to_json_string(my_string));
|
||||
json_object_put(my_string);
|
||||
|
||||
my_string = json_object_new_string("foo");
|
||||
printf("my_string=%s\n", json_object_get_string(my_string));
|
||||
printf("my_string.to_string()=%s\n", json_object_to_json_string(my_string));
|
||||
@@ -98,6 +106,10 @@ int main(int argc, char **argv)
|
||||
printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj));
|
||||
json_object_put(new_obj);
|
||||
|
||||
new_obj = json_tokener_parse("[false]");
|
||||
printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj));
|
||||
json_object_put(new_obj);
|
||||
|
||||
new_obj = json_tokener_parse("[\"abc\",null,\"def\",12]");
|
||||
printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj));
|
||||
json_object_put(new_obj);
|
||||
@@ -128,6 +140,20 @@ int main(int argc, char **argv)
|
||||
new_obj = json_tokener_parse("foo");
|
||||
if(is_error(new_obj)) printf("got error as expected\n");
|
||||
|
||||
new_obj = json_tokener_parse("{ \"foo");
|
||||
if(is_error(new_obj)) printf("got error as expected\n");
|
||||
|
||||
/* test incremental parsing */
|
||||
tok = json_tokener_new();
|
||||
new_obj = json_tokener_parse_ex(tok, "{ \"foo", 6);
|
||||
if(is_error(new_obj)) printf("got error as expected\n");
|
||||
new_obj = json_tokener_parse_ex(tok, "\": {\"bar", 8);
|
||||
if(is_error(new_obj)) printf("got error as expected\n");
|
||||
new_obj = json_tokener_parse_ex(tok, "\":13}}", 6);
|
||||
printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj));
|
||||
json_object_put(new_obj);
|
||||
json_tokener_free(tok);
|
||||
|
||||
json_object_put(my_string);
|
||||
json_object_put(my_int);
|
||||
json_object_put(my_object);
|
||||
|
||||
Reference in New Issue
Block a user