From 48997d4afdf18cf447b87f81ef7e549b8f79dd58 Mon Sep 17 00:00:00 2001 From: erickson Date: Thu, 14 Jun 2007 16:03:57 +0000 Subject: [PATCH 1/1] Added a new XML-2-JSON utility for converting XMLized OpenSRF objects to their internal JSON representations 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 | 34 ++++- src/objson/Makefile | 11 +- src/objson/xml2json.c | 229 ++++++++++++++++++++++++++++++++ src/objson/xml2json.h | 20 +++ 4 files changed, 286 insertions(+), 8 deletions(-) create mode 100644 src/objson/xml2json.c create mode 100644 src/objson/xml2json.h diff --git a/src/gateway/osrf_json_gateway.c b/src/gateway/osrf_json_gateway.c index a575bd9..80bf722 100644 --- a/src/gateway/osrf_json_gateway.c +++ b/src/gateway/osrf_json_gateway.c @@ -4,6 +4,7 @@ #include "opensrf/osrfConfig.h" #include "objson/object.h" #include "objson/json2xml.h" +#include "objson/xml2json.h" #include #include #include @@ -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 ) { diff --git a/src/objson/Makefile b/src/objson/Makefile index 5fcac70..5f26dae 100644 --- a/src/objson/Makefile +++ b/src/objson/Makefile @@ -26,12 +26,12 @@ # # -------------------------------------------------------------------- -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 index 0000000..efdcfb0 --- /dev/null +++ b/src/objson/xml2json.c @@ -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 index 0000000..35ed4ac --- /dev/null +++ b/src/objson/xml2json.h @@ -0,0 +1,20 @@ + +#include +#include +#include +#include +#include +#include +#include + +#include "object.h" +#include "json_parser.h" +#include "utils.h" +#include "osrf_list.h" + + + +jsonObject* jsonXMLToJSONObject(const char* xml); + + + -- 2.43.2