Diff for /parser3/src/classes/json.C between versions 1.24 and 1.69

version 1.24, 2012/06/21 14:22:09 version 1.69, 2025/12/17 20:34:20
Line 1 Line 1
 /** @file  /** @file
         Parser: @b json parser class.          Parser: @b json parser class.
   
         Copyright (c) 2000-2012 Art. Lebedev Studio (http://www.artlebedev.com)          Copyright (c) 2000-2024 Art. Lebedev Studio (http://www.artlebedev.com)
           Authors: Konstantin Morshnev <moko@design.ru>
 */  */
   
 #include "classes.h"  #include "classes.h"
Line 9 Line 10
   
 #include "pa_request.h"  #include "pa_request.h"
 #include "pa_vbool.h"  #include "pa_vbool.h"
   #include "pa_varray.h"
   
 #include "pa_charset.h"  #include "pa_charset.h"
 #include "pa_charsets.h"  #include "pa_charsets.h"
 #include "JSON_parser.h"  #include "pa_json.h"
   
 #ifdef XML  #ifdef XML
 #include "pa_vxdoc.h"  #include "pa_vxdoc.h"
Line 25  volatile const char * IDENT_JSON_C="$Id$ Line 27  volatile const char * IDENT_JSON_C="$Id$
 class MJson: public Methoded {  class MJson: public Methoded {
 public:  public:
         MJson();          MJson();
   
           override Value* get_element(const String&);
           override const VJunction* put_element(const String&, Value*);
 };  };
   
 // global variable  // global variable
   
 DECLARE_CLASS_VAR(json, new MJson, 0);  DECLARE_CLASS_VAR(json, new MJson);
   
   bool handle_array_default=true;
   
 // methods  // methods
 struct Json {  struct Json : public PA_Allocated {
         Stack<VHash*> stack;          Stack<VHashBase*> stack;
         Stack<String*> key_stack;          Stack<String*> key_stack;
   
         String* key;          String* key;
Line 47  struct Json { Line 54  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(handle_array_default), distinct(D_EXCEPTION){}
   
         bool set_distinct(const String &value){          bool set_distinct(const String &value){
                 if (value == "first") distinct = D_FIRST;                  if (value == "first") distinct = D_FIRST;
Line 59  struct Json { Line 69  struct Json {
                 else return false;                  else return false;
                 return true;                  return true;
         }          }
   
           bool set_handle_array(const String &value){
                   if (value == "array") handle_array = true;
                   else if (value == "hash") handle_array = false;
                   else return false;
                   return true;
           }
 };  };
   
   
   Value* MJson::get_element(const String& aname) {
           if (aname=="array"){
                   return new VString(*new String(handle_array_default ? "array" : "hash"));
           }
           return Methoded::get_element(aname);
   }
   
   const VJunction* MJson::put_element(const String& aname, Value* avalue) {
           if (aname=="array"){
                   Json json(NULL);
                   if (avalue->get_string()){
                           const String& sarray=avalue->as_string();
                           if (json.set_handle_array(sarray)){
                                   handle_array_default=json.handle_array;
                                   return 0;
                           }
                           throw Exception(PARSER_RUNTIME, &sarray, "$json:array must be 'array' or 'hash'");
                   }
                   throw Exception(PARSER_RUNTIME, 0, "$json:array must be 'array' or 'hash'");
           }
           return Methoded::put_element(aname, avalue);
   }
   
   
 static void set_json_value(Json *json, Value *value){  static void set_json_value(Json *json, Value *value){
         VHash *top = json->stack.top_value();          VHashBase *top = json->stack.top_value();
         if(json->key == NULL){          if(json->key == NULL){
                 top->hash().put(String(format(top->get_hash()->count(), 0)), value);                  top->add(value);
         } else {          } else {
                 switch (json->distinct){                  switch (json->distinct){
                         case Json::D_EXCEPTION:                          case Json::D_EXCEPTION:
Line 81  static void set_json_value(Json *json, V Line 123  static void set_json_value(Json *json, V
                                 if (top->hash().put_dont_replace(*json->key, value)){                                  if (top->hash().put_dont_replace(*json->key, value)){
                                         for(int i=2;;i++){                                          for(int i=2;;i++){
                                                 String key;                                                  String key;
                                                 key << *json->key << "_" << format(i, 0);                                                  key << *json->key << "_" << pa_uitoa(i);
                                                 if (!top->hash().put_dont_replace(key, value)) break;                                                  if (!top->hash().put_dont_replace(key, value)) break;
                                         }                                          }
                                 }                                  }
Line 91  static void set_json_value(Json *json, V Line 133  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){
         String::C result = json->charset !=NULL ?           /* do_callback_withbuf guarantees a null-terminated value */
                 Charset::transcode(String::C(value->vu.str.value, value->vu.str.length), UTF8_charset, *json->charset) :          if (size_t length = strlen(value)){
                 String::C(pa_strdup(value->vu.str.value, value->vu.str.length), value->vu.str.length);                  String::C result = json->charset !=NULL ?
         return new String(result.str, json->taint, result.length);                          Charset::transcode(String::C(value, length), pa_UTF8_charset, *json->charset) :
                           String::C(pa_strdup(value, length), length);
                   return new String(result, json->taint);
           }
           return (String*)&String::Empty;
 }  }
   
 static Value *json_hook(Request &r, Junction *hook, String* key, Value* value){  static Value *json_hook(Request &r, Junction *hook, String* key, Value* value){
         VMethodFrame frame(*hook->method, r.method_frame, hook->self);  
         Value *params[]={new VString(key ? *key : String::Empty), value};          Value *params[]={new VString(key ? *key : String::Empty), value};
           METHOD_FRAME_ACTION(*hook->method, r.method_frame, hook->self, {
         frame.store_params(params, 2);                  frame.store_params(params, 2);
         r.execute_method(frame);                  r.call(frame);
                   return &frame.result();
         return &frame.result().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)
 {  {
         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 167  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 181  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 207  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);
                         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)->as_double() ));
                           } else {
                                   // JSON_STRING
                                   set_json_value(json, new VString(*json_string(json, value)));
                           }
                         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)->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)));
                         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 365  static void _parse(Request& r, MethodPar
                         if(Value* value=options->get("object")) {                          if(Value* value=options->get("object")) {
                                 json.hook_object=value->get_junction();                                  json.hook_object=value->get_junction();
                                 json.request=&r;                                  json.request=&r;
                                 if (!json.hook_object || !json.hook_object->method || !json.hook_object->method->params_names || !(json.hook_object->method->params_names->count() == 2))                                  if (!json.hook_object || !json.hook_object->method || !json.hook_object->method->params_names || !(json.hook_object->method->params_count == 2))
                                         throw Exception(PARSER_RUNTIME, 0, "$.object must be parser method with 2 parameters");                                          throw Exception(PARSER_RUNTIME, 0, "$.object must be parser method with 2 parameters");
                                 valid_options++;                                  valid_options++;
                         }                          }
                         if(Value* value=options->get("array")) {                          if(Value* value=options->get("array")) {
                                 json.hook_array=value->get_junction();                                  if(value->get_string()){
                                 json.request=&r;                                          const String& sarray=value->as_string();
                                 if (!json.hook_array || !json.hook_array->method || !json.hook_array->method->params_names || !(json.hook_array->method->params_names->count() == 2))                                          if (!json.set_handle_array(sarray))
                                         throw Exception(PARSER_RUNTIME, 0, "$.array must be parser method with 2 parameters");                                                  throw Exception(PARSER_RUNTIME, &sarray, "$.array must be parser method with 2 parameters or 'array' or 'hash'");
                                   } else {
                                           json.hook_array=value->get_junction();
                                           json.request=&r;
                                           if (!json.hook_array || !json.hook_array->method || !json.hook_array->method->params_names || !(json.hook_array->method->params_count == 2))
                                                   throw Exception(PARSER_RUNTIME, 0, "$.array must be parser method with 2 parameters or 'array' or 'hash'");
                                   }
                                 valid_options++;                                  valid_options++;
                         }                          }
                         if(valid_options!=options->count())                          if(valid_options!=options->count())
                                 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.result) r.write_no_lang(*json.result);          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 425  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 504  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(String::Body::uitoa(i.index()), i.value() ? *i.value() : static_cast<Value&>(*VVoid::get()), *this);
                   }
                   result << "\n" << (indent=get_indent(json_string_recursion-1)) << "]";
   
           } else {
   
                   bool need_delim=false;
                   for(ArrayValue::Iterator i(*array); i; i.next() ){
                           if(need_delim) result << ",\n";
                           result << value_json_string(String::Body::uitoa(i.index()), i.value() ? *i.value() : static_cast<Value&>(*VVoid::get()), *this);
                           need_delim=true;
                   }
                   result << "\n]";
   
           }
   
           return &result;
   }
   
   const String* Json_options::array_compact_json_string(ArrayValue *array) {
           if(!array || !array->count())
                   return new String("[]", String::L_AS_IS);
   
           Json_string_recursion go_down(*this);
   
           String& result = *new String("[\n", String::L_AS_IS);
   
           if (indent){
   
                   String *delim=NULL;
                   indent=get_indent(json_string_recursion);
                   for(ArrayValue::Iterator i(*array); i; i.next() ){
                           if (i.value()){
                                   if (delim){
                                           result << *delim;
                                   } else {
                                           result << indent;
                                           delim = get_array_delim(json_string_recursion);
                                   }
                                   result << value_json_string(String::Body::uitoa(i.index()), *i.value(), *this);
                           }
                   }
                   result << "\n" << (indent=get_indent(json_string_recursion-1)) << "]";
   
           } else {
   
                   bool need_delim=false;
                   for(ArrayValue::Iterator i(*array); i; i.next() ){
                           if (i.value()){
                                   if(need_delim) result << ",\n";
                                   result << value_json_string(String::Body::uitoa(i.index()), *i.value(), *this);
                                   need_delim=true;
                           }
                   }
                   result << "\n]";
   
           }
   
         return &result;          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()) {
                           static const String::Body sindent("indent");
                         Junction* junction=method->get_junction();                          Junction* junction=method->get_junction();
                         VMethodFrame frame(*junction->method, options.r->method_frame, junction->self);                          HashStringValue* params_hash=options.params && options.indent ? options.params->get_hash() : NULL;
                           Temp_hash_value<HashStringValue, Value*> indent(params_hash, sindent, new VString(*new String(options.indent, String::L_AS_IS)));
   
                         Value *params[]={new VString(*new String(key, String::L_JSON)), &v, options.params ? options.params : VVoid::get()};                          Value *params[]={new VString(*new String(key, String::L_JSON)), &v, options.params ? options.params : VVoid::get()};
                         frame.store_params(params, 3);  
   
                         options.r->execute_method(frame);                          METHOD_FRAME_ACTION(*junction->method, options.r->method_frame, junction->self, {
                                   frame.store_params(params, 3);
                         return frame.result().as_string();                                  options.r->call(frame);
                                   return frame.result().as_string();
                           });
                 }                  }
         }          }
   
Line 365  static void _string(Request& r, MethodPa Line 623  static void _string(Request& r, MethodPa
   
         if(params.count() == 2)          if(params.count() == 2)
                 if(HashStringValue* options=params.as_hash(1)) {                  if(HashStringValue* options=params.as_hash(1)) {
                         json.params=params.get(1);                          json.params=&params[1];
                         HashStringValue* methods=new HashStringValue();                          HashStringValue* methods=new HashStringValue();
                         int valid_options=0;  
                         HashStringValue* vvalue;                          HashStringValue* vvalue;
                         for(HashStringValue::Iterator i(*options); i; i.next() ){                          for(HashStringValue::Iterator i(*options); i; i.next() ){
                                 String::Body key=i.key();                                  String::Body key=i.key();
                                 Value* value=i.value();                                  Value* value=i.value();
                                 if(key == "skip-unknown"){                                  if(key == "skip-unknown"){
                                         json.skip_unknown=r.process_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();
                                 } 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++;  
                                 } else if(key == "indent"){                                  } else if(key == "indent"){
                                         json.indent=r.process_to_value(*value).as_bool() ? "":NULL;                                          if(value->is_string()){
                                         valid_options++;                                                  json.indent=value->as_string().cstr();
                                                   json.json_string_recursion=strlen(json.indent);
                                           } else json.indent=r.process(*value).as_bool() ? "" : NULL;
                                 } else if(key == "table" && value->is_string()){                                  } else if(key == "table" && value->is_string()){
                                         const String& svalue=value->as_string();                                          const String& svalue=value->as_string();
                                         if(!json.set_table_format(svalue))                                          if(!json.set_table_format(svalue))
                                                 throw Exception(PARSER_RUNTIME, &svalue, "must be 'array', 'object' or 'compact'");                                                  throw Exception(PARSER_RUNTIME, &svalue, "must be 'array', 'object' or 'compact'");
                                         valid_options++;                                  } else if(key == "array" && value->is_string()){
                                           const String& svalue=value->as_string();
                                           if(!json.set_array_format(svalue))
                                                   throw Exception(PARSER_RUNTIME, &svalue, "must be 'array', 'object' or 'compact'");
                                 } else if(key == "file" && value->is_string()){                                  } else if(key == "file" && value->is_string()){
                                         const String& svalue=value->as_string();                                          const String& svalue=value->as_string();
                                         if(!json.set_file_format(svalue))                                          if(!json.set_file_format(svalue))
                                                 throw Exception(PARSER_RUNTIME, &svalue, "must be 'base64', 'text' or 'stat'");                                                  throw Exception(PARSER_RUNTIME, &svalue, "must be 'base64', 'text' or 'stat'");
                                         valid_options++;                                  } else if(key == "void" && value->is_string()){
                                           const String& svalue=value->as_string();
                                           if(!json.set_void_format(svalue))
                                                   throw Exception(PARSER_RUNTIME, &svalue, "must be 'string' or 'null'");
 #ifdef XML  #ifdef XML
                                 } else if(key == "xdoc" && (vvalue = value->get_hash())){                                  } else if(key == "xdoc" && (vvalue = value->get_hash())){
                                         json.xdoc_options=new XDocOutputOptions();                                          json.xdoc_options=new XDocOutputOptions();
                                         json.xdoc_options->append(r, vvalue);                                          json.xdoc_options->append(r, vvalue);
                                         valid_options++;  
 #endif  #endif
                                 } else if(Junction* junction=value->get_junction()){                                  } else if(Junction* junction=value->get_junction()){
                                         if(!junction->method || !junction->method->params_names || junction->method->params_names->count() != 3)                                          if(!junction->method || !junction->method->params_names || junction->method->params_count != 3)
                                                 throw Exception(PARSER_RUNTIME, 0, "$.%s must be parser method with 3 parameters", key.cstr());                                                  throw Exception(PARSER_RUNTIME, 0, "$.%s must be parser method with 3 parameters", key.cstr());
                                         methods->put(key, value);                                          methods->put(key, value);
                                         valid_options++;                                  } else
                                 }                                          throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
                         }                          }
   
                         if(valid_options!=options->count())  
                                 throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);  
   
                         // special handling for $._default                           // special handling for $._default 
                         if(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 423  static void _string(Request& r, MethodPa Line 686  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=String::Body(result);
           }
           r.write(*new String(result_body, String::L_AS_IS));
   }
   
 // constructor  // constructor
   
 MJson::MJson(): Methoded("json") {  MJson::MJson(): Methoded("json") {
         add_native_method("parse", Method::CT_STATIC, _parse, 1, 2);          add_native_method("parse", Method::CT_STATIC, _parse, 1, 2);
   
         add_native_method("string", Method::CT_ANY, _string, 1, 2);          add_native_method("string", Method::CT_STATIC, _string, 1, 2);
 }  }

Removed from v.1.24  
changed lines
  Added in v.1.69


E-mail: