Annotation of parser3/src/classes/json.C, revision 1.11
1.1 misha 1: /** @file
2: Parser: @b json parser class.
3:
1.3 moko 4: Copyright (c) 2010 ArtLebedev Group (http://www.artlebedev.com)
1.1 misha 5: */
6:
1.11 ! moko 7: static const char * const IDENT_RESPONSE_C="$Date: 2010-10-01 23:07:30 $";
1.1 misha 8:
9: #include "classes.h"
10: #include "pa_vmethod_frame.h"
11:
12: #include "pa_request.h"
13: #include "pa_vbool.h"
14:
15: #include "pa_charset.h"
16: #include "pa_charsets.h"
17: #include "JSON_parser.h"
18:
19: // class
20:
21: class MJson: public Methoded {
22: public:
23: MJson();
24: };
25:
26: // global variable
27:
28: DECLARE_CLASS_VAR(json, new MJson, 0);
29:
30: // methods
31: struct Json {
1.4 moko 32: Stack<VHash*> stack;
1.3 moko 33: Stack<String*> key_stack;
1.1 misha 34:
1.3 moko 35: String* key;
1.1 misha 36: Value* result;
37:
38: Junction* hook;
1.3 moko 39: Request* request;
40:
1.1 misha 41: Charset *charset;
42: bool handle_double;
1.4 moko 43: enum Distinct { D_EXCEPTION, D_FIRST, D_LAST, D_ALL } distinct;
1.3 moko 44:
1.4 moko 45: Json(Charset* acharset): stack(), key_stack(), key(NULL), result(NULL), hook(NULL), request(NULL), charset(acharset), handle_double(true), distinct(D_EXCEPTION){}
46:
47: bool set_distinct(const String &value){
48: if (value == "first") distinct = D_FIRST;
49: else if (value == "last") distinct = D_LAST;
50: else if (value == "all") distinct = D_ALL;
51: else return false;
52: return true;
53: }
1.1 misha 54: };
55:
56: static void set_json_value(Json *json, Value *value){
1.4 moko 57: VHash *top = json->stack.top_value();
1.3 moko 58: if(json->key == NULL){
1.4 moko 59: top->hash().put(String(format(top->get_hash()->count(), 0)), value);
1.1 misha 60: } else {
1.4 moko 61: switch (json->distinct){
62: case Json::D_EXCEPTION:
63: if (top->hash().put_dont_replace(*json->key, value))
64: throw Exception(PARSER_RUNTIME, json->key, "duplicate key");
65: break;
66: case Json::D_FIRST:
67: top->hash().put_dont_replace(*json->key, value);
68: break;
69: case Json::D_LAST:
70: top->hash().put(*json->key, value);
71: break;
72: case Json::D_ALL:
73: if (top->hash().put_dont_replace(*json->key, value)){
74: for(int i=2;;i++){
75: String key;
76: key << *json->key << "_" << format(i, 0);
77: if (!top->hash().put_dont_replace(key, value)) break;
78: }
79: }
80: break;
81: }
1.3 moko 82: json->key=NULL;
1.1 misha 83: }
84: }
85:
1.3 moko 86: String* json_string(Json *json, const JSON_value* value){
87: String::C result = json->charset !=NULL ?
88: Charset::transcode(String::C(value->vu.str.value, value->vu.str.length), UTF8_charset, *json->charset) :
89: String::C(pa_strdup(value->vu.str.value, value->vu.str.length), value->vu.str.length);
90: return new String(result.str, String::L_TAINTED, result.length);
1.1 misha 91: }
92:
1.3 moko 93: static Value *json_hook(Request &r, Junction *hook, String* key, Value* value){
94: VMethodFrame frame(*hook->method, r.method_frame, hook->self);
1.10 moko 95: Value *params[]={new VString(key ? *key : String::Empty), value};
1.3 moko 96:
97: frame.store_params(params, 2);
98: r.execute_method(frame);
99:
100: return &frame.result().as_value();
1.1 misha 101: }
102:
103: static int json_callback(Json *json, int type, const JSON_value* value)
104: {
105: switch(type) {
106: case JSON_T_OBJECT_BEGIN:{
1.4 moko 107: VHash *v = new VHash();
1.1 misha 108: if (json->hook){
109: json->key_stack.push(json->key);
110: } else {
111: if (json->stack.count()) set_json_value(json, v);
112: }
113: json->stack.push(v);
114: break;
115: }
116: case JSON_T_OBJECT_END:{
117: if (json->hook){
1.3 moko 118: String* key = json->key_stack.pop();
119: json->result = json_hook(*json->request, json->hook, key, json->stack.pop());
1.1 misha 120:
121: if (json->stack.count()){
122: json->key = key;
123: set_json_value(json, json->result);
124: }
125: } else {
126: json->result = json->stack.pop();
127: }
128: break;
129: }
130: case JSON_T_ARRAY_BEGIN:{
1.4 moko 131: VHash *v = new VHash();
1.1 misha 132: set_json_value(json, v);
133: json->stack.push(v);
134: break;
135: }
136: case JSON_T_ARRAY_END:
137: json->stack.pop();
138: break;
139: case JSON_T_KEY:
140: json->key = json_string(json, value);
141: break;
142: case JSON_T_INTEGER:
143: set_json_value(json, new VInt((int)value->vu.integer_value));
144: break;
145: case JSON_T_FLOAT:
146: if (json->handle_double){
1.3 moko 147: set_json_value(json, new VDouble( json_string(json, value)->as_double() ));
1.1 misha 148: break;
149: } // else is JSON_T_STRING
150: case JSON_T_STRING:
1.3 moko 151: set_json_value(json, new VString(*json_string(json, value)));
1.1 misha 152: break;
153: case JSON_T_NULL:
154: set_json_value(json, new VVoid());
155: break;
156: case JSON_T_TRUE:
157: set_json_value(json, &VBool::get(true));
158: break;
159: case JSON_T_FALSE:
160: set_json_value(json, &VBool::get(false));
161: break;
162: }
163: return 1;
164: }
165:
1.5 moko 166: static const char* json_error_message(int error_code){
167: static const char* error_messages[] = {
1.1 misha 168: NULL,
169: "invalid char",
170: "invalid keyword",
171: "invalid escape sequence",
172: "invalid unicode sequence",
173: "invalid number",
174: "nesting depth reached",
175: "unbalanced collection",
176: "expected key",
177: "expected colon",
178: "out of memory"
179: };
180: return error_messages[error_code];
181: }
182:
183: static void _parse(Request& r, MethodParams& params) {
1.3 moko 184: const String& json_string=params.as_string(0, "json must be string");
185:
186: Json json(r.charsets.source().isUTF8() ? NULL : &(r.charsets.source()));
1.1 misha 187:
188: JSON_config config;
189: init_JSON_config(&config);
190:
191: config.depth = 19;
192: config.callback = (JSON_parser_callback)&json_callback;
193: config.allow_comments = 1;
194: config.handle_floats_manually = 1;
195: config.callback_ctx = &json;
196:
197: if(params.count() == 2)
198: if(HashStringValue* options=params.as_hash(1)) {
199: int valid_options=0;
200: if(Value* value=options->get("depth")) {
1.4 moko 201: config.depth=r.process_to_value(*value).as_int();
1.1 misha 202: valid_options++;
203: }
204: if(Value* value=options->get("double")) {
1.4 moko 205: json.handle_double=r.process_to_value(*value).as_bool();
206: valid_options++;
207: }
208: if(Value* value=options->get("distinct")) {
209: const String& sdistinct=value->as_string();
210: if (!json.set_distinct(sdistinct))
211: throw Exception(PARSER_RUNTIME, &sdistinct, "must be 'first', 'last' or 'all'");
1.1 misha 212: valid_options++;
213: }
214: if(Value* value=options->get("object")) {
215: json.hook=value->get_junction();
1.3 moko 216: json.request=&r;
217: if (!json.hook || !json.hook->method || !json.hook->method->params_names || !(json.hook->method->params_names->count() == 2))
1.1 misha 218: throw Exception(PARSER_RUNTIME, 0, "$.object must be parser method with 2 parameters");
219: valid_options++;
220: }
221: if(valid_options!=options->count())
222: throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
223: }
224:
1.2 misha 225: const String::Body json_body = json_string.cstr_to_string_body_untaint(String::L_JSON, 0, &(r.charsets));
1.1 misha 226: const char *json_cstr = json.charset != NULL ? Charset::transcode(json_body, *json.charset, UTF8_charset).cstr() : json_body.cstr();
227:
1.3 moko 228: struct JSON_parser_struct* jc = new_JSON_parser(&config);
229:
1.1 misha 230: for (const char *c=json_cstr; *c; c++){
231: if (!JSON_parser_char(jc, *((const unsigned char *)c))) {
232: throw Exception("json.parse", 0, "%s at byte %d", json_error_message(JSON_parser_get_last_error(jc)), c-json_cstr);
233: }
234: }
235:
236: if (!JSON_parser_done(jc)) {
237: throw Exception("json.parse", 0, "%s at the end", json_error_message(JSON_parser_get_last_error(jc)));
238: }
239:
240: delete_JSON_parser(jc);
241:
242: if (json.result) r.write_no_lang(*json.result);
243: }
244:
1.8 moko 245: char *get_indent(uint level){
246: static char* cache[ANTI_ENDLESS_JSON_STRING_RECOURSION]={};
247: if (!cache[level]){
248: char *result = static_cast<char*>(pa_gc_malloc_atomic(level+1));
249: memset(result, '\t', level);
1.9 moko 250: result[level]='\0';
1.8 moko 251: return cache[level]=result;
252: }
253: return cache[level];
254: }
255:
1.6 misha 256: const String& value_json_string(Value& v, Json_options* options);
257:
258: const String& hash_json_string(HashStringValue &hash, Json_options* options) {
259: if(!hash.count())
1.8 moko 260: return *new String("{}", String::L_AS_IS);
261:
262: uint level = options->r->json_string_recoursion_go_down();
263:
264: String& result = *new String("{\n", String::L_AS_IS);
265:
266: if (options->indent){
267:
268: String *delim=NULL;
269: options->indent=get_indent(level);
270: for(HashStringValue::Iterator i(hash); i; i.next() ){
271: if (delim){
272: result << *delim;
273: } else {
274: result << options->indent << "\"";
275: delim = new String(",\n", String::L_AS_IS); *delim << options->indent << "\"";
276: }
277: result << String(i.key(), String::L_JSON) << "\":" << value_json_string(*i.value(), options);
278: }
279: result << "\n" << (options->indent=get_indent(level-1)) << "}";
1.6 misha 280:
1.8 moko 281: } else {
282:
283: bool need_delim=false;
284: for(HashStringValue::Iterator i(hash); i; i.next() ){
285: result << (need_delim ? ",\n\"" : "\"");
286: result << String(i.key(), String::L_JSON) << "\":" << value_json_string(*i.value(), options);
287: need_delim=true;
288: }
289: result << "\n}";
1.6 misha 290:
291: }
292:
293: options->r->json_string_recoursion_go_up();
294: return result;
295: }
296:
297: const String& value_json_string(Value& v, Json_options* options) {
298: if(options && options->methods)
299: if(Value* method=options->methods->get(v.type())){
300: Junction* junction=method->get_junction();
301: VMethodFrame frame(*junction->method, options->r->method_frame, junction->self);
302:
303: Value *params[]={&v, (options->params) ? options->params : VVoid::get()};
304: frame.store_params(params, 2);
305:
306: options->r->execute_method(frame);
307:
308: return frame.result().as_string();
309: }
310:
311: if(HashStringValue* hash=v.get_hash())
312: return hash_json_string(*hash, options);
313:
314: return *v.get_json_string(options);
315: }
316:
317: static void _string(Request& r, MethodParams& params) {
318: Json_options json(&r);
319:
320: if(params.count() == 2)
321: if(HashStringValue* options=params.as_hash(1)) {
322: json.params=params.get(1);
323: HashStringValue* methods=new HashStringValue();
324: int valid_options=0;
325: for(HashStringValue::Iterator i(*options); i; i.next() ){
326: String::Body key=i.key();
327: Value* value=i.value();
328: if(key == "skip-unknown"){
329: json.skip_unknown=r.process_to_value(*value).as_bool();
330: valid_options++;
331: } else if(key == "date" && value->is_string()){
332: const String& svalue=value->as_string();
333: if(!json.set_date_format(svalue))
334: throw Exception(PARSER_RUNTIME, &svalue, "must be 'sql-string', 'gmt-string' or 'unix-timestamp'");
335: valid_options++;
1.8 moko 336: } else if(key == "indent"){
337: json.indent=r.process_to_value(*value).as_bool() ? "":NULL;
338: valid_options++;
1.6 misha 339: } else if(key == "table" && value->is_string()){
340: const String& svalue=value->as_string();
341: if(!json.set_table_format(svalue))
342: throw Exception(PARSER_RUNTIME, &svalue, "must be 'array' or 'object'");
343: valid_options++;
344: } else if(key == "file" && value->is_string()){
345: const String& svalue=value->as_string();
346: if(!json.set_file_format(svalue))
347: throw Exception(PARSER_RUNTIME, &svalue, "must be 'base64' or 'text'");
348: valid_options++;
349: } else if(Junction* junction=value->get_junction()){
350: if(!junction->method || !junction->method->params_names || junction->method->params_names->count() != 2)
1.9 moko 351: throw Exception(PARSER_RUNTIME, 0, "$.%s must be parser method with 2 parameters", key.cstr());
1.6 misha 352: methods->put(key, value);
353: valid_options++;
354: }
355: }
356: if(valid_options!=options->count())
357: throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
358: if(methods->count())
359: json.methods=methods;
360: }
361:
362: r.write_pass_lang(value_json_string(params[0], &json));
363: }
364:
1.1 misha 365: // constructor
366:
367: MJson::MJson(): Methoded("json") {
368: add_native_method("parse", Method::CT_STATIC, _parse, 1, 2);
1.6 misha 369:
370: add_native_method("string", Method::CT_ANY, _string, 1, 2);
1.1 misha 371: }
E-mail: