Annotation of parser3/src/classes/json.C, revision 1.32
1.1 misha 1: /** @file
2: Parser: @b json parser class.
3:
1.17 moko 4: Copyright (c) 2000-2012 Art. Lebedev Studio (http://www.artlebedev.com)
1.1 misha 5: */
6:
7: #include "classes.h"
8: #include "pa_vmethod_frame.h"
9:
10: #include "pa_request.h"
11: #include "pa_vbool.h"
12:
13: #include "pa_charset.h"
14: #include "pa_charsets.h"
1.29 moko 15: #include "pa_json.h"
1.1 misha 16:
1.14 misha 17: #ifdef XML
18: #include "pa_vxdoc.h"
19: #endif
20:
1.32 ! misha 21: volatile const char * IDENT_JSON_C="$Id: json.C,v 1.31 2014/05/23 02:47:16 misha Exp $";
1.17 moko 22:
1.1 misha 23: // class
24:
25: class MJson: public Methoded {
26: public:
27: MJson();
28: };
29:
30: // global variable
31:
32: DECLARE_CLASS_VAR(json, new MJson, 0);
33:
34: // methods
35: struct Json {
1.4 moko 36: Stack<VHash*> stack;
1.3 moko 37: Stack<String*> key_stack;
1.1 misha 38:
1.3 moko 39: String* key;
1.1 misha 40: Value* result;
41:
1.16 misha 42: Junction* hook_object;
43: Junction* hook_array;
1.3 moko 44: Request* request;
45:
1.1 misha 46: Charset *charset;
1.23 moko 47: String::Language taint;
48:
1.1 misha 49: bool handle_double;
1.30 misha 50: bool handle_int;
1.4 moko 51: enum Distinct { D_EXCEPTION, D_FIRST, D_LAST, D_ALL } distinct;
1.3 moko 52:
1.23 moko 53: Json(Charset* acharset): stack(), key_stack(), key(NULL), result(NULL), hook_object(NULL), hook_array(NULL),
1.30 misha 54: request(NULL), charset(acharset), taint(String::L_TAINTED), handle_double(true), handle_int(true),
55: distinct(D_EXCEPTION){}
1.4 moko 56:
57: bool set_distinct(const String &value){
58: if (value == "first") distinct = D_FIRST;
59: else if (value == "last") distinct = D_LAST;
60: else if (value == "all") distinct = D_ALL;
61: else return false;
62: return true;
63: }
1.1 misha 64: };
65:
66: static void set_json_value(Json *json, Value *value){
1.4 moko 67: VHash *top = json->stack.top_value();
1.3 moko 68: if(json->key == NULL){
1.4 moko 69: top->hash().put(String(format(top->get_hash()->count(), 0)), value);
1.1 misha 70: } else {
1.4 moko 71: switch (json->distinct){
72: case Json::D_EXCEPTION:
73: if (top->hash().put_dont_replace(*json->key, value))
74: throw Exception(PARSER_RUNTIME, json->key, "duplicate key");
75: break;
76: case Json::D_FIRST:
77: top->hash().put_dont_replace(*json->key, value);
78: break;
79: case Json::D_LAST:
80: top->hash().put(*json->key, value);
81: break;
82: case Json::D_ALL:
83: if (top->hash().put_dont_replace(*json->key, value)){
84: for(int i=2;;i++){
85: String key;
86: key << *json->key << "_" << format(i, 0);
87: if (!top->hash().put_dont_replace(key, value)) break;
88: }
89: }
90: break;
91: }
1.3 moko 92: json->key=NULL;
1.1 misha 93: }
94: }
95:
1.25 moko 96: String* json_string(Json *json, const char *value, uint32_t length){
1.3 moko 97: String::C result = json->charset !=NULL ?
1.25 moko 98: Charset::transcode(String::C(value, length), UTF8_charset, *json->charset) :
99: String::C(pa_strdup(value, length), length);
1.23 moko 100: return new String(result.str, json->taint, result.length);
1.1 misha 101: }
102:
1.3 moko 103: static Value *json_hook(Request &r, Junction *hook, String* key, Value* value){
104: VMethodFrame frame(*hook->method, r.method_frame, hook->self);
1.10 moko 105: Value *params[]={new VString(key ? *key : String::Empty), value};
1.3 moko 106:
107: frame.store_params(params, 2);
108: r.execute_method(frame);
109:
110: return &frame.result().as_value();
1.1 misha 111: }
112:
1.25 moko 113: static int json_callback(Json *json, int type, const char *value, uint32_t length)
1.1 misha 114: {
115: switch(type) {
1.25 moko 116: case JSON_OBJECT_BEGIN:{
1.4 moko 117: VHash *v = new VHash();
1.16 misha 118: if (json->hook_object){
1.1 misha 119: json->key_stack.push(json->key);
1.16 misha 120: json->key=NULL;
1.1 misha 121: } else {
122: if (json->stack.count()) set_json_value(json, v);
123: }
124: json->stack.push(v);
125: break;
126: }
1.25 moko 127: case JSON_OBJECT_END:{
1.16 misha 128: if (json->hook_object){
1.3 moko 129: String* key = json->key_stack.pop();
1.16 misha 130: json->result = json_hook(*json->request, json->hook_object, key, json->stack.pop());
1.1 misha 131:
132: if (json->stack.count()){
133: json->key = key;
134: set_json_value(json, json->result);
135: }
136: } else {
137: json->result = json->stack.pop();
138: }
139: break;
140: }
1.25 moko 141: case JSON_ARRAY_BEGIN:{
1.4 moko 142: VHash *v = new VHash();
1.16 misha 143: if (json->hook_array){
144: json->key_stack.push(json->key);
145: json->key=NULL;
146: } else {
147: if (json->stack.count()) set_json_value(json, v);
148: }
1.1 misha 149: json->stack.push(v);
150: break;
151: }
1.25 moko 152: case JSON_ARRAY_END:
1.12 moko 153: // libjson supports array at top level, we too
1.16 misha 154: if (json->hook_array){
155: String* key = json->key_stack.pop();
156: json->result = json_hook(*json->request, json->hook_array, key, json->stack.pop());
157:
158: if (json->stack.count()){
159: json->key = key;
160: set_json_value(json, json->result);
161: }
162: } else {
163: json->result = json->stack.pop();
164: }
1.1 misha 165: break;
1.25 moko 166: case JSON_KEY:
167: json->key = json_string(json, value, length);
1.16 misha 168: break;
1.25 moko 169: case JSON_INT:
1.30 misha 170: if (json->handle_int){
171: set_json_value(json, new VDouble( json_string(json, value, length)->as_double() ));
172: } else {
173: // JSON_STRING
174: set_json_value(json, new VString(*json_string(json, value, length)));
175: }
1.1 misha 176: break;
1.25 moko 177: case JSON_FLOAT:
1.1 misha 178: if (json->handle_double){
1.25 moko 179: set_json_value(json, new VDouble( json_string(json, value, length)->as_double() ));
1.1 misha 180: break;
1.25 moko 181: } // else is JSON_STRING
182: case JSON_STRING:
183: set_json_value(json, new VString(*json_string(json, value, length)));
1.1 misha 184: break;
1.25 moko 185: case JSON_NULL:
1.18 moko 186: set_json_value(json, VVoid::get());
1.1 misha 187: break;
1.25 moko 188: case JSON_TRUE:
1.1 misha 189: set_json_value(json, &VBool::get(true));
190: break;
1.25 moko 191: case JSON_FALSE:
1.1 misha 192: set_json_value(json, &VBool::get(false));
1.25 moko 193: break;
1.1 misha 194: }
1.25 moko 195: return 0;
1.1 misha 196: }
197:
1.5 moko 198: static const char* json_error_message(int error_code){
199: static const char* error_messages[] = {
1.1 misha 200: NULL,
1.25 moko 201: "out of memory",
202: "bad character",
203: "stack empty",
204: "pop unexpected mode",
205: "nesting limit",
206: "data limit",
207: "comment not allowed by config",
208: "unexpected char",
209: "missing unicode low surrogate",
210: "unexpected unicode low surrogate",
211: "error comma out of structure",
212: "error in a callback"
1.1 misha 213: };
214: return error_messages[error_code];
215: }
216:
1.23 moko 217: extern String::Language get_untaint_lang(const String& lang_name);
218:
1.1 misha 219: static void _parse(Request& r, MethodParams& params) {
1.3 moko 220: const String& json_string=params.as_string(0, "json must be string");
221:
222: Json json(r.charsets.source().isUTF8() ? NULL : &(r.charsets.source()));
1.1 misha 223:
1.25 moko 224: json_config config = {
225: 0, // buffer_initial_size
1.26 moko 226: 128, // max_nesting
1.25 moko 227: 0, // max_data
228: 1, // allow_c_comments
229: 1, // allow_yaml_comments
230: pa_malloc,
231: pa_realloc,
232: pa_free
233: };
1.1 misha 234:
235: if(params.count() == 2)
236: if(HashStringValue* options=params.as_hash(1)) {
237: int valid_options=0;
238: if(Value* value=options->get("depth")) {
1.25 moko 239: config.max_nesting=r.process_to_value(*value).as_int();
1.1 misha 240: valid_options++;
241: }
242: if(Value* value=options->get("double")) {
1.4 moko 243: json.handle_double=r.process_to_value(*value).as_bool();
244: valid_options++;
245: }
1.30 misha 246: if(Value* value=options->get("int")) {
247: json.handle_int=r.process_to_value(*value).as_bool();
248: valid_options++;
249: }
1.4 moko 250: if(Value* value=options->get("distinct")) {
251: const String& sdistinct=value->as_string();
252: if (!json.set_distinct(sdistinct))
253: throw Exception(PARSER_RUNTIME, &sdistinct, "must be 'first', 'last' or 'all'");
1.1 misha 254: valid_options++;
255: }
1.23 moko 256: if(Value* value=options->get("taint")) {
257: json.taint=get_untaint_lang(value->as_string());
258: valid_options++;
259: }
1.1 misha 260: if(Value* value=options->get("object")) {
1.16 misha 261: json.hook_object=value->get_junction();
1.3 moko 262: json.request=&r;
1.16 misha 263: if (!json.hook_object || !json.hook_object->method || !json.hook_object->method->params_names || !(json.hook_object->method->params_names->count() == 2))
1.1 misha 264: throw Exception(PARSER_RUNTIME, 0, "$.object must be parser method with 2 parameters");
265: valid_options++;
266: }
1.16 misha 267: if(Value* value=options->get("array")) {
268: json.hook_array=value->get_junction();
269: json.request=&r;
270: if (!json.hook_array || !json.hook_array->method || !json.hook_array->method->params_names || !(json.hook_array->method->params_names->count() == 2))
271: throw Exception(PARSER_RUNTIME, 0, "$.array must be parser method with 2 parameters");
272: valid_options++;
273: }
1.1 misha 274: if(valid_options!=options->count())
275: throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
276: }
277:
1.28 moko 278: const String::Body json_body = json_string.cstr_to_string_body_untaint(String::L_JSON, r.connection(false), &r.charsets);
1.1 misha 279: const char *json_cstr = json.charset != NULL ? Charset::transcode(json_body, *json.charset, UTF8_charset).cstr() : json_body.cstr();
280:
1.25 moko 281: json_parser parser;
282: if(int result = json_parser_init(&parser, &config, (json_parser_callback)&json_callback, &json))
283: throw Exception("json.parse", 0, "%s", json_error_message(result));
284:
285: uint32_t processed;
286: if(int result = json_parser_string(&parser, json_cstr, strlen(json_cstr), &processed))
287: throw Exception("json.parse", 0, "%s at byte %d", json_error_message(result), processed);
1.3 moko 288:
1.25 moko 289: if (!json_parser_is_done(&parser))
290: throw Exception("json.parse", 0, "unexpected end of json data");
1.1 misha 291:
1.25 moko 292: json_parser_free(&parser);
1.1 misha 293:
294: if (json.result) r.write_no_lang(*json.result);
295: }
296:
1.26 moko 297: const uint ANTI_ENDLESS_JSON_STRING_RECOURSION=128;
298:
1.8 moko 299: char *get_indent(uint level){
300: static char* cache[ANTI_ENDLESS_JSON_STRING_RECOURSION]={};
301: if (!cache[level]){
302: char *result = static_cast<char*>(pa_gc_malloc_atomic(level+1));
303: memset(result, '\t', level);
1.9 moko 304: result[level]='\0';
1.8 moko 305: return cache[level]=result;
306: }
307: return cache[level];
308: }
309:
1.26 moko 310: class Json_string_recoursion {
311: Json_options& foptions;
312: public:
313: Json_string_recoursion(Json_options& aoptions) : foptions(aoptions) {
314: if(++foptions.json_string_recoursion==ANTI_ENDLESS_JSON_STRING_RECOURSION)
315: throw Exception(PARSER_RUNTIME, 0, "call canceled - endless json recursion detected");
316: }
317: ~Json_string_recoursion() {
318: if(foptions.json_string_recoursion)
319: foptions.json_string_recoursion--;
320: }
321: };
322:
1.21 moko 323: const String& value_json_string(String::Body key, Value& v, Json_options& options);
1.6 misha 324:
1.21 moko 325: const String* Json_options::hash_json_string(HashStringValue &hash) {
1.6 misha 326: if(!hash.count())
1.21 moko 327: return new String("{}", String::L_AS_IS);
1.8 moko 328:
1.26 moko 329: Json_string_recoursion go_down(*this);
1.8 moko 330:
331: String& result = *new String("{\n", String::L_AS_IS);
332:
1.21 moko 333: if (indent){
1.8 moko 334:
335: String *delim=NULL;
1.26 moko 336: indent=get_indent(json_string_recoursion);
1.8 moko 337: for(HashStringValue::Iterator i(hash); i; i.next() ){
338: if (delim){
339: result << *delim;
340: } else {
1.21 moko 341: result << indent << "\"";
342: delim = new String(",\n", String::L_AS_IS); *delim << indent << "\"";
1.8 moko 343: }
1.21 moko 344: result << String(i.key(), String::L_JSON) << "\":" << value_json_string(i.key(), *i.value(), *this);
1.8 moko 345: }
1.26 moko 346: result << "\n" << (indent=get_indent(json_string_recoursion-1)) << "}";
1.6 misha 347:
1.8 moko 348: } else {
349:
350: bool need_delim=false;
351: for(HashStringValue::Iterator i(hash); i; i.next() ){
352: result << (need_delim ? ",\n\"" : "\"");
1.21 moko 353: result << String(i.key(), String::L_JSON) << "\":" << value_json_string(i.key(), *i.value(), *this);
1.8 moko 354: need_delim=true;
355: }
356: result << "\n}";
1.6 misha 357:
358: }
359:
1.21 moko 360: return &result;
1.6 misha 361: }
362:
1.21 moko 363: static bool based_on(HashStringValue::key_type key, HashStringValue::value_type /*value*/, Value* v) {
1.15 misha 364: return v->is(key.cstr());
365: }
1.26 moko 366:
1.21 moko 367: const String& value_json_string(String::Body key, Value& v, Json_options& options) {
368: if(options.methods) {
369: Value* method=options.methods->get(v.type());
370: if(!method){
371: method=options.methods->first_that<Value*>(based_on, &v);
1.31 misha 372: options.methods->put(v.type(), method ? method : VVoid::get());
1.21 moko 373: }
374: if(method && !method->is_void()) {
1.6 misha 375: Junction* junction=method->get_junction();
1.21 moko 376: VMethodFrame frame(*junction->method, options.r->method_frame, junction->self);
1.6 misha 377:
1.26 moko 378: HashStringValue* params_hash=options.params && options.indent ? options.params->get_hash() : NULL;
1.27 moko 379: Temp_hash_value<HashStringValue, Value*> indent(params_hash, "indent", new VString(*new String(options.indent, String::L_AS_IS)));
1.26 moko 380:
1.21 moko 381: Value *params[]={new VString(*new String(key, String::L_JSON)), &v, options.params ? options.params : VVoid::get()};
1.13 moko 382: frame.store_params(params, 3);
1.6 misha 383:
1.21 moko 384: options.r->execute_method(frame);
1.6 misha 385:
386: return frame.result().as_string();
387: }
1.15 misha 388: }
1.6 misha 389:
1.21 moko 390: options.key=key;
1.6 misha 391: return *v.get_json_string(options);
392: }
393:
394: static void _string(Request& r, MethodParams& params) {
395: Json_options json(&r);
396:
397: if(params.count() == 2)
398: if(HashStringValue* options=params.as_hash(1)) {
399: json.params=params.get(1);
400: HashStringValue* methods=new HashStringValue();
401: int valid_options=0;
1.14 misha 402: HashStringValue* vvalue;
1.6 misha 403: for(HashStringValue::Iterator i(*options); i; i.next() ){
404: String::Body key=i.key();
405: Value* value=i.value();
406: if(key == "skip-unknown"){
407: json.skip_unknown=r.process_to_value(*value).as_bool();
408: valid_options++;
409: } else if(key == "date" && value->is_string()){
410: const String& svalue=value->as_string();
411: if(!json.set_date_format(svalue))
412: throw Exception(PARSER_RUNTIME, &svalue, "must be 'sql-string', 'gmt-string' or 'unix-timestamp'");
413: valid_options++;
1.8 moko 414: } else if(key == "indent"){
1.26 moko 415: if(value->is_string()){
416: json.indent=value->as_string().cstr();
417: json.json_string_recoursion=strlen(json.indent);
418: } else json.indent=r.process_to_value(*value).as_bool() ? "" : NULL;
1.8 moko 419: valid_options++;
1.6 misha 420: } else if(key == "table" && value->is_string()){
421: const String& svalue=value->as_string();
422: if(!json.set_table_format(svalue))
1.13 moko 423: throw Exception(PARSER_RUNTIME, &svalue, "must be 'array', 'object' or 'compact'");
1.6 misha 424: valid_options++;
425: } else if(key == "file" && value->is_string()){
426: const String& svalue=value->as_string();
427: if(!json.set_file_format(svalue))
1.19 misha 428: throw Exception(PARSER_RUNTIME, &svalue, "must be 'base64', 'text' or 'stat'");
1.6 misha 429: valid_options++;
1.32 ! misha 430: } else if(key == "void" && value->is_string()){
! 431: const String& svalue=value->as_string();
! 432: if(!json.set_void_format(svalue))
! 433: throw Exception(PARSER_RUNTIME, &svalue, "must be 'string' or 'null'");
! 434: valid_options++;
1.14 misha 435: #ifdef XML
436: } else if(key == "xdoc" && (vvalue = value->get_hash())){
1.24 moko 437: json.xdoc_options=new XDocOutputOptions();
438: json.xdoc_options->append(r, vvalue);
1.14 misha 439: valid_options++;
440: #endif
1.6 misha 441: } else if(Junction* junction=value->get_junction()){
1.13 moko 442: if(!junction->method || !junction->method->params_names || junction->method->params_names->count() != 3)
443: throw Exception(PARSER_RUNTIME, 0, "$.%s must be parser method with 3 parameters", key.cstr());
1.6 misha 444: methods->put(key, value);
445: valid_options++;
446: }
447: }
1.22 moko 448:
1.6 misha 449: if(valid_options!=options->count())
450: throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
1.22 moko 451:
452: // special handling for $._default
453: if(VHash* vhash=static_cast<VHash*>(params[1].as(VHASH_TYPE)))
454: if(Value* value=vhash->get_default()) {
455: Junction* junction=value->get_junction();
456: if(!junction || !junction->method || !junction->method->params_names || junction->method->params_names->count() != 3)
457: throw Exception(PARSER_RUNTIME, 0, "$.%s must be parser method with 3 parameters", HASH_DEFAULT_ELEMENT_NAME);
458: json.default_method=value;
459: }
460:
1.6 misha 461: if(methods->count())
462: json.methods=methods;
463: }
1.14 misha 464:
1.21 moko 465: const String& result_string=value_json_string(String::Body(), params[0], json);
1.28 moko 466: String::Body result_body=result_string.cstr_to_string_body_untaint(String::L_JSON, r.connection(false), &r.charsets);
1.13 moko 467: r.write_pass_lang(*new String(result_body, String::L_AS_IS));
1.6 misha 468: }
469:
1.1 misha 470: // constructor
471:
472: MJson::MJson(): Methoded("json") {
473: add_native_method("parse", Method::CT_STATIC, _parse, 1, 2);
1.6 misha 474:
475: add_native_method("string", Method::CT_ANY, _string, 1, 2);
1.1 misha 476: }
E-mail: