Added a new XML-2-JSON utility for converting XMLized OpenSRF objects to their intern...
authorerickson <erickson@9efc2488-bf62-4759-914b-345cdb29e865>
Thu, 14 Jun 2007 16:03:57 +0000 (16:03 +0000)
committererickson <erickson@9efc2488-bf62-4759-914b-345cdb29e865>
Thu, 14 Jun 2007 16:03:57 +0000 (16:03 +0000)
Added a new gateway param "input_format" to define the param format.
Options are "json" and "xml".
Unless overridden, input_format will match format

git-svn-id: svn://svn.open-ils.org/OpenSRF/trunk@948 9efc2488-bf62-4759-914b-345cdb29e865

src/gateway/osrf_json_gateway.c
src/objson/Makefile
src/objson/xml2json.c [new file with mode: 0644]
src/objson/xml2json.h [new file with mode: 0644]

index a575bd9..80bf722 100644 (file)
@@ -4,6 +4,7 @@
 #include "opensrf/osrfConfig.h"
 #include "objson/object.h"
 #include "objson/json2xml.h"
+#include "objson/xml2json.h"
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <unistd.h>
@@ -99,6 +100,7 @@ static int osrf_json_gateway_method_handler (request_rec *r) {
        char* method            = NULL; /* method to perform */
        char* format            = NULL; /* method to perform */
        char* a_l                       = NULL; /* request api level */
+    char* input_format  = NULL; /* POST data format, defaults to 'format' */
        int   isXML                     = 0;
        int   api_level = 1;
 
@@ -111,9 +113,15 @@ static int osrf_json_gateway_method_handler (request_rec *r) {
        service         = apacheGetFirstParamValue( params, "service" );
        method          = apacheGetFirstParamValue( params, "method" ); 
        format          = apacheGetFirstParamValue( params, "format" ); 
+       input_format = apacheGetFirstParamValue( params, "input_format" ); 
        a_l                     = apacheGetFirstParamValue( params, "api_level" ); 
        mparams         = apacheGetParamValues( params, "param" ); /* free me */
 
+    if(format == NULL)
+        format = "json";
+    if(input_format == NULL)
+        input_format = format;
+
    /* set the user defined timeout value */
    int timeout = 60;
    char* tout = apacheGetFirstParamValue( params, "timeout" ); /* request timeout in seconds */
@@ -126,7 +134,7 @@ static int osrf_json_gateway_method_handler (request_rec *r) {
        if (a_l)
                api_level = atoi(a_l);
 
-       if (format && !strcasecmp(format, "xml" )) {
+       if (!strcasecmp(format, "xml")) {
                isXML = 1;
                ap_set_content_type(r, "application/xml");
        } else {
@@ -158,7 +166,29 @@ static int osrf_json_gateway_method_handler (request_rec *r) {
                osrfAppSession* session = osrf_app_client_session_init(service);
 
                double starttime = get_timestamp_millis();
-               int req_id = osrf_app_session_make_req( session, NULL, method, api_level, mparams );
+               int req_id = -1;
+
+        if(!strcasecmp(input_format, "json")) {
+                   req_id = osrf_app_session_make_req( session, NULL, method, api_level, mparams );
+
+        } else {
+
+            /**
+             * If we receive XML method params, convert each param to a JSON object
+             * and pass the array of JSON object params to the method */
+            if(!strcasecmp(input_format, "xml")) {
+                jsonObject* jsonParams = jsonNewObject(NULL);
+
+                char* str;
+                int i = 0;
+                while( (str = osrfStringArrayGetString(mparams, i++)) ) {
+                    jsonObjectPush(jsonParams, jsonXMLToJSONObject(str));
+                }
+
+                       req_id = osrf_app_session_make_req( session, jsonParams, method, api_level, NULL );
+                jsonObjectFree(jsonParams);
+            }
+        }
 
 
                if( req_id == -1 ) {
index 5fcac70..5f26dae 100644 (file)
 #
 # --------------------------------------------------------------------
 
-OBJS                           = md5.o utils.o json2xml.o object.o json_parser.o
+OBJS                           = md5.o utils.o json2xml.o object.o json_parser.o xml2json.o
 UTIL_DIR                       = ../utils
 DEST_INCLUDE   = objson
 CFLAGS                 += -DSTRICT_JSON_WRITE #-DSTRICT_JSON_READ
 
-JSON_HEADERS = object.h json_parser.h json2xml.h
+JSON_HEADERS = object.h json_parser.h json2xml.h xml2json.h
 
 all:   test
 
@@ -40,6 +40,9 @@ test: libobjson.so objson_test.o
 objson_test.o: objson_test.c 
 object.o:      object.h object.c
 json_parser.o: json_parser.h json_parser.c
+json2xml.o:    json2xml.h json2xml.c
+xml2json.o:    xml2json.h xml2json.c
+
 
 install:
        mkdir -p $(INCLUDEDIR)/$(DEST_INCLUDE)
@@ -62,10 +65,6 @@ md5.o:       $(UTIL_DIR)/md5.h $(UTIL_DIR)/md5.c
        cp $(UTIL_DIR)/md5.c .
        $(CC) -c $(CFLAGS) md5.c -o $@
 
-json2xml.o:    json2xml.h json2xml.c
-       $(CC) -c $(CFLAGS) json2xml.c -o $@
-
-
 clean:
        /bin/rm -f *.o *.so utils.c utils.h libobjson.so
 
diff --git a/src/objson/xml2json.c b/src/objson/xml2json.c
new file mode 100644 (file)
index 0000000..efdcfb0
--- /dev/null
@@ -0,0 +1,229 @@
+#include "xml2json.h"
+
+struct osrfXMLGatewayParserStruct {
+    osrfList* objStack;
+    osrfList* keyStack;
+    jsonObject* obj;
+    short inString;
+    short inNumber;
+    short error;
+};
+typedef struct osrfXMLGatewayParserStruct osrfXMLGatewayParser;
+
+/** returns the attribute value with the given attribute name */
+static char* getXMLAttr(const xmlChar** atts, char* attr_name) {
+    int i;
+    if (atts != NULL) {
+        for(i = 0; (atts[i] != NULL); i++) {
+            if(strcmp((char*) atts[i++], attr_name) == 0) {
+                if(atts[i] != NULL) 
+                    return (char*) atts[i];
+            }
+        }
+    }
+    return NULL;
+}
+
+
+static void appendChild(osrfXMLGatewayParser* p, jsonObject* obj) {
+
+    if(p->obj == NULL) 
+        p->obj = obj;
+
+    if(p->objStack->size == 0)
+        return;
+    
+    jsonObject* parent = OSRF_LIST_GET_INDEX(p->objStack, p->objStack->size - 1);
+
+    if(parent->type == JSON_ARRAY) {
+        jsonObjectPush(parent, obj);
+    } else {
+        char* key = osrfListPop(p->keyStack);
+        jsonObjectSetKey(parent, key, obj);
+        free(key); /* the list is not setup for auto-freeing */
+    }
+}
+
+
+
+static void startElementHandler(
+    void *parser, const xmlChar *name, const xmlChar **atts) {
+
+    osrfXMLGatewayParser* p = (osrfXMLGatewayParser*) parser;
+    jsonObject* obj;
+
+    char* hint = getXMLAttr(atts, "class_hint");
+
+    if(!strcmp((char*) name, "null")) {
+        appendChild(p, jsonNewObject(NULL));
+        return;
+    }
+
+    if(!strcmp((char*) name, "string")) {
+        p->inString = 1;
+        return;
+    }
+
+    if(!strcmp((char*) name, "element")) {
+       osrfListPush(p->keyStack, strdup(getXMLAttr(atts, "key")));
+       return;
+    }
+
+    if(!strcmp((char*) name, "object")) {
+        obj = jsonNewObject(NULL);
+        jsonObjectSetClass(obj, hint); /* OK if hint is NULL */
+        obj->type = JSON_HASH;
+        appendChild(p, obj);
+        osrfListPush(p->objStack, obj);
+        return;
+    }
+
+    if(!strcmp((char*) name, "array")) {
+        obj = jsonNewObject(NULL);
+        jsonObjectSetClass(obj, hint); /* OK if hint is NULL */
+        obj->type = JSON_ARRAY;
+        appendChild(p, obj);
+        osrfListPush(p->objStack, obj);
+        return;
+    }
+
+
+    if(!strcmp((char*) name, "number")) {
+        p->inNumber = 1;
+        return;
+    }
+
+    if(!strcmp((char*) name, "boolean")) {
+        obj = jsonNewObject(NULL);
+        obj->type = JSON_BOOL;
+        char* val = getXMLAttr(atts, "value");
+        if(val && !strcmp(val, "true"))
+            obj->value.b = 1;
+        
+        return;
+    }
+}
+
+static void endElementHandler( void *parser, const xmlChar *name) {
+    if(!strcmp((char*) name, "array") || !strcmp((char*) name, "object")) {
+        osrfXMLGatewayParser* p = (osrfXMLGatewayParser*) parser;
+        osrfListPop(p->objStack);
+    }
+}
+
+static void characterHandler(void *parser, const xmlChar *ch, int len) {
+
+    char data[len+1];
+    strncpy(data, (char*) ch, len);
+    data[len] = '\0';
+    osrfXMLGatewayParser* p = (osrfXMLGatewayParser*) parser;
+
+    if(p->inString) {
+        appendChild(p, jsonNewObject(data));
+        p->inString = 0;
+        return;
+    }
+
+    if(p->inNumber) {
+        appendChild(p, jsonNewNumberObject(atof(data)));
+        p->inNumber = 0;
+        return;
+    }
+}
+
+static void parseWarningHandler(void *parser, const char* msg, ...) {
+    VA_LIST_TO_STRING(msg);
+    fprintf(stderr, "Parser warning %s\n", VA_BUF);
+    fflush(stderr);
+}
+
+static void parseErrorHandler(void *parser, const char* msg, ...) {
+
+    VA_LIST_TO_STRING(msg);
+    fprintf(stderr, "Parser error %s\n", VA_BUF);
+    fflush(stderr);
+
+    osrfXMLGatewayParser* p = (osrfXMLGatewayParser*) parser;
+
+    /*  keyStack as strdup'ed strings.  The list may
+     *  not be empty, so tell it to free the items
+     *  when it's freed (from the main routine)
+     */
+    osrfListSetDefaultFree(p->keyStack);
+    jsonObjectFree(p->obj);
+
+    p->obj = NULL;
+    p->error = 1;
+}
+
+
+
+
+static xmlSAXHandler SAXHandlerStruct = {
+    NULL,                            /* internalSubset */
+    NULL,                            /* isStandalone */
+    NULL,                            /* hasInternalSubset */
+    NULL,                            /* hasExternalSubset */
+    NULL,                            /* resolveEntity */
+    NULL,                            /* getEntity */
+    NULL,                            /* entityDecl */
+    NULL,                            /* notationDecl */
+    NULL,                            /* attributeDecl */
+    NULL,                            /* elementDecl */
+    NULL,                            /* unparsedEntityDecl */
+    NULL,                            /* setDocumentLocator */
+    NULL,                            /* startDocument */
+    NULL,                            /* endDocument */
+    startElementHandler,        /* startElement */
+    endElementHandler,      /* endElement */
+    NULL,                            /* reference */
+    characterHandler,           /* characters */
+    NULL,                            /* ignorableWhitespace */
+    NULL,                            /* processingInstruction */
+    NULL,                            /* comment */
+    parseWarningHandler,     /* xmlParserWarning */
+    parseErrorHandler,       /* xmlParserError */
+    NULL,                            /* xmlParserFatalError : unused */
+    NULL,                            /* getParameterEntity */
+    NULL,                            /* cdataBlock; */
+    NULL,                            /* externalSubset; */
+    1,
+    NULL,
+    NULL,                            /* startElementNs */
+    NULL,                            /* endElementNs */
+    NULL                            /* xmlStructuredErrorFunc */
+};
+
+static const xmlSAXHandlerPtr SAXHandler = &SAXHandlerStruct;
+
+jsonObject* jsonXMLToJSONObject(const char* xml) {
+
+    osrfXMLGatewayParser parser;
+
+    /* don't define freeItem, since objects will be cleaned by freeing the parent */
+    parser.objStack = osrfNewList(); 
+    /* don't define freeItem, since the list eill end up empty if there are no errors*/
+    parser.keyStack = osrfNewList(); 
+    parser.obj = NULL;
+    parser.inString = 0;
+    parser.inNumber = 0;
+
+    xmlParserCtxtPtr ctxt = xmlCreatePushParserCtxt(SAXHandler, &parser, "", 0, NULL);
+    xmlParseChunk(ctxt, xml, strlen(xml), 1);
+
+    osrfListFree(parser.objStack);
+    osrfListFree(parser.keyStack);
+    xmlFreeParserCtxt(ctxt);
+    xmlCleanupCharEncodingHandlers();
+    xmlDictCleanup();
+    xmlCleanupParser();
+
+    return parser.obj;
+}
+
+
+
+
+
+
+
diff --git a/src/objson/xml2json.h b/src/objson/xml2json.h
new file mode 100644 (file)
index 0000000..35ed4ac
--- /dev/null
@@ -0,0 +1,20 @@
+
+#include <stdio.h>
+#include <string.h>
+#include <libxml/globals.h>
+#include <libxml/xmlerror.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xmlmemory.h>
+
+#include "object.h"
+#include "json_parser.h"
+#include "utils.h"
+#include "osrf_list.h"
+
+
+
+jsonObject* jsonXMLToJSONObject(const char* xml);
+
+
+