Browse Source

* 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
tags/json-c-0.10-20120530
Michael Clark 18 years ago
parent
commit
a850f8e29e
7 changed files with 307 additions and 193 deletions
  1. +7
    -6
      COPYING
  2. +6
    -0
      ChangeLog
  3. +1
    -1
      Makefile.am
  4. +5
    -1
      json_object.c
  5. +228
    -177
      json_tokener.c
  6. +34
    -8
      json_tokener.h
  7. +26
    -0
      test1.c

+ 7
- 6
COPYING View File

@@ -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 The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. 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.

+ 6
- 0
ChangeLog View File

@@ -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 0.6
* Fix bug in escaping of control characters * Fix bug in escaping of control characters
Johan Björklund, johbjo09 at kth dot se Johan Björklund, johbjo09 at kth dot se


+ 1
- 1
Makefile.am View File

@@ -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 lib_LTLIBRARIES = libjson.la




+ 5
- 1
json_object.c View File

@@ -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. * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd.
* Michael Clark <michael@metaparadigm.com> * Michael Clark <michael@metaparadigm.com>
@@ -93,6 +93,8 @@ static int json_escape_str(struct printbuf *pb, char *str)
case '\r': case '\r':
case '\t': case '\t':
case '"': case '"':
case '\\':
case '/':
if(pos - start_offset > 0) if(pos - start_offset > 0)
printbuf_memappend(pb, str + start_offset, pos - start_offset); printbuf_memappend(pb, str + start_offset, pos - start_offset);
if(c == '\b') printbuf_memappend(pb, "\\b", 2); 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 == '\r') printbuf_memappend(pb, "\\r", 2);
else if(c == '\t') printbuf_memappend(pb, "\\t", 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);
else if(c == '/') printbuf_memappend(pb, "\\/", 2);
start_offset = ++pos; start_offset = ++pos;
break; break;
default: default:


+ 228
- 177
json_tokener.c View File

@@ -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. * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd.
* Michael Clark <michael@metaparadigm.com> * Michael Clark <michael@metaparadigm.com>
@@ -23,6 +23,7 @@
#include "json_object.h" #include "json_object.h"
#include "json_tokener.h" #include "json_tokener.h"



#if !HAVE_STRNCASECMP && defined(_MSC_VER) #if !HAVE_STRNCASECMP && defined(_MSC_VER)
/* MSC has the version as _strnicmp */ /* MSC has the version as _strnicmp */
# define strncasecmp _strnicmp # define strncasecmp _strnicmp
@@ -31,67 +32,136 @@
#endif /* HAVE_STRNCASECMP */ #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";

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 = calloc(1, sizeof(struct json_tokener));
tok->pb = printbuf_new();
json_tokener_reset(tok);
return tok;
}


struct json_object* json_tokener_parse(char * s)
void json_tokener_free(struct json_tokener *tok)
{ {
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; 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; return obj;
} }



#if !HAVE_STRNDUP #if !HAVE_STRNDUP
/* CAW: compliant version of strndup() */ /* CAW: compliant version of strndup() */
char* strndup(const char* str, size_t n) char* strndup(const char* str, size_t n)
{ {
if(str) {
size_t len = strlen(str);
size_t nn = min(len,n);
char* s = (char*)malloc(sizeof(char) * (nn + 1));

if(s) {
memcpy(s, str, nn);
s[nn] = '\0';
}
if(str) {
size_t len = strlen(str);
size_t nn = min(len,n);
char* s = (char*)malloc(sizeof(char) * (nn + 1));
if(s) {
memcpy(s, str, nn);
s[nn] = '\0';
}


return s;
}
return s;
}


return NULL;
return NULL;
} }
#endif #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; char c;


state = json_tokener_state_eatws;
saved_state = json_tokener_state_start;
tok->char_offset = 0;
tok->err = json_tokener_success;


do { 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) { switch(state) {


case json_tokener_state_eatws: case json_tokener_state_eatws:
if(isspace(c)) { if(isspace(c)) {
this->pos++;
/* okay */
} else if(c == '/') { } else if(c == '/') {
printbuf_reset(tok->pb);
printbuf_memappend(tok->pb, &c, 1);
state = json_tokener_state_comment_start; state = json_tokener_state_comment_start;
start_offset = this->pos++;
} else { } else {
state = saved_state; state = saved_state;
goto redo_char;
} }
break; break;


@@ -99,35 +169,34 @@ static struct json_object* json_tokener_do_parse(struct json_tokener *this)
switch(c) { switch(c) {
case '{': case '{':
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
saved_state = json_tokener_state_object;
saved_state = json_tokener_state_object_field_start;
current = json_object_new_object(); current = json_object_new_object();
this->pos++;
break; break;
case '[': case '[':
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
saved_state = json_tokener_state_array; saved_state = json_tokener_state_array;
current = json_object_new_array(); current = json_object_new_array();
this->pos++;
break; break;
case 'N': case 'N':
case 'n': case 'n':
state = json_tokener_state_null; state = json_tokener_state_null;
start_offset = this->pos++;
break;
printbuf_reset(tok->pb);
tok->st_pos = 0;
goto redo_char;
case '"': case '"':
case '\'': case '\'':
quote_char = c;
printbuf_reset(this->pb);
state = json_tokener_state_string; state = json_tokener_state_string;
start_offset = ++this->pos;
printbuf_reset(tok->pb);
tok->quote_char = c;
break; break;
case 'T': case 'T':
case 't': case 't':
case 'F': case 'F':
case 'f': case 'f':
state = json_tokener_state_boolean; state = json_tokener_state_boolean;
start_offset = this->pos++;
break;
printbuf_reset(tok->pb);
tok->st_pos = 0;
goto redo_char;
#if defined(__GNUC__) #if defined(__GNUC__)
case '0' ... '9': case '0' ... '9':
#else #else
@@ -143,30 +212,38 @@ static struct json_object* json_tokener_do_parse(struct json_tokener *this)
case '9': case '9':
#endif #endif
case '-': case '-':
deemed_double = 0;
state = json_tokener_state_number; state = json_tokener_state_number;
start_offset = this->pos++;
break;
printbuf_reset(tok->pb);
tok->is_double = 0;
goto redo_char;
default: default:
err = json_tokener_error_parse_unexpected;
tok->err = json_tokener_error_parse_unexpected;
goto out; goto out;
} }
break; break;


case json_tokener_state_finish: 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: 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) {
current = NULL;
saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws;
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;
goto redo_char;
}
} else { } else {
this->pos++;
tok->err = json_tokener_error_parse_null;
goto out;
} }
tok->st_pos++;
break; break;


case json_tokener_state_comment_start: 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 == '/') { } else if(c == '/') {
state = json_tokener_state_comment_eol; state = json_tokener_state_comment_eol;
} else { } else {
err = json_tokener_error_parse_comment;
tok->err = json_tokener_error_parse_comment;
goto out; goto out;
} }
this->pos++;
printbuf_memappend(tok->pb, &c, 1);
break; break;


case json_tokener_state_comment: case json_tokener_state_comment:
if(c == '*') state = json_tokener_state_comment_end; if(c == '*') state = json_tokener_state_comment_end;
this->pos++;
printbuf_memappend(tok->pb, &c, 1);
break; break;


case json_tokener_state_comment_eol: case json_tokener_state_comment_eol:
if(c == '\n') { 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; state = json_tokener_state_eatws;
} else {
printbuf_memappend(tok->pb, &c, 1);
} }
this->pos++;
break; break;


case json_tokener_state_comment_end: case json_tokener_state_comment_end:
printbuf_memappend(tok->pb, &c, 1);
if(c == '/') { 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; state = json_tokener_state_eatws;
} else { } else {
state = json_tokener_state_comment; state = json_tokener_state_comment;
} }
this->pos++;
break; break;


case json_tokener_state_string: 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; saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
} else if(c == '\\') { } else if(c == '\\') {
saved_state = json_tokener_state_string; saved_state = json_tokener_state_string;
state = json_tokener_state_string_escape; state = json_tokener_state_string_escape;
} else {
printbuf_memappend(tok->pb, &c, 1);
} }
this->pos++;
break; break;


case json_tokener_state_string_escape: case json_tokener_state_string_escape:
switch(c) { switch(c) {
case '"': case '"':
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; state = saved_state;
break; break;
case 'b': case 'b':
case 'n': case 'n':
case 'r': case 'r':
case 't': 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; state = saved_state;
break; break;
case 'u': 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; state = json_tokener_state_escape_unicode;
break; break;
default: default:
err = json_tokener_error_parse_string;
tok->err = json_tokener_error_parse_string;
goto out; goto out;
} }
break; break;


case json_tokener_state_escape_unicode: case json_tokener_state_escape_unicode:
if(strchr(json_hex_chars, c)) { 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 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 { } 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; state = saved_state;
} }
} else { } else {
err = json_tokener_error_parse_string;
tok->err = json_tokener_error_parse_string;
goto out; goto out;
} }
break; break;


case json_tokener_state_boolean: 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); current = json_object_new_boolean(1);
saved_state = json_tokener_state_finish; saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws; 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); current = json_object_new_boolean(0);
saved_state = json_tokener_state_finish; saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
} else {
this->pos++;
goto redo_char;
} }
} else { } else {
err = json_tokener_error_parse_boolean;
tok->err = json_tokener_error_parse_boolean;
goto out; goto out;
} }
tok->st_pos++;
break; break;


case json_tokener_state_number: 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; int numi;
double numd; 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); 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); current = json_object_new_double(numd);
} else { } else {
free(tmp);
err = json_tokener_error_parse_number;
tok->err = json_tokener_error_parse_number;
goto out; goto out;
} }
free(tmp);
saved_state = json_tokener_state_finish; saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
} else {
if(c == '.' || c == 'e') deemed_double = 1;
this->pos++;
goto redo_char;
} }
break; break;


case json_tokener_state_array: case json_tokener_state_array:
if(c == ']') { if(c == ']') {
this->pos++;
saved_state = json_tokener_state_finish; saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
} else { } 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; goto out;
} }
json_object_array_add(current, obj);
saved_state = json_tokener_state_array_sep;
state = json_tokener_state_eatws;
state = json_tokener_state_array_add;
tok->depth++;
json_tokener_reset_level(tok, tok->depth);
goto redo_char;
} }
break; 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;
goto redo_char;

case json_tokener_state_array_sep: case json_tokener_state_array_sep:
if(c == ']') { if(c == ']') {
this->pos++;
saved_state = json_tokener_state_finish; saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
} else if(c == ',') { } else if(c == ',') {
this->pos++;
saved_state = json_tokener_state_array; saved_state = json_tokener_state_array;
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
} else { } else {
json_object_put(current);
return error_ptr(-json_tokener_error_parse_array);
tok->err = json_tokener_error_parse_array;
goto out;
} }
break; 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: case json_tokener_state_object_field_start:
if(c == '}') { if(c == '}') {
this->pos++;
saved_state = json_tokener_state_finish; saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
} else if (c == '"' || c == '\'') { } 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; state = json_tokener_state_object_field;
start_offset = ++this->pos;
} else { } else {
err = json_tokener_error_parse_object;
tok->err = json_tokener_error_parse_object_key_name;
goto out; goto out;
} }
break; break;


case json_tokener_state_object_field: 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; saved_state = json_tokener_state_object_field_end;
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
} else if(c == '\\') { } else if(c == '\\') {
saved_state = json_tokener_state_object_field; saved_state = json_tokener_state_object_field;
state = json_tokener_state_string_escape; state = json_tokener_state_string_escape;
} else {
printbuf_memappend(tok->pb, &c, 1);
} }
this->pos++;
break; break;


case json_tokener_state_object_field_end: case json_tokener_state_object_field_end:
if(c == ':') { if(c == ':') {
this->pos++;
saved_state = json_tokener_state_object_value; saved_state = json_tokener_state_object_value;
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
} else { } else {
return error_ptr(-json_tokener_error_parse_object);
tok->err = json_tokener_error_parse_object_key_sep;
goto out;
} }
break; break;


case json_tokener_state_object_value: 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; 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); json_object_object_add(current, obj_field_name, obj);
free(obj_field_name); free(obj_field_name);
obj_field_name = NULL; obj_field_name = NULL;
saved_state = json_tokener_state_object_sep; saved_state = json_tokener_state_object_sep;
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
break;
goto redo_char;


case json_tokener_state_object_sep: case json_tokener_state_object_sep:
if(c == '}') { if(c == '}') {
this->pos++;
saved_state = json_tokener_state_finish; saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
} else if(c == ',') { } else if(c == ',') {
this->pos++;
saved_state = json_tokener_state_object;
saved_state = json_tokener_state_object_field_start;
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
} else { } else {
err = json_tokener_error_parse_object;
tok->err = json_tokener_error_parse_object_value_sep;
goto out; goto out;
} }
break; break;


} }
str++;
tok->char_offset++;
} while(c); } while(c);


if(state != json_tokener_state_finish && if(state != json_tokener_state_finish &&
saved_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: 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;
} }

+ 34
- 8
json_tokener.h View File

@@ -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. * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd.
* Michael Clark <michael@metaparadigm.com> * Michael Clark <michael@metaparadigm.com>
@@ -16,15 +16,19 @@


enum json_tokener_error { enum json_tokener_error {
json_tokener_success, json_tokener_success,
json_tokener_continue,
json_tokener_error_depth,
json_tokener_error_parse_eof,
json_tokener_error_parse_unexpected, json_tokener_error_parse_unexpected,
json_tokener_error_parse_null, json_tokener_error_parse_null,
json_tokener_error_parse_boolean, json_tokener_error_parse_boolean,
json_tokener_error_parse_number, json_tokener_error_parse_number,
json_tokener_error_parse_array, 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_string,
json_tokener_error_parse_comment,
json_tokener_error_parse_eof
json_tokener_error_parse_comment
}; };


enum json_tokener_state { enum json_tokener_state {
@@ -42,22 +46,44 @@ enum json_tokener_state {
json_tokener_state_boolean, json_tokener_state_boolean,
json_tokener_state_number, json_tokener_state_number,
json_tokener_state_array, json_tokener_state_array,
json_tokener_state_array_add,
json_tokener_state_array_sep, json_tokener_state_array_sep,
json_tokener_state_object,
json_tokener_state_object_field_start, json_tokener_state_object_field_start,
json_tokener_state_object_field, json_tokener_state_object_field,
json_tokener_state_object_field_end, json_tokener_state_object_field_end,
json_tokener_state_object_value, json_tokener_state_object_value,
json_tokener_state_object_value_add,
json_tokener_state_object_sep json_tokener_state_object_sep
}; };


struct json_tokener_srec
{
enum json_tokener_state state, saved_state;
struct json_object *obj;
struct json_object *current;
char *obj_field_name;
};

#define JSON_TOKENER_MAX_DEPTH 32

struct json_tokener struct json_tokener
{ {
char *source;
int pos;
char *str;
struct printbuf *pb; 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 struct json_object* json_tokener_parse(char *s);
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 #endif

+ 26
- 0
test1.c View File

@@ -6,15 +6,23 @@


int main(int argc, char **argv) int main(int argc, char **argv)
{ {
struct json_tokener *tok;
struct json_object *my_string, *my_int, *my_object, *my_array; struct json_object *my_string, *my_int, *my_object, *my_array;
struct json_object *new_obj; struct json_object *new_obj;
int i; int i;


mc_set_debug(1);

my_string = json_object_new_string("\t"); my_string = json_object_new_string("\t");
printf("my_string=%s\n", json_object_get_string(my_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)); printf("my_string.to_string()=%s\n", json_object_to_json_string(my_string));
json_object_put(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"); my_string = json_object_new_string("foo");
printf("my_string=%s\n", json_object_get_string(my_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)); 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)); printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj));
json_object_put(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]"); new_obj = json_tokener_parse("[\"abc\",null,\"def\",12]");
printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj));
json_object_put(new_obj); json_object_put(new_obj);
@@ -128,6 +140,20 @@ int main(int argc, char **argv)
new_obj = json_tokener_parse("foo"); new_obj = json_tokener_parse("foo");
if(is_error(new_obj)) printf("got error as expected\n"); 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_string);
json_object_put(my_int); json_object_put(my_int);
json_object_put(my_object); json_object_put(my_object);


Loading…
Cancel
Save