Diff for /parser3/src/classes/json.C between versions 1.12 and 1.49

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=&params[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

Removed from v.1.12  
changed lines
  Added in v.1.49


E-mail: