Diff for /parser3/src/classes/json.C between versions 1.14 and 1.64

version 1.14, 2011/05/18 01:25:06 version 1.64, 2024/10/16 03:28:30
Line 1 Line 1
 /** @file  /** @file
         Parser: @b json parser class.          Parser: @b json parser class.
   
         Copyright (c) 2010 ArtLebedev Group (http://www.artlebedev.com)          Copyright (c) 2000-2023 Art. Lebedev Studio (http://www.artlebedev.com)
           Authors: Konstantin Morshnev <moko@design.ru>
 */  */
   
 static const char * const IDENT_RESPONSE_C="$Date$";  
   
 #include "classes.h"  #include "classes.h"
 #include "pa_vmethod_frame.h"  #include "pa_vmethod_frame.h"
   
 #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"
 #endif  #endif
   
   volatile const char * IDENT_JSON_C="$Id$";
   
 // class  // class
   
 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;
         Value* result;          Value* result;
   
         Junction* hook;          Junction* hook_object;
           Junction* hook_array;
         Request* request;          Request* request;
   
         Charset *charset;          Charset *charset;
           String::Language taint;
   
         bool handle_double;          bool handle_double;
           bool handle_int;
           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(NULL), request(NULL), charset(acharset), handle_double(true), distinct(D_EXCEPTION){}          Json(Charset* acharset): stack(), key_stack(), key(NULL), result(NULL), hook_object(NULL), hook_array(NULL), 
                   request(NULL), charset(acharset), taint(String::L_TAINTED), handle_double(true), handle_int(true), 
                   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 55  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 77  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 87  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, uint32_t length){
         String::C result = json->charset !=NULL ?           String::C result = json->charset !=NULL ? 
                         Charset::transcode(String::C(value->vu.str.value, value->vu.str.length), UTF8_charset, *json->charset) :                  Charset::transcode(String::C(value, length), pa_UTF8_charset, *json->charset) :
                         String::C(pa_strdup(value->vu.str.value, value->vu.str.length), value->vu.str.length);                  String::C(pa_strdup(value, length), length);
         return new String(result.str, String::L_TAINTED, result.length);          return new String(result, json->taint);
 }  }
   
 static Value *json_hook(Request &r, Junction *hook, String* key, Value* value){  static Value *json_hook(Request &r, Junction *hook, String* key, Value* value){
         VMethodFrame frame(*hook->method, r.method_frame, hook->self);  
         Value *params[]={new VString(key ? *key : String::Empty), value};          Value *params[]={new VString(key ? *key : String::Empty), value};
           METHOD_FRAME_ACTION(*hook->method, r.method_frame, hook->self, {
         frame.store_params(params, 2);                  frame.store_params(params, 2);
         r.execute_method(frame);                  r.call(frame);
                   return &frame.result();
         return &frame.result().as_value();          });
 }  }
   
 static int json_callback(Json *json, int type, const JSON_value* value)  static int json_callback(Json *json, int type, const char *value, uint32_t length)
 {  {
         switch(type) {          switch(type) {
                 case JSON_T_OBJECT_BEGIN:{                  case JSON_OBJECT_BEGIN:{
                         VHash *v = new VHash();                          VHash *v = new VHash();
                         if (json->hook){                          if (json->hook_object){
                                 json->key_stack.push(json->key);                                  json->key_stack.push(json->key);
                                   json->key=NULL;
                         } else {                          } else {
                                 if (json->stack.count()) set_json_value(json, v);                                  if (json->stack.count()) set_json_value(json, v);
                         }                          }
                         json->stack.push(v);                          json->stack.push(v);
                         break;                          break;
                 }                  }
                 case JSON_T_OBJECT_END:{                  case JSON_OBJECT_END:{
                         if (json->hook){                          if (json->hook_object){
                                 String* key = json->key_stack.pop();                                  String* key = json->key_stack.pop();
                                 json->result = json_hook(*json->request, json->hook, key, json->stack.pop());                                  json->result = json_hook(*json->request, json->hook_object, key, json->stack.pop());
                                                                   
                                 if (json->stack.count()){                                  if (json->stack.count()){
                                         json->key = key;                                          json->key = key;
Line 131  static int json_callback(Json *json, int Line 177  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->stack.count()) set_json_value(json, v);                                  json->key_stack.push(json->key);
                         json->stack.push(v);                                  json->key=NULL;
                                   json->stack.push(new VHash);
                           } else {
                                   VHashBase *v = json->handle_array ? (VHashBase *)new VArray : (VHashBase *)new VHash;
                                   if (json->stack.count()) set_json_value(json, v);
                                   json->stack.push(v);
                           }
                         break;                          break;
                 }                  }
                 case JSON_T_ARRAY_END:                  case JSON_ARRAY_END:
                         // libjson supports array at top level, we too                          // libjson supports array at top level, we too
                         json->result = json->stack.pop();                          if (json->hook_array){
                                   String* key = json->key_stack.pop();
                                   json->result = json_hook(*json->request, json->hook_array, key, json->stack.pop());
                                   
                                   if (json->stack.count()){
                                           json->key = key;
                                           set_json_value(json, json->result);
                                   }
                           } else {
                                   json->result = json->stack.pop();
                           }
                         break;                          break;
                 case JSON_T_KEY:                  case JSON_KEY:
                         json->key = json_string(json, value);                          json->key = json_string(json, value, length);
                         break;    
                 case JSON_T_INTEGER:  
                         set_json_value(json, new VInt((int)value->vu.integer_value));  
                         break;                          break;
                 case JSON_T_FLOAT:                  case JSON_INT:
                           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;
                   case JSON_FLOAT:
                         if (json->handle_double){                          if (json->handle_double){
                                 set_json_value(json, new VDouble( json_string(json, value)->as_double() ));                                  set_json_value(json, new VDouble( json_string(json, value, length)->as_double() ));
                                 break;                                  break;
                         } // else is JSON_T_STRING                          } // else is JSON_STRING
                 case JSON_T_STRING:                  case JSON_STRING:
                         set_json_value(json, new VString(*json_string(json, value)));                          set_json_value(json, new VString(*json_string(json, value, length)));
                         break;                          break;
                 case JSON_T_NULL:                  case JSON_NULL:
                         set_json_value(json, new VVoid());                          set_json_value(json, VVoid::get());
                         break;                          break;
                 case JSON_T_TRUE:                  case JSON_TRUE:
                         set_json_value(json, &VBool::get(true));                          set_json_value(json, &VBool::get(true));
                         break;                          break;
                 case JSON_T_FALSE:                  case JSON_FALSE:
                         set_json_value(json, &VBool::get(false));                          set_json_value(json, &VBool::get(false));
                         break;                           break;
         }          }
         return 1;          return 0;
 }  }
   
 static const char* json_error_message(int error_code){  static const char* json_error_message(int error_code){
         static const char* error_messages[] = {          static const char* error_messages[] = {
                 NULL,                  NULL,
                 "invalid char",                  "out of memory",
                 "invalid keyword",                  "bad character",
                 "invalid escape sequence",                  "stack empty",
                 "invalid unicode sequence",                  "pop unexpected mode",
                 "invalid number",                  "nesting limit",
                 "nesting depth reached",                  "data limit",
                 "unbalanced collection",                  "comment not allowed by config",
                 "expected key",                  "unexpected character",
                 "expected colon",                  "missing unicode low surrogate",
                 "out of memory"                  "unexpected unicode low surrogate",
                   "error comma out of structure",
                   "error in a callback"
         };          };
         return error_messages[error_code];          return error_messages[error_code];
 }  }
   
   extern String::Language get_untaint_lang(const String& lang_name);
   
   #define SOURCE_MAX_LEN 60
   
   void json_exception_with_source(Request& r, const char* msg, const char* json, int offset){
           int i;
   
           int line=0;
           int start=0;
           int end=strlen(json);
   
           if(offset>end)
                   offset=end;
   
           for(i = 0; i < offset; i++){
                   if(json[i]=='\n'){
                           line++;
                   }
           }
   
           if(offset > SOURCE_MAX_LEN/2)
                   start = offset - SOURCE_MAX_LEN/2;
   
           for(i = offset-1; i>=start; i--){
                   if(json[i]=='\n'){
                           start=i+1;
                           break;
                   }
           }
   
           if(start+SOURCE_MAX_LEN < end)
                   end=start+SOURCE_MAX_LEN;
   
           for(i = offset+1; i<end; i++){
                   if(json[i]=='\n'){
                           end=i;
                           break;
                   }
           }
   
           char *source = pa_strdup(json+start, end-start);
           int source_offset = offset-start;
   
           if(source[source_offset]=='\n')
                   source[source_offset]=' ';
   
           for(i = 0; i < source_offset; i++){
                   if(source[i]=='\t'){
                           source[i]=' ';
                   }
           }
   
           if(r.charsets.source().isUTF8()){
                   source=(char *)fixUTF8(source);
                   if(source_offset>0){
                           String s_source(pa_strdup(source,source_offset));
                           source_offset=s_source.length(r.charsets.source());
                   }
           }
   
           throw Exception("json.parse", 0, "%s at line %d\n%s\n%*s", msg, line+1, source, source_offset+1, "^");
   }
   
 static void _parse(Request& r, MethodParams& params) {  static void _parse(Request& r, MethodParams& params) {
         const String& json_string=params.as_string(0, "json must be string");          const String& json_string=params.as_string(0, "json must be string");
   
         Json json(r.charsets.source().isUTF8() ? NULL : &(r.charsets.source()));          Json json(r.charsets.source().isUTF8() ? NULL : &(r.charsets.source()));
   
         JSON_config config;          json_config config = {
         init_JSON_config(&config);                  0,              // buffer_initial_size
                   128,            // max_nesting
         config.depth                  = 19;                  0,              // max_data
         config.callback               = (JSON_parser_callback)&json_callback;                  1,              // allow_c_comments
         config.allow_comments         = 1;                  1,              // allow_yaml_comments
         config.handle_floats_manually = 1;                  pa_malloc,
         config.callback_ctx           = &json;                  pa_realloc,
                   pa_free
           };
   
         if(params.count() == 2)          if(params.count() == 2)
                 if(HashStringValue* options=params.as_hash(1)) {                  if(HashStringValue* options=params.as_hash(1)) {
                         int valid_options=0;                          int valid_options=0;
                         if(Value* value=options->get("depth")) {                          if(Value* value=options->get("depth")) {
                                 config.depth=r.process_to_value(*value).as_int();                                  config.max_nesting=r.process(*value).as_int();
                                 valid_options++;                                  valid_options++;
                         }                          }
                         if(Value* value=options->get("double")) {                          if(Value* value=options->get("double")) {
                                 json.handle_double=r.process_to_value(*value).as_bool();                                  json.handle_double=r.process(*value).as_bool();
                                   valid_options++;
                           }
                           if(Value* value=options->get("int")) {
                                   json.handle_int=r.process(*value).as_bool();
                                 valid_options++;                                  valid_options++;
                         }                          }
                         if(Value* value=options->get("distinct")) {                          if(Value* value=options->get("distinct")) {
Line 216  static void _parse(Request& r, MethodPar Line 354  static void _parse(Request& r, MethodPar
                                         throw Exception(PARSER_RUNTIME, &sdistinct, "must be 'first', 'last' or 'all'");                                          throw Exception(PARSER_RUNTIME, &sdistinct, "must be 'first', 'last' or 'all'");
                                 valid_options++;                                  valid_options++;
                         }                          }
                           if(Value* value=options->get("taint")) {
                                   json.taint=get_untaint_lang(value->as_string());
                                   valid_options++;
                           }
                         if(Value* value=options->get("object")) {                          if(Value* value=options->get("object")) {
                                 json.hook=value->get_junction();                                  json.hook_object=value->get_junction();
                                 json.request=&r;                                  json.request=&r;
                                 if (!json.hook || !json.hook->method || !json.hook->method->params_names || !(json.hook->method->params_names->count() == 2))                                  if (!json.hook_object || !json.hook_object->method || !json.hook_object->method->params_names || !(json.hook_object->method->params_count == 2))
                                         throw Exception(PARSER_RUNTIME, 0, "$.object must be parser method with 2 parameters");                                          throw Exception(PARSER_RUNTIME, 0, "$.object must be parser method with 2 parameters");
                                 valid_options++;                                  valid_options++;
                         }                          }
                           if(Value* value=options->get("array")) {
                                   if(value->get_string()){
                                           const String& sarray=value->as_string();
                                           if (!json.set_handle_array(sarray))
                                                   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++;
                           }
                         if(valid_options!=options->count())                          if(valid_options!=options->count())
                                 throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);                                  throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
                 }                  }
   
         const String::Body json_body = json_string.cstr_to_string_body_untaint(String::L_JSON, 0, &(r.charsets));          const String::Body json_body = json_string.cstr_to_string_body_untaint(String::L_JSON, r.connection(false), &r.charsets);
         const char *json_cstr = json.charset != NULL ? Charset::transcode(json_body, *json.charset, UTF8_charset).cstr() : json_body.cstr();          const char *json_cstr = json.charset != NULL ? Charset::transcode(json_body, *json.charset, pa_UTF8_charset).cstr() : json_body.cstr();
   
         struct JSON_parser_struct* jc = new_JSON_parser(&config);          json_parser parser;
           if(int result = json_parser_init(&parser, &config, (json_parser_callback)&json_callback, &json))
                   throw Exception("json.parse", 0, "%s", json_error_message(result));
   
         for (const char *c=json_cstr; *c; c++){          if(!*json_cstr)
                 if (!JSON_parser_char(jc, *((const unsigned char *)c))) {                  throw Exception("json.parse", 0, "empty string is not valid json");
                         throw Exception("json.parse", 0, "%s at byte %d", json_error_message(JSON_parser_get_last_error(jc)), c-json_cstr);  
                 }  
         }  
   
         if (!JSON_parser_done(jc)) {          const char *first_quote=strchr(json_cstr,'"');
                 throw Exception("json.parse", 0, "%s at the end", json_error_message(JSON_parser_get_last_error(jc)));          if(first_quote && first_quote>json_cstr && *(--first_quote) == '\\')
         }                  json_exception_with_source(r, "illegal quote escape, json may be tainted", json_cstr, first_quote-json_cstr);
           
         delete_JSON_parser(jc);          uint32_t processed;
           if(int result = json_parser_string(&parser, json_cstr, strlen(json_cstr), &processed))
                   json_exception_with_source(r, json_error_message(result), json_cstr, processed);
   
           if (!json_parser_is_done(&parser))
                   json_exception_with_source(r, "unexpected end of json data", json_cstr, processed);
   
         if (json.result) r.write_no_lang(*json.result);          json_parser_free(&parser);
   
           if (json.result) r.write(*json.result);
 }  }
   
   const uint ANTI_ENDLESS_JSON_STRING_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 258  char *get_indent(uint level){ Line 421  char *get_indent(uint level){
         return cache[level];          return cache[level];
 }  }
   
 const String& value_json_string(String::Body key, Value& v, Json_options* options);  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& hash_json_string(HashStringValue &hash, Json_options* options) {  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 = options->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 (options->indent){          if (indent){
   
                 String *delim=NULL;                  String *delim=NULL;
                 options->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 << options->indent << "\"";                                  result << indent << "\"";
                                 delim = new String(",\n", String::L_AS_IS); *delim << options->indent << "\"";                                  delim = get_delim(json_string_recursion);
                         }                          }
                         result << String(i.key(), String::L_JSON) << "\":" << value_json_string(i.key(), *i.value(), options);                          result << String(i.key(), String::L_JSON) << "\":" << value_json_string(i.key(), *i.value(), *this);
                 }                  }
                 result << "\n" << (options->indent=get_indent(level-1)) << "}";                  result << "\n" << (indent=get_indent(json_string_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(), options);                          result << String(i.key(), String::L_JSON) << "\":" << value_json_string(i.key(), *i.value(), *this);
                         need_delim=true;                          need_delim=true;
                 }                  }
                 result << "\n}";                  result << "\n}";
   
         }          }
   
         options->r->json_string_recoursion_go_up();          return &result;
         return result;  
 }  }
   
 const String& value_json_string(String::Body key, Value& v, Json_options* options) {  const String* Json_options::array_json_string(ArrayValue *array) {
         if(options && options->methods)          if(!array || !array->count())
                 if(Value* method=options->methods->get(v.type())){                  return new String("[]", String::L_AS_IS);
                         Junction* junction=method->get_junction();  
                         VMethodFrame frame(*junction->method, options->r->method_frame, junction->self);          Json_string_recursion go_down(*this);
   
           String& result = *new String("[\n", String::L_AS_IS);
   
                         Value *params[]={new VString(*new String(key, String::L_JSON)), &v, (options->params) ? options->params : VVoid::get()};          if (indent){
                         frame.store_params(params, 3);  
                   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)) << "]";
   
                         options->r->execute_method(frame);          } else {
   
                         return frame.result().as_string();                  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);
   
         if(HashStringValue* hash=v.get_hash())          Json_string_recursion go_down(*this);
                 return hash_json_string(*hash, options);  
   
           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;
   }
   
   static bool based_on(HashStringValue::key_type key, HashStringValue::value_type /*value*/, Value* v) {
           return v->is(key.cstr());
   }
   
   const String& value_json_string(String::Body key, Value& v, Json_options& options) {
           if(options.methods) {
                   Value* method=options.methods->get(v.type());
                   if(!method){
                           method=options.methods->first_that<Value*>(based_on, &v);
                           options.methods->put(v.type(), method ? method : VVoid::get());
                   }
                   if(method && !method->is_void()) {
                           Junction* junction=method->get_junction();
                           HashStringValue* params_hash=options.params && options.indent ? options.params->get_hash() : NULL;
                           Temp_hash_value<HashStringValue, Value*> indent(params_hash, "indent", new VString(*new String(options.indent, String::L_AS_IS)));
   
                           Value *params[]={new VString(*new String(key, String::L_JSON)), &v, options.params ? options.params : VVoid::get()};
   
                           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();
                           });
                   }
           }
   
           options.key=key;
         return *v.get_json_string(options);          return *v.get_json_string(options);
 }  }
   
Line 324  static void _string(Request& r, MethodPa Line 618  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 332  static void _string(Request& r, MethodPa Line 626  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' or 'text'");                                                  throw Exception(PARSER_RUNTIME, &svalue, "must be 'base64', 'text' or 'stat'");
                                           valid_options++;
                                   } else if(key == "void" && value->is_string()){
                                           const String& svalue=value->as_string();
                                           if(!json.set_void_format(svalue))
                                                   throw Exception(PARSER_RUNTIME, &svalue, "must be 'string' or 'null'");
                                         valid_options++;                                          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++;
                                 }                                  }
                         }                          }
   
                         if(valid_options!=options->count())                          if(valid_options!=options->count())
                                 throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);                                  throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
   
                           // special handling for $._default 
                           if(VHashBase* vhash=dynamic_cast<VHashBase*>(&params[1]))
                                   if(Value* value=vhash->get_default()) {
                                           if(!value->is_string()){
                                                   Junction* junction=value->get_junction();
                                                   if(!junction || !junction->method || !junction->method->params_names || junction->method->params_count != 3)
                                                           throw Exception(PARSER_RUNTIME, 0, "$._default must be string or parser method with 3 parameters");
                                           }
                                           json.default_method=value;
                                   }
   
                         if(methods->count())                          if(methods->count())
                                 json.methods=methods;                                  json.methods=methods;
                 }                  }
   
         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.14  
changed lines
  Added in v.1.64


E-mail: