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