Annotation of parser3/src/classes/json.C, revision 1.59
1.1 misha 1: /** @file
2: Parser: @b json parser class.
3:
1.57 moko 4: Copyright (c) 2000-2023 Art. Lebedev Studio (http://www.artlebedev.com)
5: Authors: Konstantin Morshnev <moko@design.ru>
1.1 misha 6: */
7:
8: #include "classes.h"
9: #include "pa_vmethod_frame.h"
10:
11: #include "pa_request.h"
12: #include "pa_vbool.h"
1.59 ! moko 13: //#include "pa_varray.h"
1.1 misha 14:
15: #include "pa_charset.h"
16: #include "pa_charsets.h"
1.29 moko 17: #include "pa_json.h"
1.1 misha 18:
1.14 misha 19: #ifdef XML
20: #include "pa_vxdoc.h"
21: #endif
22:
1.59 ! moko 23: volatile const char * IDENT_JSON_C="$Id: json.C,v 1.58 2023/10/01 15:14:44 moko Exp $";
1.17 moko 24:
1.1 misha 25: // class
26:
27: class MJson: public Methoded {
28: public:
29: MJson();
30: };
31:
32: // global variable
33:
1.41 moko 34: DECLARE_CLASS_VAR(json, new MJson);
1.1 misha 35:
36: // methods
1.53 moko 37: struct Json : public PA_Allocated {
1.59 ! moko 38: Stack<VHashBase*> stack;
1.3 moko 39: Stack<String*> key_stack;
1.1 misha 40:
1.3 moko 41: String* key;
1.1 misha 42: Value* result;
43:
1.16 misha 44: Junction* hook_object;
45: Junction* hook_array;
1.3 moko 46: Request* request;
47:
1.1 misha 48: Charset *charset;
1.23 moko 49: String::Language taint;
50:
1.1 misha 51: bool handle_double;
1.30 misha 52: bool handle_int;
1.59 ! moko 53: bool handle_array;
1.4 moko 54: enum Distinct { D_EXCEPTION, D_FIRST, D_LAST, D_ALL } distinct;
1.3 moko 55:
1.23 moko 56: Json(Charset* acharset): stack(), key_stack(), key(NULL), result(NULL), hook_object(NULL), hook_array(NULL),
1.30 misha 57: request(NULL), charset(acharset), taint(String::L_TAINTED), handle_double(true), handle_int(true),
1.59 ! moko 58: handle_array(true), distinct(D_EXCEPTION){}
1.4 moko 59:
60: bool set_distinct(const String &value){
61: if (value == "first") distinct = D_FIRST;
62: else if (value == "last") distinct = D_LAST;
63: else if (value == "all") distinct = D_ALL;
64: else return false;
65: return true;
66: }
1.59 ! moko 67:
! 68: bool set_handle_array(const String &value){
! 69: if (value == "array") handle_array = true;
! 70: else if (value == "hash") handle_array = false;
! 71: else return false;
! 72: return true;
! 73: }
1.1 misha 74: };
75:
76: static void set_json_value(Json *json, Value *value){
1.59 ! moko 77: VHashBase *top = json->stack.top_value();
1.3 moko 78: if(json->key == NULL){
1.59 ! moko 79: top->add(value);
1.1 misha 80: } else {
1.4 moko 81: switch (json->distinct){
82: case Json::D_EXCEPTION:
83: if (top->hash().put_dont_replace(*json->key, value))
84: throw Exception(PARSER_RUNTIME, json->key, "duplicate key");
85: break;
86: case Json::D_FIRST:
87: top->hash().put_dont_replace(*json->key, value);
88: break;
89: case Json::D_LAST:
90: top->hash().put(*json->key, value);
91: break;
92: case Json::D_ALL:
93: if (top->hash().put_dont_replace(*json->key, value)){
94: for(int i=2;;i++){
95: String key;
96: key << *json->key << "_" << format(i, 0);
97: if (!top->hash().put_dont_replace(key, value)) break;
98: }
99: }
100: break;
101: }
1.3 moko 102: json->key=NULL;
1.1 misha 103: }
104: }
105:
1.25 moko 106: String* json_string(Json *json, const char *value, uint32_t length){
1.3 moko 107: String::C result = json->charset !=NULL ?
1.44 moko 108: Charset::transcode(String::C(value, length), pa_UTF8_charset, *json->charset) :
1.25 moko 109: String::C(pa_strdup(value, length), length);
1.39 moko 110: return new String(result, json->taint);
1.1 misha 111: }
112:
1.3 moko 113: static Value *json_hook(Request &r, Junction *hook, String* key, Value* value){
1.10 moko 114: Value *params[]={new VString(key ? *key : String::Empty), value};
1.49 moko 115: METHOD_FRAME_ACTION(*hook->method, r.method_frame, hook->self, {
116: frame.store_params(params, 2);
117: r.call(frame);
118: return &frame.result();
119: });
1.1 misha 120: }
121:
1.25 moko 122: static int json_callback(Json *json, int type, const char *value, uint32_t length)
1.1 misha 123: {
124: switch(type) {
1.25 moko 125: case JSON_OBJECT_BEGIN:{
1.4 moko 126: VHash *v = new VHash();
1.16 misha 127: if (json->hook_object){
1.1 misha 128: json->key_stack.push(json->key);
1.16 misha 129: json->key=NULL;
1.1 misha 130: } else {
131: if (json->stack.count()) set_json_value(json, v);
132: }
133: json->stack.push(v);
134: break;
135: }
1.25 moko 136: case JSON_OBJECT_END:{
1.16 misha 137: if (json->hook_object){
1.3 moko 138: String* key = json->key_stack.pop();
1.16 misha 139: json->result = json_hook(*json->request, json->hook_object, key, json->stack.pop());
1.1 misha 140:
141: if (json->stack.count()){
142: json->key = key;
143: set_json_value(json, json->result);
144: }
145: } else {
146: json->result = json->stack.pop();
147: }
148: break;
149: }
1.25 moko 150: case JSON_ARRAY_BEGIN:{
1.16 misha 151: if (json->hook_array){
152: json->key_stack.push(json->key);
153: json->key=NULL;
1.59 ! moko 154: json->stack.push(new VHash);
1.16 misha 155: } else {
1.59 ! moko 156: VHashBase *v = new VHash;
! 157: // VHashBase *v = json->handle_array ? (VHashBase *)new VArray : (VHashBase *)new VHash;
1.16 misha 158: if (json->stack.count()) set_json_value(json, v);
1.59 ! moko 159: json->stack.push(v);
1.16 misha 160: }
1.1 misha 161: break;
162: }
1.25 moko 163: case JSON_ARRAY_END:
1.12 moko 164: // libjson supports array at top level, we too
1.16 misha 165: if (json->hook_array){
166: String* key = json->key_stack.pop();
167: json->result = json_hook(*json->request, json->hook_array, key, json->stack.pop());
168:
169: if (json->stack.count()){
170: json->key = key;
171: set_json_value(json, json->result);
172: }
173: } else {
174: json->result = json->stack.pop();
175: }
1.1 misha 176: break;
1.25 moko 177: case JSON_KEY:
178: json->key = json_string(json, value, length);
1.16 misha 179: break;
1.25 moko 180: case JSON_INT:
1.30 misha 181: if (json->handle_int){
182: set_json_value(json, new VDouble( json_string(json, value, length)->as_double() ));
183: } else {
184: // JSON_STRING
185: set_json_value(json, new VString(*json_string(json, value, length)));
186: }
1.1 misha 187: break;
1.25 moko 188: case JSON_FLOAT:
1.1 misha 189: if (json->handle_double){
1.25 moko 190: set_json_value(json, new VDouble( json_string(json, value, length)->as_double() ));
1.1 misha 191: break;
1.25 moko 192: } // else is JSON_STRING
193: case JSON_STRING:
194: set_json_value(json, new VString(*json_string(json, value, length)));
1.1 misha 195: break;
1.25 moko 196: case JSON_NULL:
1.18 moko 197: set_json_value(json, VVoid::get());
1.1 misha 198: break;
1.25 moko 199: case JSON_TRUE:
1.1 misha 200: set_json_value(json, &VBool::get(true));
201: break;
1.25 moko 202: case JSON_FALSE:
1.1 misha 203: set_json_value(json, &VBool::get(false));
1.25 moko 204: break;
1.1 misha 205: }
1.25 moko 206: return 0;
1.1 misha 207: }
208:
1.5 moko 209: static const char* json_error_message(int error_code){
210: static const char* error_messages[] = {
1.1 misha 211: NULL,
1.25 moko 212: "out of memory",
213: "bad character",
214: "stack empty",
215: "pop unexpected mode",
216: "nesting limit",
217: "data limit",
218: "comment not allowed by config",
1.35 moko 219: "unexpected character",
1.25 moko 220: "missing unicode low surrogate",
221: "unexpected unicode low surrogate",
222: "error comma out of structure",
223: "error in a callback"
1.1 misha 224: };
225: return error_messages[error_code];
226: }
227:
1.23 moko 228: extern String::Language get_untaint_lang(const String& lang_name);
229:
1.35 moko 230: #define SOURCE_MAX_LEN 60
231:
232: void json_exception_with_source(Request& r, const char* msg, const char* json, int offset){
233: int i;
234:
235: int line=0;
236: int start=0;
237: int end=strlen(json);
238:
239: if(offset>end)
240: offset=end;
241:
242: for(i = 0; i < offset; i++){
243: if(json[i]=='\n'){
244: line++;
245: }
246: }
247:
248: if(offset > SOURCE_MAX_LEN/2)
249: start = offset - SOURCE_MAX_LEN/2;
250:
251: for(i = offset-1; i>=start; i--){
252: if(json[i]=='\n'){
253: start=i+1;
254: break;
255: }
256: }
257:
258: if(start+SOURCE_MAX_LEN < end)
259: end=start+SOURCE_MAX_LEN;
260:
261: for(i = offset+1; i<end; i++){
262: if(json[i]=='\n'){
263: end=i;
264: break;
265: }
266: }
267:
268: char *source = pa_strdup(json+start, end-start);
269: int source_offset = offset-start;
270:
271: if(source[source_offset]=='\n')
272: source[source_offset]=' ';
273:
274: for(i = 0; i < source_offset; i++){
275: if(source[i]=='\t'){
276: source[i]=' ';
277: }
278: }
279:
280: if(r.charsets.source().isUTF8()){
281: source=(char *)fixUTF8(source);
282: if(source_offset>0){
283: String s_source(pa_strdup(source,source_offset));
284: source_offset=s_source.length(r.charsets.source());
285: }
286: }
287:
288: throw Exception("json.parse", 0, "%s at line %d\n%s\n%*s", msg, line+1, source, source_offset+1, "^");
289: }
290:
1.1 misha 291: static void _parse(Request& r, MethodParams& params) {
1.3 moko 292: const String& json_string=params.as_string(0, "json must be string");
293:
294: Json json(r.charsets.source().isUTF8() ? NULL : &(r.charsets.source()));
1.1 misha 295:
1.25 moko 296: json_config config = {
297: 0, // buffer_initial_size
1.26 moko 298: 128, // max_nesting
1.25 moko 299: 0, // max_data
300: 1, // allow_c_comments
301: 1, // allow_yaml_comments
302: pa_malloc,
303: pa_realloc,
304: pa_free
305: };
1.1 misha 306:
307: if(params.count() == 2)
308: if(HashStringValue* options=params.as_hash(1)) {
309: int valid_options=0;
310: if(Value* value=options->get("depth")) {
1.46 moko 311: config.max_nesting=r.process(*value).as_int();
1.1 misha 312: valid_options++;
313: }
314: if(Value* value=options->get("double")) {
1.46 moko 315: json.handle_double=r.process(*value).as_bool();
1.4 moko 316: valid_options++;
317: }
1.30 misha 318: if(Value* value=options->get("int")) {
1.46 moko 319: json.handle_int=r.process(*value).as_bool();
1.30 misha 320: valid_options++;
321: }
1.4 moko 322: if(Value* value=options->get("distinct")) {
323: const String& sdistinct=value->as_string();
324: if (!json.set_distinct(sdistinct))
325: throw Exception(PARSER_RUNTIME, &sdistinct, "must be 'first', 'last' or 'all'");
1.1 misha 326: valid_options++;
327: }
1.23 moko 328: if(Value* value=options->get("taint")) {
329: json.taint=get_untaint_lang(value->as_string());
330: valid_options++;
331: }
1.1 misha 332: if(Value* value=options->get("object")) {
1.16 misha 333: json.hook_object=value->get_junction();
1.3 moko 334: json.request=&r;
1.49 moko 335: if (!json.hook_object || !json.hook_object->method || !json.hook_object->method->params_names || !(json.hook_object->method->params_count == 2))
1.1 misha 336: throw Exception(PARSER_RUNTIME, 0, "$.object must be parser method with 2 parameters");
337: valid_options++;
338: }
1.16 misha 339: if(Value* value=options->get("array")) {
1.59 ! moko 340: if(value->get_string()){
! 341: const String& sarray=value->as_string();
! 342: if (!json.set_handle_array(sarray))
! 343: throw Exception(PARSER_RUNTIME, &sarray, "$.array must be parser method with 2 parameters or 'array' or 'hash'");
! 344: } else {
! 345: json.hook_array=value->get_junction();
! 346: json.request=&r;
! 347: if (!json.hook_array || !json.hook_array->method || !json.hook_array->method->params_names || !(json.hook_array->method->params_count == 2))
! 348: throw Exception(PARSER_RUNTIME, 0, "$.array must be parser method with 2 parameters or 'array' or 'hash'");
! 349: }
1.16 misha 350: valid_options++;
351: }
1.1 misha 352: if(valid_options!=options->count())
353: throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
354: }
355:
1.28 moko 356: const String::Body json_body = json_string.cstr_to_string_body_untaint(String::L_JSON, r.connection(false), &r.charsets);
1.44 moko 357: const char *json_cstr = json.charset != NULL ? Charset::transcode(json_body, *json.charset, pa_UTF8_charset).cstr() : json_body.cstr();
1.1 misha 358:
1.25 moko 359: json_parser parser;
360: if(int result = json_parser_init(&parser, &config, (json_parser_callback)&json_callback, &json))
361: throw Exception("json.parse", 0, "%s", json_error_message(result));
362:
1.36 moko 363: if(!*json_cstr)
364: throw Exception("json.parse", 0, "empty string is not valid json");
365:
366: const char *first_quote=strchr(json_cstr,'"');
367: if(first_quote && first_quote>json_cstr && *(--first_quote) == '\\')
368: json_exception_with_source(r, "illegal quote escape, json may be tainted", json_cstr, first_quote-json_cstr);
369:
1.25 moko 370: uint32_t processed;
371: if(int result = json_parser_string(&parser, json_cstr, strlen(json_cstr), &processed))
1.35 moko 372: json_exception_with_source(r, json_error_message(result), json_cstr, processed);
1.3 moko 373:
1.25 moko 374: if (!json_parser_is_done(&parser))
1.35 moko 375: json_exception_with_source(r, "unexpected end of json data", json_cstr, processed);
376:
1.25 moko 377: json_parser_free(&parser);
1.1 misha 378:
1.48 moko 379: if (json.result) r.write(*json.result);
1.1 misha 380: }
381:
1.26 moko 382: const uint ANTI_ENDLESS_JSON_STRING_RECOURSION=128;
383:
1.8 moko 384: char *get_indent(uint level){
385: static char* cache[ANTI_ENDLESS_JSON_STRING_RECOURSION]={};
386: if (!cache[level]){
1.56 moko 387: char *result = static_cast<char*>(pa_malloc_atomic(level+1));
1.8 moko 388: memset(result, '\t', level);
1.9 moko 389: result[level]='\0';
1.8 moko 390: return cache[level]=result;
391: }
392: return cache[level];
393: }
394:
1.56 moko 395: String *get_delim(uint level){
396: static String* cache[ANTI_ENDLESS_JSON_STRING_RECOURSION]={};
397:
398: if (!cache[level]){
399: char *result = static_cast<char*>(pa_malloc_atomic(level+2+1+1));
400: result[0]=',';
401: result[1]='\n';
402: memset(result+2, '\t', level);
403: result[level+2]='"';
404: result[level+3]='\0';
405: return cache[level] = new String(result, String::L_AS_IS);
406: }
407: return cache[level];
408: }
409:
1.59 ! moko 410: String *get_array_delim(uint level){
! 411: static String* cache[ANTI_ENDLESS_JSON_STRING_RECOURSION]={};
! 412:
! 413: if (!cache[level]){
! 414: char *result = static_cast<char*>(pa_malloc_atomic(level+2+1));
! 415: result[0]=',';
! 416: result[1]='\n';
! 417: memset(result+2, '\t', level);
! 418: result[level+2]='\0';
! 419: return cache[level] = new String(result, String::L_AS_IS);
! 420: }
! 421: return cache[level];
! 422: }
! 423:
1.26 moko 424: class Json_string_recoursion {
425: Json_options& foptions;
426: public:
427: Json_string_recoursion(Json_options& aoptions) : foptions(aoptions) {
428: if(++foptions.json_string_recoursion==ANTI_ENDLESS_JSON_STRING_RECOURSION)
429: throw Exception(PARSER_RUNTIME, 0, "call canceled - endless json recursion detected");
430: }
431: ~Json_string_recoursion() {
432: if(foptions.json_string_recoursion)
433: foptions.json_string_recoursion--;
434: }
435: };
436:
1.21 moko 437: const String& value_json_string(String::Body key, Value& v, Json_options& options);
1.6 misha 438:
1.37 moko 439: const String* Json_options::hash_json_string(HashStringValue *hash) {
440: if(!hash || !hash->count())
1.21 moko 441: return new String("{}", String::L_AS_IS);
1.8 moko 442:
1.26 moko 443: Json_string_recoursion go_down(*this);
1.8 moko 444:
445: String& result = *new String("{\n", String::L_AS_IS);
446:
1.21 moko 447: if (indent){
1.8 moko 448:
449: String *delim=NULL;
1.26 moko 450: indent=get_indent(json_string_recoursion);
1.37 moko 451: for(HashStringValue::Iterator i(*hash); i; i.next() ){
1.8 moko 452: if (delim){
453: result << *delim;
454: } else {
1.21 moko 455: result << indent << "\"";
1.56 moko 456: delim = get_delim(json_string_recoursion);
1.8 moko 457: }
1.21 moko 458: result << String(i.key(), String::L_JSON) << "\":" << value_json_string(i.key(), *i.value(), *this);
1.8 moko 459: }
1.26 moko 460: result << "\n" << (indent=get_indent(json_string_recoursion-1)) << "}";
1.6 misha 461:
1.8 moko 462: } else {
463:
464: bool need_delim=false;
1.37 moko 465: for(HashStringValue::Iterator i(*hash); i; i.next() ){
1.8 moko 466: result << (need_delim ? ",\n\"" : "\"");
1.21 moko 467: result << String(i.key(), String::L_JSON) << "\":" << value_json_string(i.key(), *i.value(), *this);
1.8 moko 468: need_delim=true;
469: }
470: result << "\n}";
1.6 misha 471:
472: }
473:
1.21 moko 474: return &result;
1.6 misha 475: }
476:
1.59 ! moko 477: const String* Json_options::array_json_string(ArrayValue *array) {
! 478: if(!array || !array->count())
! 479: return new String("[]", String::L_AS_IS);
! 480:
! 481: Json_string_recoursion go_down(*this);
! 482:
! 483: String& result = *new String("[\n", String::L_AS_IS);
! 484:
! 485: if (indent){
! 486:
! 487: String *delim=NULL;
! 488: indent=get_indent(json_string_recoursion);
! 489: for(ArrayValue::Iterator i(*array); i; i.next() ){
! 490: if (delim){
! 491: result << *delim;
! 492: } else {
! 493: result << indent;
! 494: delim = get_array_delim(json_string_recoursion);
! 495: }
! 496: result << value_json_string(i.key(), i.value() ? *i.value() : *VVoid::get(), *this);
! 497: }
! 498: result << "\n" << (indent=get_indent(json_string_recoursion-1)) << "]";
! 499:
! 500: } else {
! 501:
! 502: bool need_delim=false;
! 503: for(ArrayValue::Iterator i(*array); i; i.next() ){
! 504: if(need_delim) result << ",\n";
! 505: result << value_json_string(i.key(), i.value() ? *i.value() : *VVoid::get(), *this);
! 506: need_delim=true;
! 507: }
! 508: result << "\n]";
! 509:
! 510: }
! 511:
! 512: return &result;
! 513: }
! 514:
! 515: const String* Json_options::array_compact_json_string(ArrayValue *array) {
! 516: if(!array || !array->count())
! 517: return new String("[]", String::L_AS_IS);
! 518:
! 519: Json_string_recoursion go_down(*this);
! 520:
! 521: String& result = *new String("[\n", String::L_AS_IS);
! 522:
! 523: if (indent){
! 524:
! 525: String *delim=NULL;
! 526: indent=get_indent(json_string_recoursion);
! 527: for(ArrayValue::Iterator i(*array); i; i.next() ){
! 528: if (i.value()){
! 529: if (delim){
! 530: result << *delim;
! 531: } else {
! 532: result << indent;
! 533: delim = get_array_delim(json_string_recoursion);
! 534: }
! 535: result << value_json_string(i.key(), *i.value(), *this);
! 536: }
! 537: }
! 538: result << "\n" << (indent=get_indent(json_string_recoursion-1)) << "]";
! 539:
! 540: } else {
! 541:
! 542: bool need_delim=false;
! 543: for(ArrayValue::Iterator i(*array); i; i.next() ){
! 544: if (i.value()){
! 545: if(need_delim) result << ",\n";
! 546: result << value_json_string(i.key(), *i.value(), *this);
! 547: need_delim=true;
! 548: }
! 549: }
! 550: result << "\n]";
! 551:
! 552: }
! 553:
! 554: return &result;
! 555: }
! 556:
1.21 moko 557: static bool based_on(HashStringValue::key_type key, HashStringValue::value_type /*value*/, Value* v) {
1.15 misha 558: return v->is(key.cstr());
559: }
1.26 moko 560:
1.21 moko 561: const String& value_json_string(String::Body key, Value& v, Json_options& options) {
562: if(options.methods) {
563: Value* method=options.methods->get(v.type());
564: if(!method){
565: method=options.methods->first_that<Value*>(based_on, &v);
1.31 misha 566: options.methods->put(v.type(), method ? method : VVoid::get());
1.21 moko 567: }
568: if(method && !method->is_void()) {
1.6 misha 569: Junction* junction=method->get_junction();
1.26 moko 570: HashStringValue* params_hash=options.params && options.indent ? options.params->get_hash() : NULL;
1.27 moko 571: Temp_hash_value<HashStringValue, Value*> indent(params_hash, "indent", new VString(*new String(options.indent, String::L_AS_IS)));
1.26 moko 572:
1.21 moko 573: Value *params[]={new VString(*new String(key, String::L_JSON)), &v, options.params ? options.params : VVoid::get()};
1.6 misha 574:
1.49 moko 575: METHOD_FRAME_ACTION(*junction->method, options.r->method_frame, junction->self, {
576: frame.store_params(params, 3);
577: options.r->call(frame);
578: return frame.result().as_string();
579: });
1.6 misha 580: }
1.15 misha 581: }
1.6 misha 582:
1.21 moko 583: options.key=key;
1.6 misha 584: return *v.get_json_string(options);
585: }
586:
587: static void _string(Request& r, MethodParams& params) {
588: Json_options json(&r);
589:
590: if(params.count() == 2)
591: if(HashStringValue* options=params.as_hash(1)) {
1.47 moko 592: json.params=¶ms[1];
1.6 misha 593: HashStringValue* methods=new HashStringValue();
594: int valid_options=0;
1.14 misha 595: HashStringValue* vvalue;
1.6 misha 596: for(HashStringValue::Iterator i(*options); i; i.next() ){
597: String::Body key=i.key();
598: Value* value=i.value();
599: if(key == "skip-unknown"){
1.46 moko 600: json.skip_unknown=r.process(*value).as_bool();
1.6 misha 601: valid_options++;
1.50 moko 602: } else if(key == "one-line"){
603: json.one_line=r.process(*value).as_bool();
604: valid_options++;
1.6 misha 605: } else if(key == "date" && value->is_string()){
606: const String& svalue=value->as_string();
607: if(!json.set_date_format(svalue))
1.38 moko 608: throw Exception(PARSER_RUNTIME, &svalue, "must be 'sql-string', 'gmt-string', 'iso-string' or 'unix-timestamp'");
1.6 misha 609: valid_options++;
1.8 moko 610: } else if(key == "indent"){
1.26 moko 611: if(value->is_string()){
612: json.indent=value->as_string().cstr();
613: json.json_string_recoursion=strlen(json.indent);
1.46 moko 614: } else json.indent=r.process(*value).as_bool() ? "" : NULL;
1.8 moko 615: valid_options++;
1.6 misha 616: } else if(key == "table" && value->is_string()){
617: const String& svalue=value->as_string();
618: if(!json.set_table_format(svalue))
1.13 moko 619: throw Exception(PARSER_RUNTIME, &svalue, "must be 'array', 'object' or 'compact'");
1.6 misha 620: valid_options++;
1.59 ! moko 621: } else if(key == "array" && value->is_string()){
! 622: const String& svalue=value->as_string();
! 623: if(!json.set_array_format(svalue))
! 624: throw Exception(PARSER_RUNTIME, &svalue, "must be 'array', 'object' or 'compact'");
! 625: valid_options++;
1.6 misha 626: } else if(key == "file" && value->is_string()){
627: const String& svalue=value->as_string();
628: if(!json.set_file_format(svalue))
1.19 misha 629: throw Exception(PARSER_RUNTIME, &svalue, "must be 'base64', 'text' or 'stat'");
1.6 misha 630: valid_options++;
1.32 misha 631: } else if(key == "void" && value->is_string()){
632: const String& svalue=value->as_string();
633: if(!json.set_void_format(svalue))
634: throw Exception(PARSER_RUNTIME, &svalue, "must be 'string' or 'null'");
635: valid_options++;
1.14 misha 636: #ifdef XML
637: } else if(key == "xdoc" && (vvalue = value->get_hash())){
1.24 moko 638: json.xdoc_options=new XDocOutputOptions();
639: json.xdoc_options->append(r, vvalue);
1.14 misha 640: valid_options++;
641: #endif
1.6 misha 642: } else if(Junction* junction=value->get_junction()){
1.49 moko 643: if(!junction->method || !junction->method->params_names || junction->method->params_count != 3)
1.13 moko 644: throw Exception(PARSER_RUNTIME, 0, "$.%s must be parser method with 3 parameters", key.cstr());
1.6 misha 645: methods->put(key, value);
646: valid_options++;
647: }
648: }
1.22 moko 649:
1.6 misha 650: if(valid_options!=options->count())
651: throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
1.22 moko 652:
653: // special handling for $._default
1.43 moko 654: if(VHashBase* vhash=static_cast<VHashBase*>(params[1].as(VHASH_TYPE)))
1.22 moko 655: if(Value* value=vhash->get_default()) {
1.34 misha 656: if(!value->is_string()){
1.43 moko 657: Junction* junction=value->get_junction();
1.49 moko 658: if(!junction || !junction->method || !junction->method->params_names || junction->method->params_count != 3)
1.42 moko 659: throw Exception(PARSER_RUNTIME, 0, "$._default must be string or parser method with 3 parameters");
1.34 misha 660: }
1.22 moko 661: json.default_method=value;
662: }
663:
1.6 misha 664: if(methods->count())
665: json.methods=methods;
666: }
1.14 misha 667:
1.46 moko 668: const String& result_string=value_json_string(String::Body(), r.process(params[0]), json);
1.28 moko 669: String::Body result_body=result_string.cstr_to_string_body_untaint(String::L_JSON, r.connection(false), &r.charsets);
1.50 moko 670: if(json.one_line){
671: char *result=result_body.cstrm();
672: for(char *c=result;*c;c++)
673: if(*c=='\n')
674: *c=' ';
675: result_body=result;
676: }
1.48 moko 677: r.write(*new String(result_body, String::L_AS_IS));
1.50 moko 678: }
1.6 misha 679:
1.1 misha 680: // constructor
681:
682: MJson::MJson(): Methoded("json") {
683: add_native_method("parse", Method::CT_STATIC, _parse, 1, 2);
1.6 misha 684:
1.58 moko 685: add_native_method("string", Method::CT_STATIC, _string, 1, 2);
1.1 misha 686: }
E-mail: