|
|
1.1 misha 1: /** @file
2: Parser: @b json parser class.
3:
4: Copyright (c) 2001-2005 ArtLebedev Group (http://www.artlebedev.com)
5: Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
6: */
7:
1.2 ! misha 8: static const char * const IDENT_RESPONSE_C="$Date: 2010-08-31 13:00:47 $";
1.1 misha 9:
10: #include "classes.h"
11: #include "pa_vmethod_frame.h"
12:
13: #include "pa_request.h"
14: #include "pa_vbool.h"
15:
16: #include "pa_charset.h"
17: #include "pa_charsets.h"
18: #include "JSON_parser.h"
19:
20: // class
21:
22: class MJson: public Methoded {
23: public:
24: MJson();
25: public: // Methoded
26: bool used_directly() { return true; }
27: };
28:
29: // global variable
30:
31: DECLARE_CLASS_VAR(json, new MJson, 0);
32:
33: // methods
34: struct Json {
35: Stack<Value*> stack;
36: Stack<String::Body> key_stack;
37:
38: String::Body key;
39: Value* result;
40:
41: Junction* hook;
42: Charset *charset;
43: bool handle_double;
44: public:
45: Json(Charset* acharset): stack(), key_stack(), key(), result(NULL), hook(NULL), charset(acharset), handle_double(true){}
46: };
47:
48: static void set_json_value(Json *json, Value *value){
49: Value *top = json->stack.top_value();
50: if(json->key.is_empty()){
51: top->put_element(String(format(top->get_hash()->count(), 0)), value, true);
52: } else {
53: top->put_element(String(json->key, String::L_TAINTED), value, true);
54: json->key=String::Body();
55: }
56: }
57:
58: String::Body json_string(Json *json, const JSON_value* value){
59: return json->charset !=NULL ?
60: Charset::transcode(String::Body(value->vu.str.value, value->vu.str.length), UTF8_charset, *json->charset) :
61: String::Body(pa_strdup(value->vu.str.value, value->vu.str.length), value->vu.str.length);
62: }
63:
64: static Value *json_hook(Junction *hook, String::Body key, Value* value){
65: return value;
66: }
67:
68: static int json_callback(Json *json, int type, const JSON_value* value)
69: {
70: switch(type) {
71: case JSON_T_OBJECT_BEGIN:{
72: Value *v = new VHash();
73: if (json->hook){
74: json->key_stack.push(json->key);
75: } else {
76: if (json->stack.count()) set_json_value(json, v);
77: }
78: json->stack.push(v);
79: break;
80: }
81: case JSON_T_OBJECT_END:{
82: if (json->hook){
83: String::Body key = json->key_stack.pop();
84: json->result = json_hook(json->hook, key, json->stack.pop());
85:
86: if (json->stack.count()){
87: json->key = key;
88: set_json_value(json, json->result);
89: }
90: } else {
91: json->result = json->stack.pop();
92: }
93: break;
94: }
95: case JSON_T_ARRAY_BEGIN:{
96: Value *v = new VHash();
97: set_json_value(json, v);
98: json->stack.push(v);
99: break;
100: }
101: case JSON_T_ARRAY_END:
102: json->stack.pop();
103: break;
104: case JSON_T_KEY:
105: json->key = json_string(json, value);
106: break;
107: case JSON_T_INTEGER:
108: set_json_value(json, new VInt((int)value->vu.integer_value));
109: break;
110: case JSON_T_FLOAT:
111: if (json->handle_double){
112: set_json_value(json, new VDouble( String(json_string(json, value), String::L_TAINTED).as_double() ));
113: break;
114: } // else is JSON_T_STRING
115: case JSON_T_STRING:
116: set_json_value(json, new VString(*new String(json_string(json, value), String::L_TAINTED)));
117: break;
118: case JSON_T_NULL:
119: set_json_value(json, new VVoid());
120: break;
121: case JSON_T_TRUE:
122: set_json_value(json, &VBool::get(true));
123: break;
124: case JSON_T_FALSE:
125: set_json_value(json, &VBool::get(false));
126: break;
127: }
128: return 1;
129: }
130:
131: static char* json_error_message(int error_code){
132: static char* error_messages[] = {
133: NULL,
134: "invalid char",
135: "invalid keyword",
136: "invalid escape sequence",
137: "invalid unicode sequence",
138: "invalid number",
139: "nesting depth reached",
140: "unbalanced collection",
141: "expected key",
142: "expected colon",
143: "out of memory"
144: };
145: return error_messages[error_code];
146: }
147:
148: static void _parse(Request& r, MethodParams& params) {
149: //Json json = Json(r.charsets.source().isUTF8() ? (Charset*)NULL : &(r.charsets.source()));
150: Json& json = *new Json(r.charsets.source().isUTF8() ? (Charset*)NULL : &(r.charsets.source()));
151:
152: JSON_config config;
153: init_JSON_config(&config);
154:
155: config.depth = 19;
156: config.callback = (JSON_parser_callback)&json_callback;
157: config.allow_comments = 1;
158: config.handle_floats_manually = 1;
159: config.callback_ctx = &json;
160:
161: if(params.count() == 2)
162: if(HashStringValue* options=params.as_hash(1)) {
163: int valid_options=0;
164: if(Value* value=options->get("depth")) {
165: config.depth=value->as_int();
166: valid_options++;
167: }
168: if(Value* value=options->get("double")) {
169: json.handle_double=value->as_bool();
170: valid_options++;
171: }
172: if(Value* value=options->get("object")) {
173: json.hook=value->get_junction();
174: if (!json.hook || !json.hook->method || !json.hook->method->params_names || !(json.hook->method->params_names->count() == 2)){
175: throw Exception(PARSER_RUNTIME, 0, "$.object must be parser method with 2 parameters");
176: }
177: valid_options++;
178: }
179: if(valid_options!=options->count())
180: throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
181: }
182:
183: struct JSON_parser_struct* jc = new_JSON_parser(&config);
184:
185: const String& json_string=r.process_to_string(params[0]); // we accept both {} and []
1.2 ! misha 186: const String::Body json_body = json_string.cstr_to_string_body_untaint(String::L_JSON, 0, &(r.charsets));
1.1 misha 187: const char *json_cstr = json.charset != NULL ? Charset::transcode(json_body, *json.charset, UTF8_charset).cstr() : json_body.cstr();
188:
189: for (const char *c=json_cstr; *c; c++){
190: if (!JSON_parser_char(jc, *((const unsigned char *)c))) {
191: throw Exception("json.parse", 0, "%s at byte %d", json_error_message(JSON_parser_get_last_error(jc)), c-json_cstr);
192: }
193: }
194:
195: if (!JSON_parser_done(jc)) {
196: throw Exception("json.parse", 0, "%s at the end", json_error_message(JSON_parser_get_last_error(jc)));
197: }
198:
199: delete_JSON_parser(jc);
200:
201: if (json.result) r.write_no_lang(*json.result);
202: }
203:
204: // constructor
205:
206: MJson::MJson(): Methoded("json") {
207: add_native_method("parse", Method::CT_STATIC, _parse, 1, 2);
208: }