]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/libopensrf/osrf_json_xml.c
1. For keeping track of the child processes: use a doubly-linked
[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         appendChild(p, obj);
106         return;
107     }
108 }
109
110 static void endElementHandler( void *parser, const xmlChar *name) {
111     if(!strcmp((char*) name, "array") || !strcmp((char*) name, "object")) {
112         osrfXMLGatewayParser* p = (osrfXMLGatewayParser*) parser;
113         osrfListPop(p->objStack);
114     }
115 }
116
117 static void characterHandler(void *parser, const xmlChar *ch, int len) {
118
119     char data[len+1];
120     strncpy(data, (char*) ch, len);
121     data[len] = '\0';
122     osrfXMLGatewayParser* p = (osrfXMLGatewayParser*) parser;
123
124     if(p->inString) {
125         appendChild(p, jsonNewObject(data));
126         p->inString = 0;
127         return;
128     }
129
130     if(p->inNumber) {
131         appendChild(p, jsonNewNumberObject(atof(data)));
132         p->inNumber = 0;
133         return;
134     }
135 }
136
137 static void parseWarningHandler(void *parser, const char* msg, ...) {
138     VA_LIST_TO_STRING(msg);
139     fprintf(stderr, "Parser warning %s\n", VA_BUF);
140     fflush(stderr);
141 }
142
143 static void parseErrorHandler(void *parser, const char* msg, ...) {
144
145     VA_LIST_TO_STRING(msg);
146     fprintf(stderr, "Parser error %s\n", VA_BUF);
147     fflush(stderr);
148
149     osrfXMLGatewayParser* p = (osrfXMLGatewayParser*) parser;
150
151     /*  keyStack as strdup'ed strings.  The list may
152      *  not be empty, so tell it to free the items
153      *  when it's freed (from the main routine)
154      */
155     osrfListSetDefaultFree(p->keyStack);
156     jsonObjectFree(p->obj);
157
158     p->obj = NULL;
159     p->error = 1;
160 }
161
162
163
164
165 static xmlSAXHandler SAXHandlerStruct = {
166     NULL,                            /* internalSubset */
167     NULL,                            /* isStandalone */
168     NULL,                            /* hasInternalSubset */
169     NULL,                            /* hasExternalSubset */
170     NULL,                            /* resolveEntity */
171     NULL,                            /* getEntity */
172     NULL,                            /* entityDecl */
173     NULL,                            /* notationDecl */
174     NULL,                            /* attributeDecl */
175     NULL,                            /* elementDecl */
176     NULL,                            /* unparsedEntityDecl */
177     NULL,                            /* setDocumentLocator */
178     NULL,                            /* startDocument */
179     NULL,                            /* endDocument */
180     startElementHandler,        /* startElement */
181     endElementHandler,      /* endElement */
182     NULL,                            /* reference */
183     characterHandler,           /* characters */
184     NULL,                            /* ignorableWhitespace */
185     NULL,                            /* processingInstruction */
186     NULL,                            /* comment */
187     parseWarningHandler,     /* xmlParserWarning */
188     parseErrorHandler,       /* xmlParserError */
189     NULL,                            /* xmlParserFatalError : unused */
190     NULL,                            /* getParameterEntity */
191     NULL,                            /* cdataBlock; */
192     NULL,                            /* externalSubset; */
193     1,
194     NULL,
195     NULL,                            /* startElementNs */
196     NULL,                            /* endElementNs */
197     NULL                            /* xmlStructuredErrorFunc */
198 };
199
200 static const xmlSAXHandlerPtr SAXHandler = &SAXHandlerStruct;
201
202 jsonObject* jsonXMLToJSONObject(const char* xml) {
203
204     osrfXMLGatewayParser parser;
205
206     /* don't define freeItem, since objects will be cleaned by freeing the parent */
207     parser.objStack = osrfNewList(); 
208     /* don't define freeItem, since the list eill end up empty if there are no errors*/
209     parser.keyStack = osrfNewList(); 
210     parser.obj = NULL;
211     parser.inString = 0;
212     parser.inNumber = 0;
213
214     xmlParserCtxtPtr ctxt = xmlCreatePushParserCtxt(SAXHandler, &parser, "", 0, NULL);
215     xmlParseChunk(ctxt, xml, strlen(xml), 1);
216
217     osrfListFree(parser.objStack);
218     osrfListFree(parser.keyStack);
219     xmlFreeParserCtxt(ctxt);
220     xmlCleanupCharEncodingHandlers();
221     xmlDictCleanup();
222     xmlCleanupParser();
223
224     return parser.obj;
225 }
226
227
228
229
230
231
232 static char* _escape_xml (const char*);
233 static int _recurse_jsonObjectToXML(const jsonObject*, growing_buffer*);
234
235 char* jsonObjectToXML(const jsonObject* obj) {
236
237         if (!obj)
238                 return strdup("<null/>");
239         
240         growing_buffer * res_xml = buffer_init(1024);
241
242         _recurse_jsonObjectToXML( obj, res_xml );
243         return buffer_release(res_xml);
244
245 }
246
247 int _recurse_jsonObjectToXML(const jsonObject* obj, growing_buffer* res_xml) {
248
249         char * hint = NULL;
250         
251         if (obj->classname)
252                 hint = strdup(obj->classname);
253
254         if(obj->type == JSON_NULL) {
255
256                 if (hint)
257                         buffer_fadd(res_xml, "<null class_hint=\"%s\"/>",hint);
258                 else
259                         buffer_add(res_xml, "<null/>");
260
261         } else if(obj->type == JSON_BOOL) {
262
263                 const char* bool_val;
264                 if (obj->value.b)
265                         bool_val = "true";
266                 else
267                         bool_val = "false";
268
269                 if (hint)
270                         buffer_fadd(res_xml, "<boolean value=\"%s\" class_hint=\"%s\"/>", bool_val, hint);
271                 else
272                         buffer_fadd(res_xml, "<boolean value=\"%s\"/>", bool_val);
273
274         } else if (obj->type == JSON_STRING) {
275                 if (hint) {
276                         char * t = _escape_xml(jsonObjectGetString(obj));
277                         buffer_fadd(res_xml,"<string class_hint=\"%s\">%s</string>", hint, t);
278                         free(t);
279                 } else {
280                         char * t = _escape_xml(jsonObjectGetString(obj));
281                         buffer_fadd(res_xml,"<string>%s</string>", t);
282                         free(t);
283                 }
284
285         } else if(obj->type == JSON_NUMBER) {
286                 double x = jsonObjectGetNumber(obj);
287                 if (hint) {
288                         if (x == (int)x)
289                                 buffer_fadd(res_xml,"<number class_hint=\"%s\">%d</number>", hint, (int)x);
290                         else
291                                 buffer_fadd(res_xml,"<number class_hint=\"%s\">%lf</number>", hint, x);
292                 } else {
293                         if (x == (int)x)
294                                 buffer_fadd(res_xml,"<number>%d</number>", (int)x);
295                         else
296                                 buffer_fadd(res_xml,"<number>%lf</number>", x);
297                 }
298
299         } else if (obj->type == JSON_ARRAY) {
300
301                 if (hint) 
302                         buffer_fadd(res_xml,"<array class_hint=\"%s\">", hint);
303                 else
304                         buffer_add(res_xml,"<array>");
305
306         int i;
307         for ( i = 0; i!= obj->size; i++ )
308                     _recurse_jsonObjectToXML(jsonObjectGetIndex(obj,i), res_xml);
309
310                 buffer_add(res_xml,"</array>");
311
312         } else if (obj->type == JSON_HASH) {
313
314                 if (hint)
315                         buffer_fadd(res_xml,"<object class_hint=\"%s\">", hint);
316                 else
317                         buffer_add(res_xml,"<object>");
318
319                 jsonIterator* itr = jsonNewIterator(obj);
320                 const jsonObject* tmp;
321                 while( (tmp = jsonIteratorNext(itr)) ) {
322                         buffer_fadd(res_xml,"<element key=\"%s\">",itr->key);
323                         _recurse_jsonObjectToXML(tmp, res_xml);
324                         buffer_add(res_xml,"</element>");
325                 }
326                 jsonIteratorFree(itr);
327
328                 buffer_add(res_xml,"</object>");
329         }
330
331         if (hint)
332                 free(hint);
333
334         return 1;
335 }
336
337 static char* _escape_xml (const char* text) {
338         growing_buffer* b = buffer_init(256);
339         int len = strlen(text);
340         int i;
341         for (i = 0; i < len; i++) {
342                 if (text[i] == '&')
343                         buffer_add(b,"&amp;");
344                 else if (text[i] == '<')
345                         buffer_add(b,"&lt;");
346                 else if (text[i] == '>')
347                         buffer_add(b,"&gt;");
348                 else
349                         buffer_add_char(b,text[i]);
350         }
351         return buffer_release(b);
352 }
353
354 #endif