]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/libopensrf/osrf_json_xml.c
Merging changes from branches/new-json2
[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, 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 (char*);
232 static int _recurse_jsonObjectToXML(jsonObject*, growing_buffer*);
233
234 char* jsonObjectToXML(jsonObject* obj) {
235
236         growing_buffer * res_xml;
237         char * output;
238
239         res_xml = buffer_init(1024);
240
241         if (!obj)
242                 return strdup("<null/>");
243         
244         _recurse_jsonObjectToXML( obj, res_xml );
245         output = buffer_data(res_xml);
246         
247         buffer_free(res_xml);
248
249         return output;
250
251 }
252
253 int _recurse_jsonObjectToXML(jsonObject* obj, growing_buffer* res_xml) {
254
255         char * hint = NULL;
256         char * bool_val = NULL;
257         int i = 0;
258         
259         if (obj->classname)
260                 hint = strdup(obj->classname);
261
262         if(obj->type == JSON_NULL) {
263
264                 if (hint)
265                         buffer_fadd(res_xml, "<null class_hint=\"%s\"/>",hint);
266                 else
267                         buffer_add(res_xml, "<null/>");
268
269         } else if(obj->type == JSON_BOOL) {
270
271                 if (obj->value.b)
272                         bool_val = strdup("true");
273                 else
274                         bool_val = strdup("false");
275
276                 if (hint)
277                         buffer_fadd(res_xml, "<boolean value=\"%s\" class_hint=\"%s\"/>", bool_val, hint);
278                 else
279                         buffer_fadd(res_xml, "<boolean value=\"%s\"/>", bool_val);
280
281                 free(bool_val);
282                 
283         } else if (obj->type == JSON_STRING) {
284                 if (hint) {
285                         char * t = _escape_xml(jsonObjectGetString(obj));
286                         buffer_fadd(res_xml,"<string class_hint=\"%s\">%s</string>", hint, t);
287                         free(t);
288                 } else {
289                         char * t = _escape_xml(jsonObjectGetString(obj));
290                         buffer_fadd(res_xml,"<string>%s</string>", t);
291                         free(t);
292                 }
293
294         } else if(obj->type == JSON_NUMBER) {
295                 double x = jsonObjectGetNumber(obj);
296                 if (hint) {
297                         if (x == (int)x)
298                                 buffer_fadd(res_xml,"<number class_hint=\"%s\">%d</number>", hint, (int)x);
299                         else
300                                 buffer_fadd(res_xml,"<number class_hint=\"%s\">%lf</number>", hint, x);
301                 } else {
302                         if (x == (int)x)
303                                 buffer_fadd(res_xml,"<number>%d</number>", (int)x);
304                         else
305                                 buffer_fadd(res_xml,"<number>%lf</number>", x);
306                 }
307
308         } else if (obj->type == JSON_ARRAY) {
309
310                 if (hint) 
311                         buffer_fadd(res_xml,"<array class_hint=\"%s\">", hint);
312                 else
313                         buffer_add(res_xml,"<array>");
314
315                 for ( i = 0; i!= obj->size; i++ )
316                         _recurse_jsonObjectToXML(jsonObjectGetIndex(obj,i), res_xml);
317
318                 buffer_add(res_xml,"</array>");
319
320         } else if (obj->type == JSON_HASH) {
321
322                 if (hint)
323                         buffer_fadd(res_xml,"<object class_hint=\"%s\">", hint);
324                 else
325                         buffer_add(res_xml,"<object>");
326
327                 jsonIterator* itr = jsonNewIterator(obj);
328                 jsonObject* tmp;
329                 while( (tmp = jsonIteratorNext(itr)) ) {
330                         buffer_fadd(res_xml,"<element key=\"%s\">",itr->key);
331                         _recurse_jsonObjectToXML(tmp, res_xml);
332                         buffer_add(res_xml,"</element>");
333                 }
334                 jsonIteratorFree(itr);
335
336                 buffer_add(res_xml,"</object>");
337         }
338
339         if (hint)
340                 free(hint);
341
342         return 1;
343 }
344
345 char* _escape_xml (char* text) {
346         char* out;
347         growing_buffer* b = buffer_init(256);
348         int len = strlen(text);
349         int i;
350         for (i = 0; i < len; i++) {
351                 if (text[i] == '&')
352                         buffer_add(b,"&amp;");
353                 else if (text[i] == '<')
354                         buffer_add(b,"&lt;");
355                 else if (text[i] == '>')
356                         buffer_add(b,"&gt;");
357                 else
358                         buffer_add_char(b,text[i]);
359         }
360         out = buffer_data(b);
361         buffer_free(b);
362         return out;
363 }
364
365 #endif