Annotation of parser3/src/classes/json.C, revision 1.13
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.13 ! moko 7: static const char * const IDENT_RESPONSE_C="$Date: 2010-10-26 11:41:42 $";
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.13 ! moko 257: const String& value_json_string(String::Body key, Value& v, Json_options* options);
1.6 misha 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: }
1.13 ! moko 278: result << String(i.key(), String::L_JSON) << "\":" << value_json_string(i.key(), *i.value(), options);
1.8 moko 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\"" : "\"");
1.13 ! moko 287: result << String(i.key(), String::L_JSON) << "\":" << value_json_string(i.key(), *i.value(), options);
1.8 moko 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:
1.13 ! moko 298: const String& value_json_string(String::Body key, Value& v, Json_options* options) {
1.6 misha 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:
1.13 ! moko 304: Value *params[]={new VString(*new String(key, String::L_JSON)), &v, (options->params) ? options->params : VVoid::get()};
! 305: frame.store_params(params, 3);
1.6 misha 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))
1.13 ! moko 343: throw Exception(PARSER_RUNTIME, &svalue, "must be 'array', 'object' or 'compact'");
1.6 misha 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()){
1.13 ! moko 351: if(!junction->method || !junction->method->params_names || junction->method->params_names->count() != 3)
! 352: throw Exception(PARSER_RUNTIME, 0, "$.%s must be parser method with 3 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: }
1.13 ! moko 362: const String& result_string=value_json_string(String::Body(), params[0], &json);
! 363: String::Body result_body=result_string.cstr_to_string_body_untaint(String::L_JSON, 0, &r.charsets);
! 364: r.write_pass_lang(*new String(result_body, String::L_AS_IS));
1.6 misha 365: }
366:
1.1 misha 367: // constructor
368:
369: MJson::MJson(): Methoded("json") {
370: add_native_method("parse", Method::CT_STATIC, _parse, 1, 2);
1.6 misha 371:
372: add_native_method("string", Method::CT_ANY, _string, 1, 2);
1.1 misha 373: }
E-mail: