JSON to XML patches from Scott McKellar:
[OpenSRF.git] / src / libopensrf / osrf_json_xml.c
1 #include <opensrf/osrf_json_xml.h>
2
3 #ifdef OSRF_JSON_ENABLE_XML_UTILS
4
5 struct osrfXMLGatewayParserStruct {
6     osrfList* objStack;
7     osrfList* keyStack;
8     jsonObject* obj;
9     short inString;
10     short inNumber;
11     short error;
12 };
13 typedef struct osrfXMLGatewayParserStruct osrfXMLGatewayParser;
14
15 /** returns the attribute value with the given attribute name */
16 static char* getXMLAttr(const xmlChar** atts, const char* attr_name) {
17     int i;
18     if (atts != NULL) {
19         for(i = 0; (atts[i] != NULL); i++) {
20             if(strcmp((char*) atts[i++], attr_name) == 0) {
21                 if(atts[i] != NULL) 
22                     return (char*) atts[i];
23             }
24         }
25     }
26     return NULL;
27 }
28
29
30 static void appendChild(osrfXMLGatewayParser* p, jsonObject* obj) {
31
32     if(p->obj == NULL) 
33         p->obj = obj;
34
35     if(p->objStack->size == 0)
36         return;
37     
38     jsonObject* parent = OSRF_LIST_GET_INDEX(p->objStack, p->objStack->size - 1);
39
40     if(parent->type == JSON_ARRAY) {
41         jsonObjectPush(parent, obj);
42     } else {
43         char* key = osrfListPop(p->keyStack);
44         jsonObjectSetKey(parent, key, obj);
45         free(key); /* the list is not setup for auto-freeing */
46     }
47 }
48
49
50
51 static void startElementHandler(
52     void *parser, const xmlChar *name, const xmlChar **atts) {
53
54     osrfXMLGatewayParser* p = (osrfXMLGatewayParser*) parser;
55     jsonObject* obj;
56
57     char* hint = getXMLAttr(atts, "class_hint");
58
59     if(!strcmp((char*) name, "null")) {
60         appendChild(p, jsonNewObject(NULL));
61         return;
62     }
63
64     if(!strcmp((char*) name, "string")) {
65         p->inString = 1;
66         return;
67     }
68
69     if(!strcmp((char*) name, "element")) {
70        osrfListPush(p->keyStack, strdup(getXMLAttr(atts, "key")));
71        return;
72     }
73
74     if(!strcmp((char*) name, "object")) {
75         obj = jsonNewObject(NULL);
76         jsonObjectSetClass(obj, hint); /* OK if hint is NULL */
77         obj->type = JSON_HASH;
78         appendChild(p, obj);
79         osrfListPush(p->objStack, obj);
80         return;
81     }
82
83     if(!strcmp((char*) name, "array")) {
84         obj = jsonNewObject(NULL);
85         jsonObjectSetClass(obj, hint); /* OK if hint is NULL */
86         obj->type = JSON_ARRAY;
87         appendChild(p, obj);
88         osrfListPush(p->objStack, obj);
89         return;
90     }
91
92
93     if(!strcmp((char*) name, "number")) {
94         p->inNumber = 1;
95         return;
96     }
97
98     if(!strcmp((char*) name, "boolean")) {
99         obj = jsonNewObject(NULL);
100         obj->type = JSON_BOOL;
101         char* val = getXMLAttr(atts, "value");
102         if(val && !strcmp(val, "true"))
103             obj->value.b = 1;
104         
105         return;
106     }
107 }
108
109 static void endElementHandler( void *parser, const xmlChar *name) {
110     if(!strcmp((char*) name, "array") || !strcmp((char*) name, "object")) {
111         osrfXMLGatewayParser* p = (osrfXMLGatewayParser*) parser;
112         osrfListPop(p->objStack);
113     }
114 }
115
116 static void characterHandler(void *parser, const xmlChar *ch, int len) {
117
118     char data[len+1];
119     strncpy(data, (char*) ch, len);
120     data[len] = '\0';
121     osrfXMLGatewayParser* p = (osrfXMLGatewayParser*) parser;
122
123     if(p->inString) {
124         appendChild(p, jsonNewObject(data));
125         p->inString = 0;
126         return;
127     }
128
129     if(p->inNumber) {
130         appendChild(p, jsonNewNumberObject(atof(data)));
131         p->inNumber = 0;
132         return;
133     }
134 }
135
136 static void parseWarningHandler(void *parser, const char* msg, ...) {
137     VA_LIST_TO_STRING(msg);
138     fprintf(stderr, "Parser warning %s\n", VA_BUF);
139     fflush(stderr);
140 }
141
142 static void parseErrorHandler(void *parser, const char* msg, ...) {
143
144     VA_LIST_TO_STRING(msg);
145     fprintf(stderr, "Parser error %s\n", VA_BUF);
146     fflush(stderr);
147
148     osrfXMLGatewayParser* p = (osrfXMLGatewayParser*) parser;
149
150     /*  keyStack as strdup'ed strings.  The list may
151      *  not be empty, so tell it to free the items
152      *  when it's freed (from the main routine)
153      */
154     osrfListSetDefaultFree(p->keyStack);
155     jsonObjectFree(p->obj);
156
157     p->obj = NULL;
158     p->error = 1;
159 }
160
161
162
163
164 static xmlSAXHandler SAXHandlerStruct = {
165     NULL,                            /* internalSubset */
166     NULL,                            /* isStandalone */
167     NULL,                            /* hasInternalSubset */
168     NULL,                            /* hasExternalSubset */
169     NULL,                            /* resolveEntity */
170     NULL,                            /* getEntity */
171     NULL,                            /* entityDecl */
172     NULL,                            /* notationDecl */
173     NULL,                            /* attributeDecl */
174     NULL,                            /* elementDecl */
175     NULL,                            /* unparsedEntityDecl */
176     NULL,                            /* setDocumentLocator */
177     NULL,                            /* startDocument */
178     NULL,                            /* endDocument */
179     startElementHandler,        /* startElement */
180     endElementHandler,      /* endElement */
181     NULL,                            /* reference */
182     characterHandler,           /* characters */
183     NULL,                            /* ignorableWhitespace */
184     NULL,                            /* processingInstruction */
185     NULL,                            /* comment */
186     parseWarningHandler,     /* xmlParserWarning */
187     parseErrorHandler,       /* xmlParserError */
188     NULL,                            /* xmlParserFatalError : unused */
189     NULL,                            /* getParameterEntity */
190     NULL,                            /* cdataBlock; */
191     NULL,                            /* externalSubset; */
192     1,
193     NULL,
194     NULL,                            /* startElementNs */
195     NULL,                            /* endElementNs */
196     NULL                            /* xmlStructuredErrorFunc */
197 };
198
199 static const xmlSAXHandlerPtr SAXHandler = &SAXHandlerStruct;
200
201 jsonObject* jsonXMLToJSONObject(const char* xml) {
202
203     osrfXMLGatewayParser parser;
204
205     /* don't define freeItem, since objects will be cleaned by freeing the parent */
206     parser.objStack = osrfNewList(); 
207     /* don't define freeItem, since the list eill end up empty if there are no errors*/
208     parser.keyStack = osrfNewList(); 
209     parser.obj = NULL;
210     parser.inString = 0;
211     parser.inNumber = 0;
212
213     xmlParserCtxtPtr ctxt = xmlCreatePushParserCtxt(SAXHandler, &parser, "", 0, NULL);
214     xmlParseChunk(ctxt, xml, strlen(xml), 1);
215
216     osrfListFree(parser.objStack);
217     osrfListFree(parser.keyStack);
218     xmlFreeParserCtxt(ctxt);
219     xmlCleanupCharEncodingHandlers();
220     xmlDictCleanup();
221     xmlCleanupParser();
222
223     return parser.obj;
224 }
225
226
227
228
229
230
231 static char* _escape_xml (const char*);
232 static int _recurse_jsonObjectToXML(const jsonObject*, growing_buffer*);
233
234 char* jsonObjectToXML(const jsonObject* obj) {
235
236         if (!obj)
237                 return strdup("<null/>");
238         
239         growing_buffer * res_xml = buffer_init(1024);
240
241         _recurse_jsonObjectToXML( obj, res_xml );
242         return buffer_release(res_xml);
243
244 }
245
246 int _recurse_jsonObjectToXML(const jsonObject* obj, growing_buffer* res_xml) {
247
248         char * hint = NULL;
249         
250         if (obj->classname)
251                 hint = strdup(obj->classname);
252
253         if(obj->type == JSON_NULL) {
254
255                 if (hint)
256                         buffer_fadd(res_xml, "<null class_hint=\"%s\"/>",hint);
257                 else
258                         buffer_add(res_xml, "<null/>");
259
260         } else if(obj->type == JSON_BOOL) {
261
262                 const char* bool_val;
263                 if (obj->value.b)
264                         bool_val = "true";
265                 else
266                         bool_val = "false";
267
268                 if (hint)
269                         buffer_fadd(res_xml, "<boolean value=\"%s\" class_hint=\"%s\"/>", bool_val, hint);
270                 else
271                         buffer_fadd(res_xml, "<boolean value=\"%s\"/>", bool_val);
272
273         } else if (obj->type == JSON_STRING) {
274                 if (hint) {
275                         char * t = _escape_xml(jsonObjectGetString(obj));
276                         buffer_fadd(res_xml,"<string class_hint=\"%s\">%s</string>", hint, t);
277                         free(t);
278                 } else {
279                         char * t = _escape_xml(jsonObjectGetString(obj));
280                         buffer_fadd(res_xml,"<string>%s</string>", t);
281                         free(t);
282                 }
283
284         } else if(obj->type == JSON_NUMBER) {
285                 double x = jsonObjectGetNumber(obj);
286                 if (hint) {
287                         if (x == (int)x)
288                                 buffer_fadd(res_xml,"<number class_hint=\"%s\">%d</number>", hint, (int)x);
289                         else
290                                 buffer_fadd(res_xml,"<number class_hint=\"%s\">%lf</number>", hint, x);
291                 } else {
292                         if (x == (int)x)
293                                 buffer_fadd(res_xml,"<number>%d</number>", (int)x);
294                         else
295                                 buffer_fadd(res_xml,"<number>%lf</number>", x);
296                 }
297
298         } else if (obj->type == JSON_ARRAY) {
299
300                 if (hint) 
301                         buffer_fadd(res_xml,"<array class_hint=\"%s\">", hint);
302                 else
303                         buffer_add(res_xml,"<array>");
304
305         int i;
306         for ( i = 0; i!= obj->size; i++ )
307                     _recurse_jsonObjectToXML(jsonObjectGetIndex(obj,i), res_xml);
308
309                 buffer_add(res_xml,"</array>");
310
311         } else if (obj->type == JSON_HASH) {
312
313                 if (hint)
314                         buffer_fadd(res_xml,"<object class_hint=\"%s\">", hint);
315                 else
316                         buffer_add(res_xml,"<object>");
317
318                 jsonIterator* itr = jsonNewIterator(obj);
319                 const jsonObject* tmp;
320                 while( (tmp = jsonIteratorNext(itr)) ) {
321                         buffer_fadd(res_xml,"<element key=\"%s\">",itr->key);
322                         _recurse_jsonObjectToXML(tmp, res_xml);
323                         buffer_add(res_xml,"</element>");
324                 }
325                 jsonIteratorFree(itr);
326
327                 buffer_add(res_xml,"</object>");
328         }
329
330         if (hint)
331                 free(hint);
332
333         return 1;
334 }
335
336 static char* _escape_xml (const char* text) {
337         growing_buffer* b = buffer_init(256);
338         int len = strlen(text);
339         int i;
340         for (i = 0; i < len; i++) {
341                 if (text[i] == '&')
342                         buffer_add(b,"&amp;");
343                 else if (text[i] == '<')
344                         buffer_add(b,"&lt;");
345                 else if (text[i] == '>')
346                         buffer_add(b,"&gt;");
347                 else
348                         buffer_add_char(b,text[i]);
349         }
350         return buffer_release(b);
351 }
352
353 #endif