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