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