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