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