Annotation of parser3/src/classes/json.C, revision 1.12

1.1       misha       1: /** @file
                      2:        Parser: @b json parser class.
                      3: 
1.3       moko        4:        Copyright (c) 2010 ArtLebedev Group (http://www.artlebedev.com)
1.1       misha       5: */
                      6: 
1.12    ! moko        7: static const char * const IDENT_RESPONSE_C="$Date: 2010-10-21 15:06:29 $";
1.1       misha       8: 
                      9: #include "classes.h"
                     10: #include "pa_vmethod_frame.h"
                     11: 
                     12: #include "pa_request.h"
                     13: #include "pa_vbool.h"
                     14: 
                     15: #include "pa_charset.h"
                     16: #include "pa_charsets.h"
                     17: #include "JSON_parser.h"
                     18: 
                     19: // class
                     20: 
                     21: class MJson: public Methoded {
                     22: public:
                     23:        MJson();
                     24: };
                     25: 
                     26: // global variable
                     27: 
                     28: DECLARE_CLASS_VAR(json, new MJson, 0);
                     29: 
                     30: // methods
                     31: struct Json {
1.4       moko       32:        Stack<VHash*> stack;
1.3       moko       33:        Stack<String*> key_stack;
1.1       misha      34: 
1.3       moko       35:        String* key;
1.1       misha      36:        Value* result;
                     37: 
                     38:        Junction* hook;
1.3       moko       39:        Request* request;
                     40: 
1.1       misha      41:        Charset *charset;
                     42:        bool handle_double;
1.4       moko       43:        enum Distinct { D_EXCEPTION, D_FIRST, D_LAST, D_ALL } distinct;
1.3       moko       44: 
1.4       moko       45:        Json(Charset* acharset): stack(), key_stack(), key(NULL), result(NULL), hook(NULL), request(NULL), charset(acharset), handle_double(true), distinct(D_EXCEPTION){}
                     46: 
                     47:        bool set_distinct(const String &value){
                     48:                if (value == "first") distinct = D_FIRST;
                     49:                else if (value == "last") distinct = D_LAST;
                     50:                else if (value == "all") distinct = D_ALL;
                     51:                else return false;
                     52:                return true;
                     53:        }
1.1       misha      54: };
                     55: 
                     56: static void set_json_value(Json *json, Value *value){
1.4       moko       57:        VHash *top = json->stack.top_value();
1.3       moko       58:        if(json->key == NULL){
1.4       moko       59:                top->hash().put(String(format(top->get_hash()->count(), 0)), value);
1.1       misha      60:        } else {
1.4       moko       61:                switch (json->distinct){
                     62:                        case Json::D_EXCEPTION:
                     63:                                if (top->hash().put_dont_replace(*json->key, value))
                     64:                                        throw Exception(PARSER_RUNTIME, json->key, "duplicate key");
                     65:                                break;
                     66:                        case Json::D_FIRST:
                     67:                                top->hash().put_dont_replace(*json->key, value);
                     68:                                break;
                     69:                        case Json::D_LAST:
                     70:                                top->hash().put(*json->key, value);
                     71:                                break;
                     72:                        case Json::D_ALL:
                     73:                                if (top->hash().put_dont_replace(*json->key, value)){
                     74:                                        for(int i=2;;i++){
                     75:                                                String key;
                     76:                                                key << *json->key << "_" << format(i, 0);
                     77:                                                if (!top->hash().put_dont_replace(key, value)) break;
                     78:                                        }
                     79:                                }
                     80:                                break;
                     81:                }
1.3       moko       82:                json->key=NULL;
1.1       misha      83:        }
                     84: }
                     85: 
1.3       moko       86: String* json_string(Json *json, const JSON_value* value){
                     87:        String::C result = json->charset !=NULL ? 
                     88:                        Charset::transcode(String::C(value->vu.str.value, value->vu.str.length), UTF8_charset, *json->charset) :
                     89:                        String::C(pa_strdup(value->vu.str.value, value->vu.str.length), value->vu.str.length);
                     90:        return new String(result.str, String::L_TAINTED, result.length);
1.1       misha      91: }
                     92: 
1.3       moko       93: static Value *json_hook(Request &r, Junction *hook, String* key, Value* value){
                     94:        VMethodFrame frame(*hook->method, r.method_frame, hook->self);
1.10      moko       95:        Value *params[]={new VString(key ? *key : String::Empty), value};
1.3       moko       96: 
                     97:        frame.store_params(params, 2);
                     98:        r.execute_method(frame);
                     99: 
                    100:        return &frame.result().as_value();
1.1       misha     101: }
                    102: 
                    103: static int json_callback(Json *json, int type, const JSON_value* value)
                    104: {
                    105:        switch(type) {
                    106:                case JSON_T_OBJECT_BEGIN:{
1.4       moko      107:                        VHash *v = new VHash();
1.1       misha     108:                        if (json->hook){
                    109:                                json->key_stack.push(json->key);
                    110:                        } else {
                    111:                                if (json->stack.count()) set_json_value(json, v);
                    112:                        }
                    113:                        json->stack.push(v);
                    114:                        break;
                    115:                }
                    116:                case JSON_T_OBJECT_END:{
                    117:                        if (json->hook){
1.3       moko      118:                                String* key = json->key_stack.pop();
                    119:                                json->result = json_hook(*json->request, json->hook, key, json->stack.pop());
1.1       misha     120:                                
                    121:                                if (json->stack.count()){
                    122:                                        json->key = key;
                    123:                                        set_json_value(json, json->result);
                    124:                                }
                    125:                        } else {
                    126:                                json->result = json->stack.pop();
                    127:                        }
                    128:                        break;
                    129:                }
                    130:                case JSON_T_ARRAY_BEGIN:{
1.4       moko      131:                        VHash *v = new VHash();
1.12    ! moko      132:                        if (json->stack.count()) set_json_value(json, v);
1.1       misha     133:                        json->stack.push(v);
                    134:                        break;
                    135:                }
                    136:                case JSON_T_ARRAY_END:
1.12    ! moko      137:                        // libjson supports array at top level, we too
        !           138:                        json->result = json->stack.pop();
1.1       misha     139:                        break;
                    140:                case JSON_T_KEY:
                    141:                        json->key = json_string(json, value);
                    142:                        break;  
                    143:                case JSON_T_INTEGER:
                    144:                        set_json_value(json, new VInt((int)value->vu.integer_value));
                    145:                        break;
                    146:                case JSON_T_FLOAT:
                    147:                        if (json->handle_double){
1.3       moko      148:                                set_json_value(json, new VDouble( json_string(json, value)->as_double() ));
1.1       misha     149:                                break;
                    150:                        } // else is JSON_T_STRING
                    151:                case JSON_T_STRING:
1.3       moko      152:                        set_json_value(json, new VString(*json_string(json, value)));
1.1       misha     153:                        break;
                    154:                case JSON_T_NULL:
                    155:                        set_json_value(json, new VVoid());
                    156:                        break;
                    157:                case JSON_T_TRUE:
                    158:                        set_json_value(json, &VBool::get(true));
                    159:                        break;
                    160:                case JSON_T_FALSE:
                    161:                        set_json_value(json, &VBool::get(false));
                    162:                        break; 
                    163:        }
                    164:        return 1;
                    165: }
                    166: 
1.5       moko      167: static const char* json_error_message(int error_code){
                    168:        static const char* error_messages[] = {
1.1       misha     169:                NULL,
                    170:                "invalid char",
                    171:                "invalid keyword",
                    172:                "invalid escape sequence",
                    173:                "invalid unicode sequence",
                    174:                "invalid number",
                    175:                "nesting depth reached",
                    176:                "unbalanced collection",
                    177:                "expected key",
                    178:                "expected colon",
                    179:                "out of memory"
                    180:        };
                    181:        return error_messages[error_code];
                    182: }
                    183: 
                    184: static void _parse(Request& r, MethodParams& params) {
1.3       moko      185:        const String& json_string=params.as_string(0, "json must be string");
                    186: 
                    187:        Json json(r.charsets.source().isUTF8() ? NULL : &(r.charsets.source()));
1.1       misha     188: 
                    189:        JSON_config config;
                    190:        init_JSON_config(&config);
                    191: 
                    192:        config.depth                  = 19;
                    193:        config.callback               = (JSON_parser_callback)&json_callback;
                    194:        config.allow_comments         = 1;
                    195:        config.handle_floats_manually = 1;
                    196:        config.callback_ctx           = &json;
                    197: 
                    198:        if(params.count() == 2)
                    199:                if(HashStringValue* options=params.as_hash(1)) {
                    200:                        int valid_options=0;
                    201:                        if(Value* value=options->get("depth")) {
1.4       moko      202:                                config.depth=r.process_to_value(*value).as_int();
1.1       misha     203:                                valid_options++;
                    204:                        }
                    205:                        if(Value* value=options->get("double")) {
1.4       moko      206:                                json.handle_double=r.process_to_value(*value).as_bool();
                    207:                                valid_options++;
                    208:                        }
                    209:                        if(Value* value=options->get("distinct")) {
                    210:                                const String& sdistinct=value->as_string();
                    211:                                if (!json.set_distinct(sdistinct))
                    212:                                        throw Exception(PARSER_RUNTIME, &sdistinct, "must be 'first', 'last' or 'all'");
1.1       misha     213:                                valid_options++;
                    214:                        }
                    215:                        if(Value* value=options->get("object")) {
                    216:                                json.hook=value->get_junction();
1.3       moko      217:                                json.request=&r;
                    218:                                if (!json.hook || !json.hook->method || !json.hook->method->params_names || !(json.hook->method->params_names->count() == 2))
1.1       misha     219:                                        throw Exception(PARSER_RUNTIME, 0, "$.object must be parser method with 2 parameters");
                    220:                                valid_options++;
                    221:                        }
                    222:                        if(valid_options!=options->count())
                    223:                                throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
                    224:                }
                    225: 
1.2       misha     226:        const String::Body json_body = json_string.cstr_to_string_body_untaint(String::L_JSON, 0, &(r.charsets));
1.1       misha     227:        const char *json_cstr = json.charset != NULL ? Charset::transcode(json_body, *json.charset, UTF8_charset).cstr() : json_body.cstr();
                    228: 
1.3       moko      229:        struct JSON_parser_struct* jc = new_JSON_parser(&config);
                    230: 
1.1       misha     231:        for (const char *c=json_cstr; *c; c++){
                    232:                if (!JSON_parser_char(jc, *((const unsigned char *)c))) {
                    233:                        throw Exception("json.parse", 0, "%s at byte %d", json_error_message(JSON_parser_get_last_error(jc)), c-json_cstr);
                    234:                }
                    235:        }
                    236: 
                    237:        if (!JSON_parser_done(jc)) {
                    238:                throw Exception("json.parse", 0, "%s at the end", json_error_message(JSON_parser_get_last_error(jc)));
                    239:        }
                    240:        
                    241:        delete_JSON_parser(jc);
                    242: 
                    243:        if (json.result) r.write_no_lang(*json.result);
                    244: }
                    245: 
1.8       moko      246: char *get_indent(uint level){
                    247:        static char* cache[ANTI_ENDLESS_JSON_STRING_RECOURSION]={};
                    248:        if (!cache[level]){
                    249:                char *result =  static_cast<char*>(pa_gc_malloc_atomic(level+1));
                    250:                memset(result, '\t', level);
1.9       moko      251:                result[level]='\0';
1.8       moko      252:                return cache[level]=result;
                    253:        }
                    254:        return cache[level];
                    255: }
                    256: 
1.6       misha     257: const String& value_json_string(Value& v, Json_options* options);
                    258: 
                    259: const String& hash_json_string(HashStringValue &hash, Json_options* options) {
                    260:        if(!hash.count())
1.8       moko      261:                return *new String("{}", String::L_AS_IS);
                    262: 
                    263:        uint level = options->r->json_string_recoursion_go_down();
                    264: 
                    265:        String& result = *new String("{\n", String::L_AS_IS);
                    266: 
                    267:        if (options->indent){
                    268: 
                    269:                String *delim=NULL;
                    270:                options->indent=get_indent(level);
                    271:                for(HashStringValue::Iterator i(hash); i; i.next() ){
                    272:                        if (delim){
                    273:                                result << *delim;
                    274:                        } else {
                    275:                                result << options->indent << "\"";
                    276:                                delim = new String(",\n", String::L_AS_IS); *delim << options->indent << "\"";
                    277:                        }
                    278:                        result << String(i.key(), String::L_JSON) << "\":" << value_json_string(*i.value(), options);
                    279:                }
                    280:                result << "\n" << (options->indent=get_indent(level-1)) << "}";
1.6       misha     281: 
1.8       moko      282:        } else {
                    283: 
                    284:                bool need_delim=false;
                    285:                for(HashStringValue::Iterator i(hash); i; i.next() ){
                    286:                        result << (need_delim ? ",\n\"" : "\"");
                    287:                        result << String(i.key(), String::L_JSON) << "\":" << value_json_string(*i.value(), options);
                    288:                        need_delim=true;
                    289:                }
                    290:                result << "\n}";
1.6       misha     291: 
                    292:        }
                    293: 
                    294:        options->r->json_string_recoursion_go_up();
                    295:        return result;
                    296: }
                    297: 
                    298: const String& value_json_string(Value& v, Json_options* options) {
                    299:        if(options && options->methods)
                    300:                if(Value* method=options->methods->get(v.type())){
                    301:                        Junction* junction=method->get_junction();
                    302:                        VMethodFrame frame(*junction->method, options->r->method_frame, junction->self);
                    303: 
                    304:                        Value *params[]={&v, (options->params) ? options->params : VVoid::get()};
                    305:                        frame.store_params(params, 2);
                    306: 
                    307:                        options->r->execute_method(frame);
                    308: 
                    309:                        return frame.result().as_string();
                    310:                }
                    311: 
                    312:        if(HashStringValue* hash=v.get_hash())
                    313:                return hash_json_string(*hash, options);
                    314: 
                    315:        return *v.get_json_string(options);
                    316: }
                    317: 
                    318: static void _string(Request& r, MethodParams& params) {
                    319:        Json_options json(&r);
                    320: 
                    321:        if(params.count() == 2)
                    322:                if(HashStringValue* options=params.as_hash(1)) {
                    323:                        json.params=params.get(1);
                    324:                        HashStringValue* methods=new HashStringValue();
                    325:                        int valid_options=0;
                    326:                        for(HashStringValue::Iterator i(*options); i; i.next() ){
                    327:                                String::Body key=i.key();
                    328:                                Value* value=i.value();
                    329:                                if(key == "skip-unknown"){
                    330:                                        json.skip_unknown=r.process_to_value(*value).as_bool();
                    331:                                        valid_options++;
                    332:                                } else if(key == "date" && value->is_string()){
                    333:                                        const String& svalue=value->as_string();
                    334:                                        if(!json.set_date_format(svalue))
                    335:                                                throw Exception(PARSER_RUNTIME, &svalue, "must be 'sql-string', 'gmt-string' or 'unix-timestamp'");
                    336:                                        valid_options++;
1.8       moko      337:                                } else if(key == "indent"){
                    338:                                        json.indent=r.process_to_value(*value).as_bool() ? "":NULL;
                    339:                                        valid_options++;
1.6       misha     340:                                } else if(key == "table" && value->is_string()){
                    341:                                        const String& svalue=value->as_string();
                    342:                                        if(!json.set_table_format(svalue))
                    343:                                                throw Exception(PARSER_RUNTIME, &svalue, "must be 'array' or 'object'");
                    344:                                        valid_options++;
                    345:                                } else if(key == "file" && value->is_string()){
                    346:                                        const String& svalue=value->as_string();
                    347:                                        if(!json.set_file_format(svalue))
                    348:                                                throw Exception(PARSER_RUNTIME, &svalue, "must be 'base64' or 'text'");
                    349:                                        valid_options++;
                    350:                                } else if(Junction* junction=value->get_junction()){
                    351:                                        if(!junction->method || !junction->method->params_names || junction->method->params_names->count() != 2)
1.9       moko      352:                                                throw Exception(PARSER_RUNTIME, 0, "$.%s must be parser method with 2 parameters", key.cstr());
1.6       misha     353:                                        methods->put(key, value);
                    354:                                        valid_options++;
                    355:                                }
                    356:                        }
                    357:                        if(valid_options!=options->count())
                    358:                                throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
                    359:                        if(methods->count())
                    360:                                json.methods=methods;
                    361:                }
                    362: 
                    363:        r.write_pass_lang(value_json_string(params[0], &json));
                    364:  }
                    365: 
1.1       misha     366: // constructor
                    367: 
                    368: MJson::MJson(): Methoded("json") {
                    369:        add_native_method("parse", Method::CT_STATIC, _parse, 1, 2);
1.6       misha     370: 
                    371:        add_native_method("string", Method::CT_ANY, _string, 1, 2);
1.1       misha     372: }

E-mail: