Diff for /parser3/src/classes/json.C between versions 1.23 and 1.63

version 1.23, 2012/06/04 13:46:18 version 1.63, 2024/10/02 21:24:41
Line 1 Line 1
 /** @file  /** @file
         Parser: @b json parser class.          Parser: @b json parser class.
   
         Copyright (c) 2000-2012 Art. Lebedev Studio (http://www.artlebedev.com)          Copyright (c) 2000-2023 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"
 #include "JSON_parser.h"  #include "pa_json.h"
   
 #ifdef XML  #ifdef XML
 #include "pa_vxdoc.h"  #include "pa_vxdoc.h"
Line 29  public: Line 31  public:
   
 // global variable  // global variable
   
 DECLARE_CLASS_VAR(json, new MJson, 0);  DECLARE_CLASS_VAR(json, new MJson);
   
 // 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 47  struct Json { Line 49  struct Json {
         String::Language taint;          String::Language taint;
   
         bool handle_double;          bool handle_double;
           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), distinct(D_EXCEPTION){}                  request(NULL), charset(acharset), taint(String::L_TAINTED), handle_double(true), handle_int(true), 
                   handle_array(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 59  struct Json { Line 64  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;
           }
 };  };
   
 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 81  static void set_json_value(Json *json, V Line 93  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 91  static void set_json_value(Json *json, V Line 103  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, json->taint, 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_object){                          if (json->hook_object){
                                 json->key_stack.push(json->key);                                  json->key_stack.push(json->key);
Line 122  static int json_callback(Json *json, int Line 133  static int json_callback(Json *json, int
                         json->stack.push(v);                          json->stack.push(v);
                         break;                          break;
                 }                  }
                 case JSON_T_OBJECT_END:{                  case JSON_OBJECT_END:{
                         if (json->hook_object){                          if (json->hook_object){
                                 String* key = json->key_stack.pop();                                  String* key = json->key_stack.pop();
                                 json->result = json_hook(*json->request, json->hook_object, key, json->stack.pop());                                  json->result = json_hook(*json->request, json->hook_object, key, json->stack.pop());
Line 136  static int json_callback(Json *json, int Line 147  static int json_callback(Json *json, int
                         }                          }
                         break;                          break;
                 }                  }
                 case JSON_T_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_T_ARRAY_END:                  case JSON_ARRAY_END:
                         // libjson supports array at top level, we too                          // libjson supports array at top level, we too
                         if (json->hook_array){                          if (json->hook_array){
                                 String* key = json->key_stack.pop();                                  String* key = json->key_stack.pop();
Line 161  static int json_callback(Json *json, int Line 173  static int json_callback(Json *json, int
                                 json->result = json->stack.pop();                                  json->result = json->stack.pop();
                         }                          }
                         break;                          break;
                 case JSON_T_KEY:                  case JSON_KEY:
                         json->key = json_string(json, value);                          json->key = json_string(json, value, length);
                         break;                          break;
                 case JSON_T_INTEGER:                  case JSON_INT:
                         set_json_value(json, new VDouble((double)value->vu.integer_value));                          if (json->handle_int){
                                   set_json_value(json, new VDouble( json_string(json, value, length)->as_double() ));
                           } else {
                                   // 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, VVoid::get());                          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);  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 245  static void _parse(Request& r, MethodPar Line 331  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())
                                 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);  
   
         if (json.result) r.write_no_lang(*json.result);          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);
   
           json_parser_free(&parser);
   
           if (json.result) r.write(*json.result);
 }  }
   
   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 291  char *get_indent(uint level){ Line 391  char *get_indent(uint level){
         return cache[level];          return cache[level];
 }  }
   
   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;
   public:
           Json_string_recursion(Json_options& aoptions) : foptions(aoptions) {
                   if(++foptions.json_string_recursion==ANTI_ENDLESS_JSON_STRING_RECURSION)
                           throw Exception(PARSER_RUNTIME, 0, "call canceled - endless json recursion detected");
           }
           ~Json_string_recursion() {
                   if(foptions.json_string_recursion)
                           foptions.json_string_recursion--;
           }
   };
   
 const String& value_json_string(String::Body key, Value& v, Json_options& options);  const String& value_json_string(String::Body key, Value& v, Json_options& options);
   
 const String* Json_options::hash_json_string(HashStringValue &hash) {  const String* Json_options::hash_json_string(HashStringValue *hash) {
         if(!hash.count())          if(!hash || !hash->count())
                 return new String("{}", String::L_AS_IS);                  return new String("{}", String::L_AS_IS);
   
         uint level = r->json_string_recoursion_go_down();          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(level);                  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(level-1)) << "}";                  result << "\n" << (indent=get_indent(json_string_recursion-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.key(), *i.value(), *this);                          result << String(i.key(), String::L_JSON) << "\":" << value_json_string(i.key(), *i.value(), *this);
                         need_delim=true;                          need_delim=true;
Line 328  const String* Json_options::hash_json_st Line 470  const String* Json_options::hash_json_st
   
         }          }
   
         r->json_string_recoursion_go_up();          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(i.key(), i.value() ? *i.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(i.key(), i.value() ? *i.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(i.key(), *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(i.key(), *i.value(), *this);
                                   need_delim=true;
                           }
                   }
                   result << "\n]";
   
           }
   
         return &result;          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());
 }  }
                                           
 const String& value_json_string(String::Body key, Value& v, Json_options& options) {  const String& value_json_string(String::Body key, Value& v, Json_options& options) {
         if(options.methods) {          if(options.methods) {
                 Value* method=options.methods->get(v.type());                  Value* method=options.methods->get(v.type());
                 if(!method){                  if(!method){
                         method=options.methods->first_that<Value*>(based_on, &v);                          method=options.methods->first_that<Value*>(based_on, &v);
                         options.methods->put(key, method ? method : VVoid::get());                          options.methods->put(v.type(), method ? method : VVoid::get());
                 }                  }
                 if(method && !method->is_void()) {                  if(method && !method->is_void()) {
                         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;
                           Temp_hash_value<HashStringValue, Value*> indent(params_hash, "indent", 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 365  static void _string(Request& r, MethodPa Line 588  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;                          HashStringValue* vvalue;
Line 373  static void _string(Request& r, MethodPa Line 596  static void _string(Request& r, MethodPa
                                 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++;
                                   } else if(key == "one-line"){
                                           json.one_line=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_recursion=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', 'object' or 'compact'");                                                  throw Exception(PARSER_RUNTIME, &svalue, "must be 'array', 'object' or 'compact'");
                                         valid_options++;                                          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'");
                                           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', 'text' or 'stat'");                                                  throw Exception(PARSER_RUNTIME, &svalue, "must be 'base64', 'text' or 'stat'");
                                         valid_options++;                                          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  #ifdef XML
                                 } else if(key == "xdoc" && (vvalue = value->get_hash())){                                  } else if(key == "xdoc" && (vvalue = value->get_hash())){
                                         json.xdoc_options=new XDocOutputOptions(r, vvalue);                                          json.xdoc_options=new XDocOutputOptions();
                                           json.xdoc_options->append(r, vvalue);
                                         valid_options++;                                          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++;                                          valid_options++;
Line 410  static void _string(Request& r, MethodPa Line 650  static void _string(Request& r, MethodPa
                                 throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);                                  throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
   
                         // special handling for $._default                           // special handling for $._default 
                         if(VHash* vhash=static_cast<VHash*>(params[1].as(VHASH_TYPE)))                          if(VHashBase* vhash=dynamic_cast<VHashBase*>(&params[1]))
                                 if(Value* value=vhash->get_default()) {                                  if(Value* value=vhash->get_default()) {
                                         Junction* junction=value->get_junction();                                          if(!value->is_string()){
                                         if(!junction || !junction->method || !junction->method->params_names || junction->method->params_names->count() != 3)                                                  Junction* junction=value->get_junction();
                                                 throw Exception(PARSER_RUNTIME, 0, "$.%s must be parser method with 3 parameters", HASH_DEFAULT_ELEMENT_NAME);                                                  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;                                          json.default_method=value;
                                 }                                  }
   
Line 422  static void _string(Request& r, MethodPa Line 664  static void _string(Request& r, MethodPa
                                 json.methods=methods;                                  json.methods=methods;
                 }                  }
   
         const String& result_string=value_json_string(String::Body(), 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, 0, &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=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);
 }  }

Removed from v.1.23  
changed lines
  Added in v.1.63


E-mail: