|
|
| version 1.12, 2010/10/26 11:41:42 | version 1.49, 2016/11/03 16:17:37 |
|---|---|
| Line 1 | Line 1 |
| /** @file | /** @file |
| Parser: @b json parser class. | Parser: @b json parser class. |
| Copyright (c) 2010 ArtLebedev Group (http://www.artlebedev.com) | Copyright (c) 2000-2015 Art. Lebedev Studio (http://www.artlebedev.com) |
| */ | */ |
| static const char * const IDENT_RESPONSE_C="$Date$"; | |
| #include "classes.h" | #include "classes.h" |
| #include "pa_vmethod_frame.h" | #include "pa_vmethod_frame.h" |
| Line 14 static const char * const IDENT_RESPONSE | Line 12 static const char * const IDENT_RESPONSE |
| #include "pa_charset.h" | #include "pa_charset.h" |
| #include "pa_charsets.h" | #include "pa_charsets.h" |
| #include "JSON_parser.h" | #include "pa_json.h" |
| #ifdef XML | |
| #include "pa_vxdoc.h" | |
| #endif | |
| volatile const char * IDENT_JSON_C="$Id$"; | |
| // class | // class |
| Line 25 public: | Line 29 public: |
| // global variable | // global variable |
| DECLARE_CLASS_VAR(json, new MJson, 0); | DECLARE_CLASS_VAR(json, new MJson); |
| // methods | // methods |
| struct Json { | struct Json { |
| Line 35 struct Json { | Line 39 struct Json { |
| String* key; | String* key; |
| Value* result; | Value* result; |
| Junction* hook; | Junction* hook_object; |
| Junction* hook_array; | |
| Request* request; | Request* request; |
| Charset *charset; | Charset *charset; |
| String::Language taint; | |
| bool handle_double; | bool handle_double; |
| bool handle_int; | |
| 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(NULL), request(NULL), charset(acharset), handle_double(true), distinct(D_EXCEPTION){} | 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), | |
| 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 83 static void set_json_value(Json *json, V | Line 93 static void set_json_value(Json *json, V |
| } | } |
| } | } |
| String* json_string(Json *json, const JSON_value* value){ | String* json_string(Json *json, const char *value, uint32_t length){ |
| String::C result = json->charset !=NULL ? | String::C result = json->charset !=NULL ? |
| Charset::transcode(String::C(value->vu.str.value, value->vu.str.length), UTF8_charset, *json->charset) : | Charset::transcode(String::C(value, length), pa_UTF8_charset, *json->charset) : |
| String::C(pa_strdup(value->vu.str.value, value->vu.str.length), value->vu.str.length); | String::C(pa_strdup(value, length), length); |
| return new String(result.str, String::L_TAINTED, result.length); | return new String(result, json->taint); |
| } | } |
| 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().as_value(); | }); |
| } | } |
| static int json_callback(Json *json, int type, const JSON_value* value) | static int json_callback(Json *json, int type, const char *value, uint32_t length) |
| { | { |
| switch(type) { | switch(type) { |
| case JSON_T_OBJECT_BEGIN:{ | case JSON_OBJECT_BEGIN:{ |
| VHash *v = new VHash(); | VHash *v = new VHash(); |
| if (json->hook){ | if (json->hook_object){ |
| json->key_stack.push(json->key); | json->key_stack.push(json->key); |
| json->key=NULL; | |
| } else { | } else { |
| 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_T_OBJECT_END:{ | case JSON_OBJECT_END:{ |
| if (json->hook){ | if (json->hook_object){ |
| String* key = json->key_stack.pop(); | String* key = json->key_stack.pop(); |
| json->result = json_hook(*json->request, json->hook, key, json->stack.pop()); | json->result = json_hook(*json->request, json->hook_object, key, json->stack.pop()); |
| if (json->stack.count()){ | if (json->stack.count()){ |
| json->key = key; | json->key = key; |
| Line 127 static int json_callback(Json *json, int | Line 137 static int json_callback(Json *json, int |
| } | } |
| break; | break; |
| } | } |
| case JSON_T_ARRAY_BEGIN:{ | case JSON_ARRAY_BEGIN:{ |
| VHash *v = new VHash(); | VHash *v = new VHash(); |
| if (json->stack.count()) set_json_value(json, v); | if (json->hook_array){ |
| json->key_stack.push(json->key); | |
| json->key=NULL; | |
| } else { | |
| if (json->stack.count()) set_json_value(json, v); | |
| } | |
| json->stack.push(v); | json->stack.push(v); |
| break; | break; |
| } | } |
| case JSON_T_ARRAY_END: | case JSON_ARRAY_END: |
| // libjson supports array at top level, we too | // libjson supports array at top level, we too |
| json->result = json->stack.pop(); | if (json->hook_array){ |
| String* key = json->key_stack.pop(); | |
| json->result = json_hook(*json->request, json->hook_array, key, json->stack.pop()); | |
| if (json->stack.count()){ | |
| json->key = key; | |
| set_json_value(json, json->result); | |
| } | |
| } else { | |
| json->result = json->stack.pop(); | |
| } | |
| break; | |
| case JSON_KEY: | |
| json->key = json_string(json, value, length); | |
| break; | break; |
| case JSON_T_KEY: | case JSON_INT: |
| json->key = json_string(json, value); | if (json->handle_int){ |
| break; | set_json_value(json, new VDouble( json_string(json, value, length)->as_double() )); |
| case JSON_T_INTEGER: | } else { |
| set_json_value(json, new VInt((int)value->vu.integer_value)); | // JSON_STRING |
| set_json_value(json, new VString(*json_string(json, value, length))); | |
| } | |
| break; | break; |
| case JSON_T_FLOAT: | case JSON_FLOAT: |
| if (json->handle_double){ | if (json->handle_double){ |
| set_json_value(json, new VDouble( json_string(json, value)->as_double() )); | set_json_value(json, new VDouble( json_string(json, value, length)->as_double() )); |
| break; | break; |
| } // else is JSON_T_STRING | } // else is JSON_STRING |
| case JSON_T_STRING: | case JSON_STRING: |
| set_json_value(json, new VString(*json_string(json, value))); | set_json_value(json, new VString(*json_string(json, value, length))); |
| break; | break; |
| case JSON_T_NULL: | case JSON_NULL: |
| set_json_value(json, new VVoid()); | set_json_value(json, VVoid::get()); |
| break; | break; |
| case JSON_T_TRUE: | case JSON_TRUE: |
| set_json_value(json, &VBool::get(true)); | set_json_value(json, &VBool::get(true)); |
| break; | break; |
| case JSON_T_FALSE: | case JSON_FALSE: |
| set_json_value(json, &VBool::get(false)); | set_json_value(json, &VBool::get(false)); |
| break; | break; |
| } | } |
| return 1; | return 0; |
| } | } |
| static const char* json_error_message(int error_code){ | static const char* json_error_message(int error_code){ |
| static const char* error_messages[] = { | static const char* error_messages[] = { |
| NULL, | NULL, |
| "invalid char", | "out of memory", |
| "invalid keyword", | "bad character", |
| "invalid escape sequence", | "stack empty", |
| "invalid unicode sequence", | "pop unexpected mode", |
| "invalid number", | "nesting limit", |
| "nesting depth reached", | "data limit", |
| "unbalanced collection", | "comment not allowed by config", |
| "expected key", | "unexpected character", |
| "expected colon", | "missing unicode low surrogate", |
| "out of memory" | "unexpected unicode low surrogate", |
| "error comma out of structure", | |
| "error in a callback" | |
| }; | }; |
| return error_messages[error_code]; | return error_messages[error_code]; |
| } | } |
| extern String::Language get_untaint_lang(const String& lang_name); | |
| #define SOURCE_MAX_LEN 60 | |
| void json_exception_with_source(Request& r, const char* msg, const char* json, int offset){ | |
| int i; | |
| int line=0; | |
| int start=0; | |
| int end=strlen(json); | |
| if(offset>end) | |
| offset=end; | |
| for(i = 0; i < offset; i++){ | |
| if(json[i]=='\n'){ | |
| line++; | |
| } | |
| } | |
| if(offset > SOURCE_MAX_LEN/2) | |
| start = offset - SOURCE_MAX_LEN/2; | |
| for(i = offset-1; i>=start; i--){ | |
| if(json[i]=='\n'){ | |
| start=i+1; | |
| break; | |
| } | |
| } | |
| if(start+SOURCE_MAX_LEN < end) | |
| end=start+SOURCE_MAX_LEN; | |
| for(i = offset+1; i<end; i++){ | |
| if(json[i]=='\n'){ | |
| end=i; | |
| break; | |
| } | |
| } | |
| char *source = pa_strdup(json+start, end-start); | |
| int source_offset = offset-start; | |
| if(source[source_offset]=='\n') | |
| source[source_offset]=' '; | |
| for(i = 0; i < source_offset; i++){ | |
| if(source[i]=='\t'){ | |
| source[i]=' '; | |
| } | |
| } | |
| if(r.charsets.source().isUTF8()){ | |
| source=(char *)fixUTF8(source); | |
| if(source_offset>0){ | |
| String s_source(pa_strdup(source,source_offset)); | |
| source_offset=s_source.length(r.charsets.source()); | |
| } | |
| } | |
| throw Exception("json.parse", 0, "%s at line %d\n%s\n%*s", msg, line+1, source, source_offset+1, "^"); | |
| } | |
| static void _parse(Request& r, MethodParams& params) { | static void _parse(Request& r, MethodParams& params) { |
| const String& json_string=params.as_string(0, "json must be string"); | const String& json_string=params.as_string(0, "json must be string"); |
| Json json(r.charsets.source().isUTF8() ? NULL : &(r.charsets.source())); | Json json(r.charsets.source().isUTF8() ? NULL : &(r.charsets.source())); |
| JSON_config config; | json_config config = { |
| init_JSON_config(&config); | 0, // buffer_initial_size |
| 128, // max_nesting | |
| config.depth = 19; | 0, // max_data |
| config.callback = (JSON_parser_callback)&json_callback; | 1, // allow_c_comments |
| config.allow_comments = 1; | 1, // allow_yaml_comments |
| config.handle_floats_manually = 1; | pa_malloc, |
| config.callback_ctx = &json; | pa_realloc, |
| pa_free | |
| }; | |
| if(params.count() == 2) | if(params.count() == 2) |
| if(HashStringValue* options=params.as_hash(1)) { | if(HashStringValue* options=params.as_hash(1)) { |
| int valid_options=0; | int valid_options=0; |
| if(Value* value=options->get("depth")) { | if(Value* value=options->get("depth")) { |
| config.depth=r.process_to_value(*value).as_int(); | config.max_nesting=r.process(*value).as_int(); |
| valid_options++; | valid_options++; |
| } | } |
| if(Value* value=options->get("double")) { | if(Value* value=options->get("double")) { |
| json.handle_double=r.process_to_value(*value).as_bool(); | json.handle_double=r.process(*value).as_bool(); |
| valid_options++; | |
| } | |
| if(Value* value=options->get("int")) { | |
| json.handle_int=r.process(*value).as_bool(); | |
| valid_options++; | valid_options++; |
| } | } |
| if(Value* value=options->get("distinct")) { | if(Value* value=options->get("distinct")) { |
| Line 212 static void _parse(Request& r, MethodPar | Line 313 static void _parse(Request& r, MethodPar |
| throw Exception(PARSER_RUNTIME, &sdistinct, "must be 'first', 'last' or 'all'"); | throw Exception(PARSER_RUNTIME, &sdistinct, "must be 'first', 'last' or 'all'"); |
| valid_options++; | valid_options++; |
| } | } |
| if(Value* value=options->get("taint")) { | |
| json.taint=get_untaint_lang(value->as_string()); | |
| valid_options++; | |
| } | |
| if(Value* value=options->get("object")) { | if(Value* value=options->get("object")) { |
| json.hook=value->get_junction(); | json.hook_object=value->get_junction(); |
| json.request=&r; | json.request=&r; |
| if (!json.hook || !json.hook->method || !json.hook->method->params_names || !(json.hook->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")) { | |
| 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"); | |
| valid_options++; | |
| } | |
| if(valid_options!=options->count()) | if(valid_options!=options->count()) |
| throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); | throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); |
| } | } |
| const String::Body json_body = json_string.cstr_to_string_body_untaint(String::L_JSON, 0, &(r.charsets)); | const String::Body json_body = json_string.cstr_to_string_body_untaint(String::L_JSON, r.connection(false), &r.charsets); |
| const char *json_cstr = json.charset != NULL ? Charset::transcode(json_body, *json.charset, UTF8_charset).cstr() : json_body.cstr(); | const char *json_cstr = json.charset != NULL ? Charset::transcode(json_body, *json.charset, pa_UTF8_charset).cstr() : json_body.cstr(); |
| struct JSON_parser_struct* jc = new_JSON_parser(&config); | json_parser parser; |
| if(int result = json_parser_init(&parser, &config, (json_parser_callback)&json_callback, &json)) | |
| throw Exception("json.parse", 0, "%s", json_error_message(result)); | |
| for (const char *c=json_cstr; *c; c++){ | if(!*json_cstr) |
| if (!JSON_parser_char(jc, *((const unsigned char *)c))) { | throw Exception("json.parse", 0, "empty string is not valid json"); |
| throw Exception("json.parse", 0, "%s at byte %d", json_error_message(JSON_parser_get_last_error(jc)), c-json_cstr); | |
| } | |
| } | |
| if (!JSON_parser_done(jc)) { | const char *first_quote=strchr(json_cstr,'"'); |
| throw Exception("json.parse", 0, "%s at the end", json_error_message(JSON_parser_get_last_error(jc))); | if(first_quote && first_quote>json_cstr && *(--first_quote) == '\\') |
| } | json_exception_with_source(r, "illegal quote escape, json may be tainted", json_cstr, first_quote-json_cstr); |
| delete_JSON_parser(jc); | uint32_t processed; |
| if(int result = json_parser_string(&parser, json_cstr, strlen(json_cstr), &processed)) | |
| json_exception_with_source(r, json_error_message(result), json_cstr, processed); | |
| if (!json_parser_is_done(&parser)) | |
| json_exception_with_source(r, "unexpected end of json data", json_cstr, processed); | |
| if (json.result) r.write_no_lang(*json.result); | json_parser_free(&parser); |
| if (json.result) r.write(*json.result); | |
| } | } |
| const uint ANTI_ENDLESS_JSON_STRING_RECOURSION=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_RECOURSION]={}; |
| if (!cache[level]){ | if (!cache[level]){ |
| Line 254 char *get_indent(uint level){ | Line 374 char *get_indent(uint level){ |
| return cache[level]; | return cache[level]; |
| } | } |
| const String& value_json_string(Value& v, Json_options* options); | class Json_string_recoursion { |
| Json_options& foptions; | |
| public: | |
| Json_string_recoursion(Json_options& aoptions) : foptions(aoptions) { | |
| if(++foptions.json_string_recoursion==ANTI_ENDLESS_JSON_STRING_RECOURSION) | |
| throw Exception(PARSER_RUNTIME, 0, "call canceled - endless json recursion detected"); | |
| } | |
| ~Json_string_recoursion() { | |
| if(foptions.json_string_recoursion) | |
| foptions.json_string_recoursion--; | |
| } | |
| }; | |
| const String& hash_json_string(HashStringValue &hash, Json_options* options) { | const String& value_json_string(String::Body key, Value& v, Json_options& options); |
| if(!hash.count()) | |
| return *new String("{}", String::L_AS_IS); | |
| uint level = options->r->json_string_recoursion_go_down(); | const String* Json_options::hash_json_string(HashStringValue *hash) { |
| if(!hash || !hash->count()) | |
| return new String("{}", String::L_AS_IS); | |
| Json_string_recoursion go_down(*this); | |
| String& result = *new String("{\n", String::L_AS_IS); | String& result = *new String("{\n", String::L_AS_IS); |
| if (options->indent){ | if (indent){ |
| String *delim=NULL; | String *delim=NULL; |
| options->indent=get_indent(level); | indent=get_indent(json_string_recoursion); |
| 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 << options->indent << "\""; | result << indent << "\""; |
| delim = new String(",\n", String::L_AS_IS); *delim << options->indent << "\""; | delim = new String(",\n", String::L_AS_IS); *delim << indent << "\""; |
| } | } |
| result << String(i.key(), String::L_JSON) << "\":" << value_json_string(*i.value(), options); | result << String(i.key(), String::L_JSON) << "\":" << value_json_string(i.key(), *i.value(), *this); |
| } | } |
| result << "\n" << (options->indent=get_indent(level-1)) << "}"; | result << "\n" << (indent=get_indent(json_string_recoursion-1)) << "}"; |
| } else { | } else { |
| bool need_delim=false; | bool need_delim=false; |
| for(HashStringValue::Iterator i(hash); i; i.next() ){ | for(HashStringValue::Iterator i(*hash); i; i.next() ){ |
| result << (need_delim ? ",\n\"" : "\""); | result << (need_delim ? ",\n\"" : "\""); |
| result << String(i.key(), String::L_JSON) << "\":" << value_json_string(*i.value(), options); | result << String(i.key(), String::L_JSON) << "\":" << value_json_string(i.key(), *i.value(), *this); |
| need_delim=true; | need_delim=true; |
| } | } |
| result << "\n}"; | result << "\n}"; |
| } | } |
| options->r->json_string_recoursion_go_up(); | return &result; |
| return result; | |
| } | } |
| const String& value_json_string(Value& v, Json_options* options) { | static bool based_on(HashStringValue::key_type key, HashStringValue::value_type /*value*/, Value* v) { |
| if(options && options->methods) | return v->is(key.cstr()); |
| if(Value* method=options->methods->get(v.type())){ | } |
| Junction* junction=method->get_junction(); | |
| VMethodFrame frame(*junction->method, options->r->method_frame, junction->self); | |
| Value *params[]={&v, (options->params) ? options->params : VVoid::get()}; | const String& value_json_string(String::Body key, Value& v, Json_options& options) { |
| frame.store_params(params, 2); | if(options.methods) { |
| Value* method=options.methods->get(v.type()); | |
| if(!method){ | |
| method=options.methods->first_that<Value*>(based_on, &v); | |
| options.methods->put(v.type(), method ? method : VVoid::get()); | |
| } | |
| if(method && !method->is_void()) { | |
| Junction* junction=method->get_junction(); | |
| 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))); | |
| options->r->execute_method(frame); | Value *params[]={new VString(*new String(key, String::L_JSON)), &v, options.params ? options.params : VVoid::get()}; |
| 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(); | |
| }); | |
| } | } |
| } | |
| if(HashStringValue* hash=v.get_hash()) | options.key=key; |
| return hash_json_string(*hash, options); | |
| return *v.get_json_string(options); | return *v.get_json_string(options); |
| } | } |
| Line 320 static void _string(Request& r, MethodPa | Line 462 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; | int valid_options=0; |
| 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_to_value(*value).as_bool(); | json.skip_unknown=r.process(*value).as_bool(); |
| valid_options++; | valid_options++; |
| } 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' or 'unix-timestamp'"); | throw Exception(PARSER_RUNTIME, &svalue, "must be 'sql-string', 'gmt-string', 'iso-string' or 'unix-timestamp'"); |
| valid_options++; | valid_options++; |
| } else if(key == "indent"){ | } else if(key == "indent"){ |
| json.indent=r.process_to_value(*value).as_bool() ? "":NULL; | if(value->is_string()){ |
| json.indent=value->as_string().cstr(); | |
| json.json_string_recoursion=strlen(json.indent); | |
| } else json.indent=r.process(*value).as_bool() ? "" : NULL; | |
| valid_options++; | 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' or 'object'"); | throw Exception(PARSER_RUNTIME, &svalue, "must be 'array', 'object' or 'compact'"); |
| valid_options++; | valid_options++; |
| } 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' or 'text'"); | throw Exception(PARSER_RUNTIME, &svalue, "must be 'base64', 'text' or 'stat'"); |
| valid_options++; | |
| } else if(key == "void" && value->is_string()){ | |
| const String& svalue=value->as_string(); | |
| if(!json.set_void_format(svalue)) | |
| throw Exception(PARSER_RUNTIME, &svalue, "must be 'string' or 'null'"); | |
| valid_options++; | |
| #ifdef XML | |
| } else if(key == "xdoc" && (vvalue = value->get_hash())){ | |
| json.xdoc_options=new XDocOutputOptions(); | |
| json.xdoc_options->append(r, vvalue); | |
| valid_options++; | valid_options++; |
| #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() != 2) | if(!junction->method || !junction->method->params_names || junction->method->params_count != 3) |
| throw Exception(PARSER_RUNTIME, 0, "$.%s must be parser method with 2 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++; | valid_options++; |
| } | } |
| } | } |
| if(valid_options!=options->count()) | if(valid_options!=options->count()) |
| throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); | throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); |
| // special handling for $._default | |
| if(VHashBase* vhash=static_cast<VHashBase*>(params[1].as(VHASH_TYPE))) | |
| if(Value* value=vhash->get_default()) { | |
| if(!value->is_string()){ | |
| Junction* junction=value->get_junction(); | |
| 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"); | |
| } | |
| json.default_method=value; | |
| } | |
| if(methods->count()) | if(methods->count()) |
| json.methods=methods; | json.methods=methods; |
| } | } |
| r.write_pass_lang(value_json_string(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); | |
| r.write(*new String(result_body, String::L_AS_IS)); | |
| } | } |
| // constructor | // constructor |