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