|
|
| version 1.46, 2016/10/04 13:23:46 | version 1.69, 2025/12/17 20:34:20 |
|---|---|
| Line 1 | Line 1 |
| /** @file | /** @file |
| Parser: @b json parser class. | Parser: @b json parser class. |
| Copyright (c) 2000-2015 Art. Lebedev Studio (http://www.artlebedev.com) | Copyright (c) 2000-2024 Art. Lebedev Studio (http://www.artlebedev.com) |
| Authors: Konstantin Morshnev <moko@design.ru> | |
| */ | */ |
| #include "classes.h" | #include "classes.h" |
| Line 9 | Line 10 |
| #include "pa_request.h" | #include "pa_request.h" |
| #include "pa_vbool.h" | #include "pa_vbool.h" |
| #include "pa_varray.h" | |
| #include "pa_charset.h" | #include "pa_charset.h" |
| #include "pa_charsets.h" | #include "pa_charsets.h" |
| Line 25 volatile const char * IDENT_JSON_C="$Id$ | Line 27 volatile const char * IDENT_JSON_C="$Id$ |
| class MJson: public Methoded { | class MJson: public Methoded { |
| public: | public: |
| MJson(); | MJson(); |
| override Value* get_element(const String&); | |
| override const VJunction* put_element(const String&, Value*); | |
| }; | }; |
| // global variable | // global variable |
| DECLARE_CLASS_VAR(json, new MJson); | DECLARE_CLASS_VAR(json, new MJson); |
| bool handle_array_default=true; | |
| // methods | // methods |
| struct Json { | struct Json : public PA_Allocated { |
| Stack<VHash*> stack; | Stack<VHashBase*> stack; |
| Stack<String*> key_stack; | Stack<String*> key_stack; |
| String* key; | String* key; |
| Line 48 struct Json { | Line 55 struct Json { |
| bool handle_double; | bool handle_double; |
| bool handle_int; | bool handle_int; |
| bool handle_array; | |
| enum Distinct { D_EXCEPTION, D_FIRST, D_LAST, D_ALL } distinct; | enum Distinct { D_EXCEPTION, D_FIRST, D_LAST, D_ALL } distinct; |
| Json(Charset* acharset): stack(), key_stack(), key(NULL), result(NULL), hook_object(NULL), hook_array(NULL), | Json(Charset* acharset): stack(), key_stack(), key(NULL), result(NULL), hook_object(NULL), hook_array(NULL), |
| request(NULL), charset(acharset), taint(String::L_TAINTED), handle_double(true), handle_int(true), | request(NULL), charset(acharset), taint(String::L_TAINTED), handle_double(true), handle_int(true), |
| distinct(D_EXCEPTION){} | handle_array(handle_array_default), distinct(D_EXCEPTION){} |
| bool set_distinct(const String &value){ | bool set_distinct(const String &value){ |
| if (value == "first") distinct = D_FIRST; | if (value == "first") distinct = D_FIRST; |
| Line 61 struct Json { | Line 69 struct Json { |
| else return false; | else return false; |
| return true; | return true; |
| } | } |
| bool set_handle_array(const String &value){ | |
| if (value == "array") handle_array = true; | |
| else if (value == "hash") handle_array = false; | |
| else return false; | |
| return true; | |
| } | |
| }; | }; |
| Value* MJson::get_element(const String& aname) { | |
| if (aname=="array"){ | |
| return new VString(*new String(handle_array_default ? "array" : "hash")); | |
| } | |
| return Methoded::get_element(aname); | |
| } | |
| const VJunction* MJson::put_element(const String& aname, Value* avalue) { | |
| if (aname=="array"){ | |
| Json json(NULL); | |
| if (avalue->get_string()){ | |
| const String& sarray=avalue->as_string(); | |
| if (json.set_handle_array(sarray)){ | |
| handle_array_default=json.handle_array; | |
| return 0; | |
| } | |
| throw Exception(PARSER_RUNTIME, &sarray, "$json:array must be 'array' or 'hash'"); | |
| } | |
| throw Exception(PARSER_RUNTIME, 0, "$json:array must be 'array' or 'hash'"); | |
| } | |
| return Methoded::put_element(aname, avalue); | |
| } | |
| static void set_json_value(Json *json, Value *value){ | static void set_json_value(Json *json, Value *value){ |
| VHash *top = json->stack.top_value(); | VHashBase *top = json->stack.top_value(); |
| if(json->key == NULL){ | if(json->key == NULL){ |
| top->hash().put(String(format(top->get_hash()->count(), 0)), value); | top->add(value); |
| } else { | } else { |
| switch (json->distinct){ | switch (json->distinct){ |
| case Json::D_EXCEPTION: | case Json::D_EXCEPTION: |
| Line 83 static void set_json_value(Json *json, V | Line 123 static void set_json_value(Json *json, V |
| if (top->hash().put_dont_replace(*json->key, value)){ | if (top->hash().put_dont_replace(*json->key, value)){ |
| for(int i=2;;i++){ | for(int i=2;;i++){ |
| String key; | String key; |
| key << *json->key << "_" << format(i, 0); | key << *json->key << "_" << pa_uitoa(i); |
| if (!top->hash().put_dont_replace(key, value)) break; | if (!top->hash().put_dont_replace(key, value)) break; |
| } | } |
| } | } |
| Line 93 static void set_json_value(Json *json, V | Line 133 static void set_json_value(Json *json, V |
| } | } |
| } | } |
| String* json_string(Json *json, const char *value, uint32_t length){ | String* json_string(Json *json, const char *value){ |
| String::C result = json->charset !=NULL ? | /* do_callback_withbuf guarantees a null-terminated value */ |
| Charset::transcode(String::C(value, length), pa_UTF8_charset, *json->charset) : | if (size_t length = strlen(value)){ |
| String::C(pa_strdup(value, length), length); | String::C result = json->charset !=NULL ? |
| return new String(result, json->taint); | Charset::transcode(String::C(value, length), pa_UTF8_charset, *json->charset) : |
| String::C(pa_strdup(value, length), length); | |
| return new String(result, json->taint); | |
| } | |
| return (String*)&String::Empty; | |
| } | } |
| static Value *json_hook(Request &r, Junction *hook, String* key, Value* value){ | static Value *json_hook(Request &r, Junction *hook, String* key, Value* value){ |
| VMethodFrame frame(*hook->method, r.method_frame, hook->self); | |
| Value *params[]={new VString(key ? *key : String::Empty), value}; | Value *params[]={new VString(key ? *key : String::Empty), value}; |
| METHOD_FRAME_ACTION(*hook->method, r.method_frame, hook->self, { | |
| frame.store_params(params, 2); | frame.store_params(params, 2); |
| r.execute_method(frame); | r.call(frame); |
| return &frame.result(); | |
| return &frame.result(); | }); |
| } | } |
| static int json_callback(Json *json, int type, const char *value, uint32_t length) | static int json_callback(Json *json, int type, const char *value, uint32_t) |
| { | { |
| switch(type) { | switch(type) { |
| case JSON_OBJECT_BEGIN:{ | case JSON_OBJECT_BEGIN:{ |
| Line 139 static int json_callback(Json *json, int | Line 182 static int json_callback(Json *json, int |
| break; | break; |
| } | } |
| case JSON_ARRAY_BEGIN:{ | case JSON_ARRAY_BEGIN:{ |
| VHash *v = new VHash(); | |
| if (json->hook_array){ | if (json->hook_array){ |
| json->key_stack.push(json->key); | json->key_stack.push(json->key); |
| json->key=NULL; | json->key=NULL; |
| json->stack.push(new VHash); | |
| } else { | } else { |
| VHashBase *v = json->handle_array ? (VHashBase *)new VArray : (VHashBase *)new VHash; | |
| if (json->stack.count()) set_json_value(json, v); | if (json->stack.count()) set_json_value(json, v); |
| json->stack.push(v); | |
| } | } |
| json->stack.push(v); | |
| break; | break; |
| } | } |
| case JSON_ARRAY_END: | case JSON_ARRAY_END: |
| Line 164 static int json_callback(Json *json, int | Line 208 static int json_callback(Json *json, int |
| } | } |
| break; | break; |
| case JSON_KEY: | case JSON_KEY: |
| json->key = json_string(json, value, length); | json->key = json_string(json, value); |
| break; | break; |
| case JSON_INT: | case JSON_INT: |
| if (json->handle_int){ | if (json->handle_int){ |
| set_json_value(json, new VDouble( json_string(json, value, length)->as_double() )); | set_json_value(json, new VDouble( json_string(json, value)->as_double() )); |
| } else { | } else { |
| // JSON_STRING | // JSON_STRING |
| set_json_value(json, new VString(*json_string(json, value, length))); | set_json_value(json, new VString(*json_string(json, value))); |
| } | } |
| break; | break; |
| case JSON_FLOAT: | case JSON_FLOAT: |
| if (json->handle_double){ | if (json->handle_double){ |
| set_json_value(json, new VDouble( json_string(json, value, length)->as_double() )); | set_json_value(json, new VDouble( json_string(json, value)->as_double() )); |
| break; | break; |
| } // else is JSON_STRING | } // else is JSON_STRING |
| case JSON_STRING: | case JSON_STRING: |
| set_json_value(json, new VString(*json_string(json, value, length))); | set_json_value(json, new VString(*json_string(json, value))); |
| break; | break; |
| case JSON_NULL: | case JSON_NULL: |
| set_json_value(json, VVoid::get()); | set_json_value(json, VVoid::get()); |
| Line 321 static void _parse(Request& r, MethodPar | Line 365 static void _parse(Request& r, MethodPar |
| if(Value* value=options->get("object")) { | if(Value* value=options->get("object")) { |
| json.hook_object=value->get_junction(); | json.hook_object=value->get_junction(); |
| json.request=&r; | json.request=&r; |
| if (!json.hook_object || !json.hook_object->method || !json.hook_object->method->params_names || !(json.hook_object->method->params_names->count() == 2)) | if (!json.hook_object || !json.hook_object->method || !json.hook_object->method->params_names || !(json.hook_object->method->params_count == 2)) |
| throw Exception(PARSER_RUNTIME, 0, "$.object must be parser method with 2 parameters"); | throw Exception(PARSER_RUNTIME, 0, "$.object must be parser method with 2 parameters"); |
| valid_options++; | valid_options++; |
| } | } |
| if(Value* value=options->get("array")) { | if(Value* value=options->get("array")) { |
| json.hook_array=value->get_junction(); | if(value->get_string()){ |
| json.request=&r; | const String& sarray=value->as_string(); |
| if (!json.hook_array || !json.hook_array->method || !json.hook_array->method->params_names || !(json.hook_array->method->params_names->count() == 2)) | if (!json.set_handle_array(sarray)) |
| throw Exception(PARSER_RUNTIME, 0, "$.array must be parser method with 2 parameters"); | throw Exception(PARSER_RUNTIME, &sarray, "$.array must be parser method with 2 parameters or 'array' or 'hash'"); |
| } else { | |
| json.hook_array=value->get_junction(); | |
| json.request=&r; | |
| if (!json.hook_array || !json.hook_array->method || !json.hook_array->method->params_names || !(json.hook_array->method->params_count == 2)) | |
| throw Exception(PARSER_RUNTIME, 0, "$.array must be parser method with 2 parameters or 'array' or 'hash'"); | |
| } | |
| valid_options++; | valid_options++; |
| } | } |
| if(valid_options!=options->count()) | if(valid_options!=options->count()) |
| Line 359 static void _parse(Request& r, MethodPar | Line 409 static void _parse(Request& r, MethodPar |
| json_parser_free(&parser); | json_parser_free(&parser); |
| if (json.result) r.write_no_lang(*json.result); | if (json.result) r.write(*json.result); |
| } | } |
| const uint ANTI_ENDLESS_JSON_STRING_RECOURSION=128; | const uint ANTI_ENDLESS_JSON_STRING_RECURSION=128; |
| char *get_indent(uint level){ | char *get_indent(uint level){ |
| static char* cache[ANTI_ENDLESS_JSON_STRING_RECOURSION]={}; | static char* cache[ANTI_ENDLESS_JSON_STRING_RECURSION]={}; |
| if (!cache[level]){ | if (!cache[level]){ |
| char *result = static_cast<char*>(pa_gc_malloc_atomic(level+1)); | char *result = static_cast<char*>(pa_malloc_atomic(level+1)); |
| memset(result, '\t', level); | memset(result, '\t', level); |
| result[level]='\0'; | result[level]='\0'; |
| return cache[level]=result; | return cache[level]=result; |
| Line 375 char *get_indent(uint level){ | Line 425 char *get_indent(uint level){ |
| return cache[level]; | return cache[level]; |
| } | } |
| class Json_string_recoursion { | String *get_delim(uint level){ |
| static String* cache[ANTI_ENDLESS_JSON_STRING_RECURSION]={}; | |
| if (!cache[level]){ | |
| char *result = static_cast<char*>(pa_malloc_atomic(level+2+1+1)); | |
| result[0]=','; | |
| result[1]='\n'; | |
| memset(result+2, '\t', level); | |
| result[level+2]='"'; | |
| result[level+3]='\0'; | |
| return cache[level] = new String(result, String::L_AS_IS); | |
| } | |
| return cache[level]; | |
| } | |
| String *get_array_delim(uint level){ | |
| static String* cache[ANTI_ENDLESS_JSON_STRING_RECURSION]={}; | |
| if (!cache[level]){ | |
| char *result = static_cast<char*>(pa_malloc_atomic(level+2+1)); | |
| result[0]=','; | |
| result[1]='\n'; | |
| memset(result+2, '\t', level); | |
| result[level+2]='\0'; | |
| return cache[level] = new String(result, String::L_AS_IS); | |
| } | |
| return cache[level]; | |
| } | |
| class Json_string_recursion { | |
| Json_options& foptions; | Json_options& foptions; |
| public: | public: |
| Json_string_recoursion(Json_options& aoptions) : foptions(aoptions) { | Json_string_recursion(Json_options& aoptions) : foptions(aoptions) { |
| if(++foptions.json_string_recoursion==ANTI_ENDLESS_JSON_STRING_RECOURSION) | if(++foptions.json_string_recursion==ANTI_ENDLESS_JSON_STRING_RECURSION) |
| throw Exception(PARSER_RUNTIME, 0, "call canceled - endless json recursion detected"); | throw Exception(PARSER_RUNTIME, 0, "call canceled - endless json recursion detected"); |
| } | } |
| ~Json_string_recoursion() { | ~Json_string_recursion() { |
| if(foptions.json_string_recoursion) | if(foptions.json_string_recursion) |
| foptions.json_string_recoursion--; | foptions.json_string_recursion--; |
| } | } |
| }; | }; |
| Line 394 const String* Json_options::hash_json_st | Line 473 const String* Json_options::hash_json_st |
| if(!hash || !hash->count()) | if(!hash || !hash->count()) |
| return new String("{}", String::L_AS_IS); | return new String("{}", String::L_AS_IS); |
| Json_string_recoursion go_down(*this); | Json_string_recursion go_down(*this); |
| String& result = *new String("{\n", String::L_AS_IS); | String& result = *new String("{\n", String::L_AS_IS); |
| if (indent){ | if (indent){ |
| String *delim=NULL; | String *delim=NULL; |
| indent=get_indent(json_string_recoursion); | indent=get_indent(json_string_recursion); |
| for(HashStringValue::Iterator i(*hash); i; i.next() ){ | for(HashStringValue::Iterator i(*hash); i; i.next() ){ |
| if (delim){ | if (delim){ |
| result << *delim; | result << *delim; |
| } else { | } else { |
| result << indent << "\""; | result << indent << "\""; |
| delim = new String(",\n", String::L_AS_IS); *delim << indent << "\""; | delim = get_delim(json_string_recursion); |
| } | } |
| result << String(i.key(), String::L_JSON) << "\":" << value_json_string(i.key(), *i.value(), *this); | result << String(i.key(), String::L_JSON) << "\":" << value_json_string(i.key(), *i.value(), *this); |
| } | } |
| result << "\n" << (indent=get_indent(json_string_recoursion-1)) << "}"; | result << "\n" << (indent=get_indent(json_string_recursion-1)) << "}"; |
| } else { | } else { |
| Line 428 const String* Json_options::hash_json_st | Line 507 const String* Json_options::hash_json_st |
| return &result; | return &result; |
| } | } |
| const String* Json_options::array_json_string(ArrayValue *array) { | |
| if(!array || !array->count()) | |
| return new String("[]", String::L_AS_IS); | |
| Json_string_recursion go_down(*this); | |
| String& result = *new String("[\n", String::L_AS_IS); | |
| if (indent){ | |
| String *delim=NULL; | |
| indent=get_indent(json_string_recursion); | |
| for(ArrayValue::Iterator i(*array); i; i.next() ){ | |
| if (delim){ | |
| result << *delim; | |
| } else { | |
| result << indent; | |
| delim = get_array_delim(json_string_recursion); | |
| } | |
| result << value_json_string(String::Body::uitoa(i.index()), i.value() ? *i.value() : static_cast<Value&>(*VVoid::get()), *this); | |
| } | |
| result << "\n" << (indent=get_indent(json_string_recursion-1)) << "]"; | |
| } else { | |
| bool need_delim=false; | |
| for(ArrayValue::Iterator i(*array); i; i.next() ){ | |
| if(need_delim) result << ",\n"; | |
| result << value_json_string(String::Body::uitoa(i.index()), i.value() ? *i.value() : static_cast<Value&>(*VVoid::get()), *this); | |
| need_delim=true; | |
| } | |
| result << "\n]"; | |
| } | |
| return &result; | |
| } | |
| const String* Json_options::array_compact_json_string(ArrayValue *array) { | |
| if(!array || !array->count()) | |
| return new String("[]", String::L_AS_IS); | |
| Json_string_recursion go_down(*this); | |
| String& result = *new String("[\n", String::L_AS_IS); | |
| if (indent){ | |
| String *delim=NULL; | |
| indent=get_indent(json_string_recursion); | |
| for(ArrayValue::Iterator i(*array); i; i.next() ){ | |
| if (i.value()){ | |
| if (delim){ | |
| result << *delim; | |
| } else { | |
| result << indent; | |
| delim = get_array_delim(json_string_recursion); | |
| } | |
| result << value_json_string(String::Body::uitoa(i.index()), *i.value(), *this); | |
| } | |
| } | |
| result << "\n" << (indent=get_indent(json_string_recursion-1)) << "]"; | |
| } else { | |
| bool need_delim=false; | |
| for(ArrayValue::Iterator i(*array); i; i.next() ){ | |
| if (i.value()){ | |
| if(need_delim) result << ",\n"; | |
| result << value_json_string(String::Body::uitoa(i.index()), *i.value(), *this); | |
| need_delim=true; | |
| } | |
| } | |
| result << "\n]"; | |
| } | |
| return &result; | |
| } | |
| static bool based_on(HashStringValue::key_type key, HashStringValue::value_type /*value*/, Value* v) { | static bool based_on(HashStringValue::key_type key, HashStringValue::value_type /*value*/, Value* v) { |
| return v->is(key.cstr()); | return v->is(key.cstr()); |
| } | } |
| Line 440 const String& value_json_string(String:: | Line 599 const String& value_json_string(String:: |
| options.methods->put(v.type(), method ? method : VVoid::get()); | options.methods->put(v.type(), method ? method : VVoid::get()); |
| } | } |
| if(method && !method->is_void()) { | if(method && !method->is_void()) { |
| static const String::Body sindent("indent"); | |
| Junction* junction=method->get_junction(); | Junction* junction=method->get_junction(); |
| VMethodFrame frame(*junction->method, options.r->method_frame, junction->self); | |
| HashStringValue* params_hash=options.params && options.indent ? options.params->get_hash() : NULL; | HashStringValue* params_hash=options.params && options.indent ? options.params->get_hash() : NULL; |
| Temp_hash_value<HashStringValue, Value*> indent(params_hash, "indent", new VString(*new String(options.indent, String::L_AS_IS))); | Temp_hash_value<HashStringValue, Value*> indent(params_hash, sindent, new VString(*new String(options.indent, String::L_AS_IS))); |
| Value *params[]={new VString(*new String(key, String::L_JSON)), &v, options.params ? options.params : VVoid::get()}; | Value *params[]={new VString(*new String(key, String::L_JSON)), &v, options.params ? options.params : VVoid::get()}; |
| frame.store_params(params, 3); | |
| options.r->execute_method(frame); | |
| return frame.result().as_string(); | METHOD_FRAME_ACTION(*junction->method, options.r->method_frame, junction->self, { |
| frame.store_params(params, 3); | |
| options.r->call(frame); | |
| return frame.result().as_string(); | |
| }); | |
| } | } |
| } | } |
| Line 464 static void _string(Request& r, MethodPa | Line 623 static void _string(Request& r, MethodPa |
| if(params.count() == 2) | if(params.count() == 2) |
| if(HashStringValue* options=params.as_hash(1)) { | if(HashStringValue* options=params.as_hash(1)) { |
| json.params=params.get(1); | json.params=¶ms[1]; |
| HashStringValue* methods=new HashStringValue(); | HashStringValue* methods=new HashStringValue(); |
| int valid_options=0; | |
| HashStringValue* vvalue; | HashStringValue* vvalue; |
| for(HashStringValue::Iterator i(*options); i; i.next() ){ | for(HashStringValue::Iterator i(*options); i; i.next() ){ |
| String::Body key=i.key(); | String::Body key=i.key(); |
| Value* value=i.value(); | Value* value=i.value(); |
| if(key == "skip-unknown"){ | if(key == "skip-unknown"){ |
| json.skip_unknown=r.process(*value).as_bool(); | json.skip_unknown=r.process(*value).as_bool(); |
| valid_options++; | } else if(key == "one-line"){ |
| json.one_line=r.process(*value).as_bool(); | |
| } else if(key == "date" && value->is_string()){ | } else if(key == "date" && value->is_string()){ |
| const String& svalue=value->as_string(); | const String& svalue=value->as_string(); |
| if(!json.set_date_format(svalue)) | if(!json.set_date_format(svalue)) |
| throw Exception(PARSER_RUNTIME, &svalue, "must be 'sql-string', 'gmt-string', 'iso-string' or 'unix-timestamp'"); | throw Exception(PARSER_RUNTIME, &svalue, "must be 'sql-string', 'gmt-string', 'iso-string' or 'unix-timestamp'"); |
| valid_options++; | |
| } else if(key == "indent"){ | } else if(key == "indent"){ |
| if(value->is_string()){ | if(value->is_string()){ |
| json.indent=value->as_string().cstr(); | json.indent=value->as_string().cstr(); |
| json.json_string_recoursion=strlen(json.indent); | json.json_string_recursion=strlen(json.indent); |
| } else json.indent=r.process(*value).as_bool() ? "" : NULL; | } else json.indent=r.process(*value).as_bool() ? "" : NULL; |
| valid_options++; | |
| } else if(key == "table" && value->is_string()){ | } else if(key == "table" && value->is_string()){ |
| const String& svalue=value->as_string(); | const String& svalue=value->as_string(); |
| if(!json.set_table_format(svalue)) | if(!json.set_table_format(svalue)) |
| throw Exception(PARSER_RUNTIME, &svalue, "must be 'array', 'object' or 'compact'"); | throw Exception(PARSER_RUNTIME, &svalue, "must be 'array', 'object' or 'compact'"); |
| valid_options++; | } else if(key == "array" && value->is_string()){ |
| const String& svalue=value->as_string(); | |
| if(!json.set_array_format(svalue)) | |
| throw Exception(PARSER_RUNTIME, &svalue, "must be 'array', 'object' or 'compact'"); | |
| } else if(key == "file" && value->is_string()){ | } else if(key == "file" && value->is_string()){ |
| const String& svalue=value->as_string(); | const String& svalue=value->as_string(); |
| if(!json.set_file_format(svalue)) | if(!json.set_file_format(svalue)) |
| throw Exception(PARSER_RUNTIME, &svalue, "must be 'base64', 'text' or 'stat'"); | throw Exception(PARSER_RUNTIME, &svalue, "must be 'base64', 'text' or 'stat'"); |
| valid_options++; | |
| } else if(key == "void" && value->is_string()){ | } else if(key == "void" && value->is_string()){ |
| const String& svalue=value->as_string(); | const String& svalue=value->as_string(); |
| if(!json.set_void_format(svalue)) | if(!json.set_void_format(svalue)) |
| throw Exception(PARSER_RUNTIME, &svalue, "must be 'string' or 'null'"); | throw Exception(PARSER_RUNTIME, &svalue, "must be 'string' or 'null'"); |
| valid_options++; | |
| #ifdef XML | #ifdef XML |
| } else if(key == "xdoc" && (vvalue = value->get_hash())){ | } else if(key == "xdoc" && (vvalue = value->get_hash())){ |
| json.xdoc_options=new XDocOutputOptions(); | json.xdoc_options=new XDocOutputOptions(); |
| json.xdoc_options->append(r, vvalue); | json.xdoc_options->append(r, vvalue); |
| valid_options++; | |
| #endif | #endif |
| } else if(Junction* junction=value->get_junction()){ | } else if(Junction* junction=value->get_junction()){ |
| if(!junction->method || !junction->method->params_names || junction->method->params_names->count() != 3) | if(!junction->method || !junction->method->params_names || junction->method->params_count != 3) |
| throw Exception(PARSER_RUNTIME, 0, "$.%s must be parser method with 3 parameters", key.cstr()); | throw Exception(PARSER_RUNTIME, 0, "$.%s must be parser method with 3 parameters", key.cstr()); |
| methods->put(key, value); | methods->put(key, value); |
| valid_options++; | } else |
| } | throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); |
| } | } |
| if(valid_options!=options->count()) | |
| throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); | |
| // special handling for $._default | // special handling for $._default |
| if(VHashBase* vhash=static_cast<VHashBase*>(params[1].as(VHASH_TYPE))) | if(VHashBase* vhash=dynamic_cast<VHashBase*>(¶ms[1])) |
| if(Value* value=vhash->get_default()) { | if(Value* value=vhash->get_default()) { |
| if(!value->is_string()){ | if(!value->is_string()){ |
| Junction* junction=value->get_junction(); | Junction* junction=value->get_junction(); |
| if(!junction || !junction->method || !junction->method->params_names || junction->method->params_names->count() != 3) | if(!junction || !junction->method || !junction->method->params_names || junction->method->params_count != 3) |
| throw Exception(PARSER_RUNTIME, 0, "$._default must be string or parser method with 3 parameters"); | throw Exception(PARSER_RUNTIME, 0, "$._default must be string or parser method with 3 parameters"); |
| } | } |
| json.default_method=value; | json.default_method=value; |
| Line 534 static void _string(Request& r, MethodPa | Line 688 static void _string(Request& r, MethodPa |
| const String& result_string=value_json_string(String::Body(), r.process(params[0]), json); | const String& result_string=value_json_string(String::Body(), r.process(params[0]), json); |
| String::Body result_body=result_string.cstr_to_string_body_untaint(String::L_JSON, r.connection(false), &r.charsets); | String::Body result_body=result_string.cstr_to_string_body_untaint(String::L_JSON, r.connection(false), &r.charsets); |
| r.write_pass_lang(*new String(result_body, String::L_AS_IS)); | if(json.one_line){ |
| } | char *result=result_body.cstrm(); |
| for(char *c=result;*c;c++) | |
| if(*c=='\n') | |
| *c=' '; | |
| result_body=String::Body(result); | |
| } | |
| r.write(*new String(result_body, String::L_AS_IS)); | |
| } | |
| // constructor | // constructor |
| MJson::MJson(): Methoded("json") { | MJson::MJson(): Methoded("json") { |
| add_native_method("parse", Method::CT_STATIC, _parse, 1, 2); | add_native_method("parse", Method::CT_STATIC, _parse, 1, 2); |
| add_native_method("string", Method::CT_ANY, _string, 1, 2); | add_native_method("string", Method::CT_STATIC, _string, 1, 2); |
| } | } |