From 2c4a565ff265f4b05345a2e13338f4faab84f256 Mon Sep 17 00:00:00 2001 From: erickson Date: Fri, 20 Jul 2007 12:58:59 +0000 Subject: [PATCH] Merging changes from branches/new-json2 svn merge -r1006:1012 svn://svn.open-ils.org/OpenSRF/branches/new-json2 svn merge -r1018:1019 svn://svn.open-ils.org/OpenSRF/branches/new-json2 svn merge -r1022:1025 svn://svn.open-ils.org/OpenSRF/branches/new-json2 svn merge -r1026:1028 svn://svn.open-ils.org/OpenSRF/branches/new-json2 svn merge -r1036:1037 svn://svn.open-ils.org/OpenSRF/branches/new-json2 svn merge -r1039:1040 svn://svn.open-ils.org/OpenSRF/branches/new-json2 svn merge -r1041:1043 svn://svn.open-ils.org/OpenSRF/branches/new-json2 svn merge -r1045:1048 svn://svn.open-ils.org/OpenSRF/branches/new-json2 This moves OpenSRF to the new JSON wire protocol, including: a new C parser a new osrfList based string_array module a compatibility layer for mimicking libobjson and legacy JSON I/O in the HTTP gateway a small JSON test program for profiling, etc. git-svn-id: svn://svn.open-ils.org/OpenSRF/trunk@1051 9efc2488-bf62-4759-914b-345cdb29e865 --- include/objson/json2xml.h | 19 +- include/objson/json_parser.h | 87 +--- include/objson/object.h | 288 +----------- include/objson/xml2json.h | 26 +- include/opensrf/osrfConfig.h | 2 +- include/opensrf/osrf_app_session.h | 3 +- include/opensrf/osrf_application.h | 2 +- include/opensrf/osrf_cache.h | 3 +- include/opensrf/osrf_json.h | 381 ++++++++++++++++ include/opensrf/osrf_json_utils.h | 131 ++++++ include/opensrf/osrf_json_xml.h | 26 ++ include/opensrf/osrf_legacy_json.h | 135 ++++++ include/opensrf/osrf_message.h | 3 +- include/opensrf/osrf_settings.h | 3 +- include/opensrf/string_array.h | 13 +- include/opensrf/utils.h | 4 + include/opensrf/xml_utils.h | 2 +- install.conf | 1 + src/Makefile | 45 +- src/c-apps/Makefile | 2 +- src/c-apps/osrf_dbmath.c | 8 +- src/c-apps/osrf_math.c | 8 +- src/c-apps/osrf_version.c | 2 +- src/gateway/Makefile | 2 +- src/gateway/osrf_json_gateway.c | 63 ++- src/jserver/Makefile | 2 +- src/libopensrf/Makefile | 68 ++- src/libopensrf/Makefile.json | 40 ++ src/libopensrf/osrf_application.c | 4 - src/libopensrf/osrf_hash.c | 8 +- src/libopensrf/osrf_json_object.c | 400 +++++++++++++++++ src/libopensrf/osrf_json_parser.c | 632 ++++++++++++++++++++++++++ src/libopensrf/osrf_json_test.c | 56 +++ src/libopensrf/osrf_json_tools.c | 297 +++++++++++++ src/libopensrf/osrf_json_xml.c | 365 +++++++++++++++ src/libopensrf/osrf_legacy_json.c | 880 +++++++++++++++++++++++++++++++++++++ src/libopensrf/string_array.c | 91 ++-- src/perlmods/OpenSRF/Utils/JSON.pm | 130 ++++-- src/router/Makefile | 2 +- src/srfsh/Makefile | 2 +- src/srfsh/srfsh.c | 4 +- 41 files changed, 3681 insertions(+), 559 deletions(-) create mode 100644 include/opensrf/osrf_json.h create mode 100644 include/opensrf/osrf_json_utils.h create mode 100644 include/opensrf/osrf_json_xml.h create mode 100644 include/opensrf/osrf_legacy_json.h create mode 100644 src/libopensrf/Makefile.json create mode 100644 src/libopensrf/osrf_json_object.c create mode 100644 src/libopensrf/osrf_json_parser.c create mode 100644 src/libopensrf/osrf_json_test.c create mode 100644 src/libopensrf/osrf_json_tools.c create mode 100644 src/libopensrf/osrf_json_xml.c create mode 100644 src/libopensrf/osrf_legacy_json.c diff --git a/include/objson/json2xml.h b/include/objson/json2xml.h index 9eaa1c0..6539adf 100644 --- a/include/objson/json2xml.h +++ b/include/objson/json2xml.h @@ -1,11 +1,10 @@ - -#include -#include - -/* the JSON parser, so we can read the response we're XMLizing */ -#include -#include -#include - -char* jsonObjectToXML(jsonObject*); +/* + * Header to support legacy objson library + */ +#ifndef OBJSON_JSON2XML_H +#define OBJSON_JSON2XML_H +#include +#include +#include +#endif diff --git a/include/objson/json_parser.h b/include/objson/json_parser.h index a1f785b..2c982ee 100644 --- a/include/objson/json_parser.h +++ b/include/objson/json_parser.h @@ -1,84 +1,9 @@ /* -Copyright (C) 2005 Georgia Public Library Service -Bill Erickson - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. -*/ - - - - -/* --------------------------------------------------------------------------------------- - JSON parser. - * --------------------------------------------------------------------------------------- */ -#ifndef JSON_PARSER_H -#define JSON_PARSER_H - -#include -#include -#include -#include - - - -/* Parses the given JSON string and returns the built object. - * returns NULL (and prints parser error to stderr) on error. + * Header to support legacy objson library */ - -jsonObject* json_parse_string(char* string); - -jsonObject* jsonParseString(char* string); -jsonObject* jsonParseStringFmt( char* string, ... ); - -jsonObject* json_parse_file( const char* filename ); - -jsonObject* jsonParseFile( const char* string ); - - - -/* does the actual parsing work. returns 0 on success. -1 on error and - * -2 if there was no object to build (string was all comments) - */ -int _json_parse_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen); - -/* returns 0 on success and turns obj into a string object */ -int json_parse_json_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen); - -/* returns 0 on success and turns obj into a number or double object */ -int json_parse_json_number(char* string, unsigned long* index, jsonObject* obj, int current_strlen); - -/* returns 0 on success and turns obj into an 'object' object */ -int json_parse_json_object(char* string, unsigned long* index, jsonObject* obj, int current_strlen); - -/* returns 0 on success and turns object into an array object */ -int json_parse_json_array(char* string, unsigned long* index, jsonObject* obj, int current_strlen); - -/* churns through whitespace and increments index as it goes. - * eat_all == true means we should eat newlines, tabs - */ -void json_eat_ws(char* string, unsigned long* index, int eat_all, int current_strlen); - -int json_parse_json_bool(char* string, unsigned long* index, jsonObject* obj, int current_strlen); - -/* removes comments from a json string. if the comment contains a class hint - * and class_hint isn't NULL, an allocated char* with the class name will be - * shoved into *class_hint. returns 0 on success, -1 on parse error. - * 'index' is assumed to be at the second character (*) of the comment - */ -int json_eat_comment(char* string, unsigned long* index, char** class_hint, int parse_class, int current_strlen); - -/* prints a useful error message to stderr. always returns -1 */ -int json_handle_error(char* string, unsigned long* index, char* err_msg); - -int json_parse_json_null(char* string, unsigned long* index, jsonObject* obj, int current_strlen); - - +#ifndef OBJSON_XML2JSON_H +#define OBJSON_XML2JSON_H +#include +#include +#include #endif diff --git a/include/objson/object.h b/include/objson/object.h index 8d62c1a..e54f8ed 100644 --- a/include/objson/object.h +++ b/include/objson/object.h @@ -1,286 +1,8 @@ /* -Copyright (C) 2005 Georgia Public Library Service -Bill Erickson - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. -*/ - - -/* --------------------------------------------------------------------------------------- - libjson - * --------------------------------------------------------------------------------------- */ - -#ifndef _JSON_OBJECT_H -#define _JSON_OBJECT_H - -#include -#include -#include -#include - -#include - -/* json object types */ -#define JSON_HASH 0 -#define JSON_ARRAY 1 -#define JSON_STRING 2 -#define JSON_NUMBER 3 -#define JSON_NULL 4 -#define JSON_BOOL 5 - - -/* top level generic object structure */ -struct _jsonObjectStruct { - - /* how many sub-objects do we contain if we're an array or an object. - Note that this includes null array elements in sparse arrays */ - unsigned long size; - - /* optional class hint */ - char* classname; - - /* see JSON types above */ - int type; - - - /* our cargo */ - union _jsonObjectValue { - struct _jsonObjectNodeStruct* c; /* our list of sub-objects if we're an array or a hash */ - char* s; /* string */ - int b; /* bool */ - double n; /* number */ - } value; - - - /* client may provide a comment string which will be - * added to the object when stringified */ - char* comment; - -}; -typedef struct _jsonObjectStruct jsonObject; - - -/** - String parsing function. This is assigned by the json_parser code. - to avoid circular dependency, declare the parse function here, - and have the json parse code set the variable to a real function -*/ -//jsonObject* (*jsonParseString) (char* str); - - -/* this contains a single element of the object along with the elements - * index (if this object is an array) and key (if this object is a hash) + * Header to support legacy objson library */ -struct _jsonObjectNodeStruct { - - unsigned long index; /* our array position */ - char* key; /* our hash key */ - - jsonObject* item; /* our object */ - struct _jsonObjectNodeStruct* next; /* pointer to the next object node */ -}; -typedef struct _jsonObjectNodeStruct jsonObjectNode; - - - -/* utility object for iterating over hash objects */ -struct _jsonObjectIteratorStruct { - const jsonObject* obj; /* the topic object */ - jsonObjectNode* current; /* the current node within the object */ -}; -typedef struct _jsonObjectIteratorStruct jsonObjectIterator; - - -/** Allocates a new iterator - @param obj The object over which to iterate. -*/ -jsonObjectIterator* jsonNewObjectIterator(const jsonObject* obj); - -/** - De-allocates an iterator - @param iter The iterator object to free -*/ -void jsonObjectIteratorFree(jsonObjectIterator* iter); - -/** - Returns the object_node currently pointed to by the iterator - and increments the pointer to the next node - @param iter The iterator in question. - */ -jsonObjectNode* jsonObjectIteratorNext(jsonObjectIterator* iter); - -/** - @param iter The iterator. - @return True if there is another node after the current node. - */ -int jsonObjectIteratorHasNext(const jsonObjectIterator* iter); - - -/** - Allocates a new object. - @param string The string data if this object is to be a string. - if not, string should be NULL - @return The newly allocated object or NULL on memory error. -*/ -jsonObject* jsonNewObjectFmt(const char* string, ...); -jsonObject* jsonNewObject(const char* string); - -/** - Allocates a new JSON number object. - @param num The number this object is to hold - @return The newly allocated object. -*/ -jsonObject* jsonNewNumberObject( double num ); - - -/** - Returns a pointer to the object at the given index. This call is - only valid if the object has a type of JSON_ARRAY. - @param obj The object - @param index The position within the object - @return The object at the given index. -*/ -jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index ); - - -/** - Returns a pointer to the object with the given key - @param obj The object - @param key The key - @return The object with the given key. -*/ -jsonObject* jsonObjectGetKey( const jsonObject* obj, const char* key ); - -/** - De-allocates an object. Note that this function should only be called - on objects that are _not_ children of other objects or there will be - double-free's - @param obj The object to free. -*/ -void jsonObjectFree(jsonObject* obj); - - -/** - Allocates a new object node. - @param obj The object to which the node will be appended. - @return The new object node. -*/ -jsonObjectNode* jsonNewObjectNode(jsonObject* obj); - -/** - De-allocates an object node - @param obj The object node to de-allocate. -*/ -void jsonObjectNodeFree(jsonObjectNode* obj); - - -/** - Pushes the given object onto the end of the list. This coerces an object - into becoming an array. _Only_ use this function on objects that you - want to become an array. - If obj is NULL, inserts a new NULL object into the list. - @return array size on success, -1 on error - */ -unsigned long jsonObjectPush(jsonObject* dest, jsonObject* newObj); - -/* removes (and deallocates) the object at the given index (if one exists) and inserts - * the new one. returns the size on success, -1 on error - * If obj is NULL, inserts a new object into the list with is_null set to true - */ -unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj); - -/* inserts the new object, overwriting (removing, deallocating) any - * previous object with the given key. - * returns the size on success, -1 on error - * if 'obj' is NULL, a new object is inserted at key 'key' with 'is_null' - * set to true - */ -unsigned long jsonObjectSetKey(jsonObject* dest, const char* key, jsonObject* newObj); - -/* removes the object at the given index and, if more items exist, - * re-indexes (shifts down by 1) the rest of the objects in the array - */ -unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index); - -/* removes (and deallocates) the object with key 'key' if it exists */ -unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key); - -/* returns a pointer to the string data held by this object if this object - is a string. Otherwise returns NULL*/ -char* jsonObjectGetString(const jsonObject*); - -double jsonObjectGetNumber( const jsonObject* obj ); - -/* sets the string data */ -void jsonObjectSetString(jsonObject* dest, const char* string); - -/* sets the number value for the object */ -void jsonObjectSetNumber(jsonObject* dest, double num); - -/* sets the class hint for this object */ -void jsonObjectSetClass(jsonObject* dest, const char* classname ); - -/* converts an object to a json string. client is responsible for freeing the return string */ -char* jsonObjectToJSON( const jsonObject* obj ); - -/* set this object's comment string */ -void jsonObjectSetComment(jsonObject* dest, const char* classname); - -/* utility method. starting at index 'index', shifts all indices down by one and - * decrements the objects size by 1 - */ -void _jsonObjectShiftIndex(jsonObject* dest, unsigned long index); - -/* formats a JSON string from printing. User must free returned string */ -char* jsonFormatString( const char* jsonString ); - -jsonObject* jsonObjectClone( const jsonObject* o ); - -/* tries to extract the string data from an object. - if object -> NULL (the C NULL) - if array -> NULL (the C NULL) - if null -> NULL (the C NULL) - if true/false -> true/false - if string/number/double the string version of either of those - The caller is responsible for freeing the returned string - */ -char* jsonObjectToSimpleString( const jsonObject* o ); - -int jsonBoolIsTrue( const jsonObject* o ); - - -/* ------------------------------------------------------------------------ */ -/* XPATH */ - -/* provides an XPATH style search interface (e.g. /some/node/here) and - return the object at that location if one exists. Naturally, - every element in the path must be a proper object ("hash" / {}). - Returns NULL if the specified node is not found - Note also that the object returned is a clone and - must be freed by the caller -*/ -jsonObject* jsonObjectFindPath( const jsonObject* obj, char* path, ... ); - - -/* Utility method. finds any object in the tree that matches the path. - Use this for finding paths that start with '//' */ -jsonObject* _jsonObjectFindPathRecurse( const jsonObject* o, char* root, char* path ); - -/* returns a list of object whose key is 'root'. These are used as - potential objects when doing a // search */ -jsonObject* __jsonObjectFindPathRecurse( const jsonObject* o, char* root ); - -/* ------------------------------------------------------------------------ */ - - +#ifndef OBJSON_OBJECT_H +#define OBJSON_OBJECT_H +#include +#include #endif - - diff --git a/include/objson/xml2json.h b/include/objson/xml2json.h index a9ded67..037fc75 100644 --- a/include/objson/xml2json.h +++ b/include/objson/xml2json.h @@ -1,19 +1,11 @@ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - - -jsonObject* jsonXMLToJSONObject(const char* xml); - +/* + * Header to support legacy objson library + */ +#ifndef OBJSON_XML2JSON_H +#define OBJSON_XML2JSON_H +#include +#include +#include +#endif diff --git a/include/opensrf/osrfConfig.h b/include/opensrf/osrfConfig.h index 75dbcfd..dbe59ed 100644 --- a/include/opensrf/osrfConfig.h +++ b/include/opensrf/osrfConfig.h @@ -19,7 +19,7 @@ GNU General Public License for more details. #include #include #include -#include +#include typedef struct { jsonObject* config; diff --git a/include/opensrf/osrf_app_session.h b/include/opensrf/osrf_app_session.h index aae373c..3cdbcb6 100644 --- a/include/opensrf/osrf_app_session.h +++ b/include/opensrf/osrf_app_session.h @@ -9,8 +9,7 @@ #include #include -#include -#include +#include diff --git a/include/opensrf/osrf_application.h b/include/opensrf/osrf_application.h index ac548a2..20bb5e0 100644 --- a/include/opensrf/osrf_application.h +++ b/include/opensrf/osrf_application.h @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include diff --git a/include/opensrf/osrf_cache.h b/include/opensrf/osrf_cache.h index 5a755ff..8960801 100644 --- a/include/opensrf/osrf_cache.h +++ b/include/opensrf/osrf_cache.h @@ -14,8 +14,7 @@ GNU General Public License for more details. */ -#include -#include +#include #include #include diff --git a/include/opensrf/osrf_json.h b/include/opensrf/osrf_json.h new file mode 100644 index 0000000..6c6e489 --- /dev/null +++ b/include/opensrf/osrf_json.h @@ -0,0 +1,381 @@ +/* +Copyright (C) 2006 Georgia Public Library Service +Bill Erickson + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + + +#include +#include +#include + +#ifndef _JSON_H +#define _JSON_H + + +/* parser states */ +#define JSON_STATE_IN_OBJECT 0x1 +#define JSON_STATE_IN_ARRAY 0x2 +#define JSON_STATE_IN_STRING 0x4 +#define JSON_STATE_IN_UTF 0x8 +#define JSON_STATE_IN_ESCAPE 0x10 +#define JSON_STATE_IN_KEY 0x20 +#define JSON_STATE_IN_NULL 0x40 +#define JSON_STATE_IN_TRUE 0x80 +#define JSON_STATE_IN_FALSE 0x100 +#define JSON_STATE_IN_NUMBER 0x200 +#define JSON_STATE_IS_INVALID 0x400 +#define JSON_STATE_IS_DONE 0x800 +#define JSON_STATE_START_COMMEN 0x1000 +#define JSON_STATE_IN_COMMENT 0x2000 +#define JSON_STATE_END_COMMENT 0x4000 + + +/* object and array (container) states are pushed onto a stack so we + * can keep track of the object nest. All other states are + * simply stored in the state field of the parser */ +#define JSON_STATE_SET(ctx,s) ctx->state |= s; /* set a state */ +#define JSON_STATE_REMOVE(ctx,s) ctx->state &= ~s; /* unset a state */ +#define JSON_STATE_CHECK(ctx,s) (ctx->state & s) ? 1 : 0 /* check if a state is set */ +#define JSON_STATE_POP(ctx) osrfListPop( ctx->stateStack ); /* remove a state from the stack */ +#define JSON_STATE_PUSH(ctx, state) osrfListPush( ctx->stateStack,(void*) state );/* push a state on the stack */ +#define JSON_STATE_PEEK(ctx) osrfListGetIndex(ctx->stateStack, ctx->stateStack->size -1) /* check which container type we're currently in */ +#define JSON_STATE_CHECK_STACK(ctx, s) (JSON_STATE_PEEK(ctx) == (void*) s ) ? 1 : 0 /* compare stack values */ + +/* JSON types */ +#define JSON_HASH 0 +#define JSON_ARRAY 1 +#define JSON_STRING 2 +#define JSON_NUMBER 3 +#define JSON_NULL 4 +#define JSON_BOOL 5 + +#define JSON_PARSE_LAST_CHUNK 0x1 /* this is the last part of the string we're parsing */ + +#define JSON_PARSE_FLAG_CHECK(ctx, f) (ctx->flags & f) ? 1 : 0 /* check if a parser state is set */ + +#ifndef JSON_CLASS_KEY +#define JSON_CLASS_KEY "__c" +#endif +#ifndef JSON_DATA_KEY +#define JSON_DATA_KEY "__p" +#endif + + +struct jsonParserContextStruct { + int state; /* what are we currently parsing */ + char* chunk; /* the chunk we're currently parsing */ + int index; /* where we are in parsing the current chunk */ + int chunksize; /* the size of the current chunk */ + int flags; /* parser flags */ + osrfList* stateStack; /* represents the nest of object/array states */ + growing_buffer* buffer; /* used to hold JSON strings, number, true, false, and null sequences */ + growing_buffer* utfbuf; /* holds the current unicode characters */ + void* userData; /* opaque user pointer. we ignore this */ + struct jsonParserHandlerStruct* handler; /* the event handler struct */ +}; +typedef struct jsonParserContextStruct jsonParserContext; + +struct jsonParserHandlerStruct { + void (*handleStartObject) (void* userData); + void (*handleObjectKey) (void* userData, char* key); + void (*handleEndObject) (void* userData); + void (*handleStartArray) (void* userData); + void (*handleEndArray) (void* userData); + void (*handleNull) (void* userData); + void (*handleString) (void* userData, char* string); + void (*handleBool) (void* userData, int boolval); + void (*handleNumber) (void* userData, double num); + void (*handleError) (void* userData, char* err, ...); +}; +typedef struct jsonParserHandlerStruct jsonParserHandler; + +struct _jsonObjectStruct { + unsigned long size; /* number of sub-items */ + char* classname; /* optional class hint (not part of the JSON spec) */ + int type; /* JSON type */ + struct _jsonObjectStruct* parent; /* who we're attached to */ + union __jsonValue { /* cargo */ + osrfHash* h; /* object container */ + osrfList* l; /* array container */ + char* s; /* string */ + int b; /* bool */ +// double n; /* number */ + double n; /* number */ + } value; +}; +typedef struct _jsonObjectStruct jsonObject; + +struct _jsonIteratorStruct { + jsonObject* obj; /* the object we're traversing */ + osrfHashIterator* hashItr; /* the iterator for this hash */ + char* key; /* if this object is an object, the current key */ + unsigned long index; /* if this object is an array, the index */ +}; +typedef struct _jsonIteratorStruct jsonIterator; + + + +/** + * Allocates a new parser context object + * @param handler The event handler struct + * @param userData Opaque user pointer which is available in callbacks + * and ignored by the parser + * @return An allocated parser context, NULL on error + */ +jsonParserContext* jsonNewParser( jsonParserHandler* handler, void* userData); + +/** + * Deallocates a parser context + * @param ctx The context object + */ +void jsonParserFree( jsonParserContext* ctx ); + +/** + * Parse a chunk of data. + * @param ctx The parser context + * @param data The data to parse + * @param datalen The size of the chunk to parser + * @param flags Reserved + */ +int jsonParseChunk( jsonParserContext* ctx, char* data, int datalen, int flags ); + + +/** + * Parses a JSON string; + * @param str The string to parser + * @return The resulting JSON object or NULL on error + */ +jsonObject* jsonParseString( char* str ); +jsonObject* jsonParseStringRaw( char* str ); + +jsonObject* jsonParseStringFmt( char* str, ... ); + +/** + * Parses a JSON string; + * @param str The string to parser + * @return The resulting JSON object or NULL on error + */ +jsonObject* jsonParseStringHandleError( void (*errorHandler) (const char*), char* str, ... ); + + + +/** + * Creates a new json object + * @param data The string data this object will hold if + * this object happens to be a JSON_STRING, NULL otherwise + * @return The allocated json object. Must be freed with + * jsonObjectFree() + */ +jsonObject* jsonNewObject(const char* data); +jsonObject* jsonNewObjectFmt(const char* data, ...); + +/** + * Creates a new object of the given type + */ +jsonObject* jsonNewObjectType(int type); + +/** + * Creates a new number object + */ +jsonObject* jsonNewNumberObject( double num ); + + +/** + * Creates a new json bool + */ +jsonObject* jsonNewBoolObject(int val); + +/** + * Deallocates an object + */ +void jsonObjectFree( jsonObject* o ); + +/** + * Forces the given object to become an array (if it isn't already one) + * and pushes the new object into the array + */ +unsigned long jsonObjectPush(jsonObject* o, jsonObject* newo); + +/** + * Forces the given object to become a hash (if it isn't already one) + * and assigns the new object to the key of the hash + */ +unsigned long jsonObjectSetKey( + jsonObject* o, const char* key, jsonObject* newo); + + +/** + * Turns the object into a JSON string. The string must be freed by the caller */ +char* jsonObjectToJSON( const jsonObject* obj ); +char* jsonObjectToJSONRaw( const jsonObject* obj ); + + +/** + * Retrieves the object at the given key + */ +jsonObject* jsonObjectGetKey( const jsonObject* obj, const char* key ); + + + + + + +/** Allocates a new iterator + @param obj The object over which to iterate. +*/ +jsonIterator* jsonNewIterator(const jsonObject* obj); + + +/** + De-allocates an iterator + @param iter The iterator object to free +*/ +void jsonIteratorFree(jsonIterator* iter); + +/** + Returns the object_node currently pointed to by the iterator + and increments the pointer to the next node + @param iter The iterator in question. + */ +jsonObject* jsonIteratorNext(jsonIterator* iter); + + +/** + @param iter The iterator. + @return True if there is another node after the current node. + */ +int jsonIteratorHasNext(const jsonIterator* iter); + + +/** + Returns a pointer to the object at the given index. This call is + only valid if the object has a type of JSON_ARRAY. + @param obj The object + @param index The position within the object + @return The object at the given index. +*/ +jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index ); + + +/* removes (and deallocates) the object at the given index (if one exists) and inserts + * the new one. returns the size on success, -1 on error + * If obj is NULL, inserts a new object into the list with is_null set to true + */ +unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj); + +/* removes the object at the given index and, if more items exist, + * re-indexes (shifts down by 1) the rest of the objects in the array + */ +unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index); + +/* removes (and deallocates) the object with key 'key' if it exists */ +unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key); + +/* returns a pointer to the string data held by this object if this object + is a string. Otherwise returns NULL*/ +char* jsonObjectGetString(const jsonObject*); + +double jsonObjectGetNumber( const jsonObject* obj ); + +/* sets the string data */ +void jsonObjectSetString(jsonObject* dest, const char* string); + +/* sets the number value for the object */ +void jsonObjectSetNumber(jsonObject* dest, double num); + +/* sets the class hint for this object */ +void jsonObjectSetClass(jsonObject* dest, const char* classname ); +const char* jsonObjectGetClass(const jsonObject* dest); + +int jsonBoolIsTrue( jsonObject* boolObj ); + +void jsonSetBool(jsonObject* bl, int val); + +jsonObject* jsonObjectClone( const jsonObject* o ); + + +/* tries to extract the string data from an object. + if object -> NULL (the C NULL) + if array -> NULL + if null -> NULL + if bool -> NULL + if string/number the string version of either of those + The caller is responsible for freeing the returned string + */ +char* jsonObjectToSimpleString( const jsonObject* o ); + + + +/* provides an XPATH style search interface (e.g. /some/node/here) and + return the object at that location if one exists. Naturally, + every element in the path must be a proper object ("hash" / {}). + Returns NULL if the specified node is not found + Note also that the object returned is a clone and + must be freed by the caller +*/ +jsonObject* jsonObjectFindPath( const jsonObject* obj, char* path, ... ); + + +/* formats a JSON string from printing. User must free returned string */ +char* jsonFormatString( const char* jsonString ); + +/* sets the error handler for all parsers */ +void jsonSetGlobalErrorHandler(void (*errorHandler) (const char*)); + +jsonObject* jsonParseFile( char* filename ); + +/* ------------------------------------------------------------------------- */ +/** + * The following methods provide a ficility for serializing and + * deserializing "classed" JSON objects. To give a JSON object a + * class, simply call jsonObjectSetClass(). + * Then, calling jsonObjectEncodeClass() will convert the JSON + * object (and any sub-objects) to a JSON object with class + * wrapper objects like so: + * { _c : "classname", _d : } + * In this example _c is the class key and _d is the data (object) + * key. The keys are defined by the constants + * OSRF_JSON_CLASS_KEY and OSRF_JSON_DATA_KEY + * To revive a serialized object, simply call + * jsonObjectDecodeClass() + */ + + +/** Converts a class-wrapped object into an object with the + * classname set + * Caller must free the returned object + */ +jsonObject* jsonObjectDecodeClass( jsonObject* obj ); + + +/** Converts an object with a classname into a + * class-wrapped (serialized) object + * Caller must free the returned object + */ +jsonObject* jsonObjectEncodeClass( jsonObject* obj ); + +/* ------------------------------------------------------------------------- */ + + +/** + * Generates an XML representation of a JSON object */ +char* jsonObjectToXML(jsonObject*); + + +/* + * Builds a JSON object from the provided XML + */ +jsonObject* jsonXMLToJSONObject(const char* xml); + + +#endif diff --git a/include/opensrf/osrf_json_utils.h b/include/opensrf/osrf_json_utils.h new file mode 100644 index 0000000..db3c1aa --- /dev/null +++ b/include/opensrf/osrf_json_utils.h @@ -0,0 +1,131 @@ +/* +Copyright (C) 2006 Georgia Public Library Service +Bill Erickson + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + + +/* ----------------------------------------------------------------------- */ +/* Clients need not include this file. These are internal utilities only */ +/* ----------------------------------------------------------------------- */ + +#define JSON_EAT_WS(ctx) \ + while( ctx->index < ctx->chunksize ) { \ + if(!isspace(ctx->chunk[ctx->index])) break; \ + ctx->index++; \ + } \ + if( ctx->index >= ctx->chunksize ) return 0; \ + c = ctx->chunk[ctx->index]; + +#define JSON_CACHE_DATA(ctx, buf, size) \ + while( (buf->n_used < size) && (ctx->index < ctx->chunksize) ) \ + buffer_add_char(buf, ctx->chunk[ctx->index++]); + +#define JSON_LOG_MARK __FILE__,__LINE__ + +#define JSON_NUMBER_CHARS "0123456789.+-e" + + +/* cleans up an object if it is morphing another object, also + * verifies that the appropriate storage container exists where appropriate */ +#define JSON_INIT_CLEAR(_obj_, newtype) \ + if( _obj_->type == JSON_HASH && newtype != JSON_HASH ) { \ + osrfHashFree(_obj_->value.h); \ + _obj_->value.h = NULL; \ + } else if( _obj_->type == JSON_ARRAY && newtype != JSON_ARRAY ) { \ + osrfListFree(_obj_->value.l); \ + _obj_->value.l = NULL; \ + } else if( _obj_->type == JSON_STRING && newtype != JSON_STRING ) { \ + free(_obj_->value.s); \ + _obj_->value.s = NULL; \ + } \ + _obj_->type = newtype;\ + if( newtype == JSON_HASH && _obj_->value.h == NULL ) { \ + _obj_->value.h = osrfNewHash(); \ + _obj_->value.h->freeItem = _jsonFreeHashItem; \ + } else if( newtype == JSON_ARRAY && _obj_->value.l == NULL ) { \ + _obj_->value.l = osrfNewList(); \ + _obj_->value.l->freeItem = _jsonFreeListItem;\ + } \ + + +/** + * These are the callbacks through which the top level parser + * builds objects via the push parser + */ +void _jsonHandleStartObject(void*); +void _jsonHandleObjectKey(void*, char* key); +void _jsonHandleEndObject(void*); +void _jsonHandleStartArray(void*); +void _jsonHandleEndArray(void*); +void _jsonHandleNull(void*); +void _jsonHandleString(void*, char* string); +void _jsonHandleBool(void*, int boolval); +void _jsonHandleNumber(void*, double num); +void _jsonHandleError(void*, char* str, ...); + +struct jsonInternalParserStruct { + jsonParserContext* ctx; + jsonObject* obj; + jsonObject* current; + char* lastkey; + void (*handleError) (const char*); +}; +typedef struct jsonInternalParserStruct jsonInternalParser; + +jsonInternalParser* _jsonNewInternalParser(); +void _jsonInternalParserFree(jsonInternalParser* p); + +/** + * Calls the defined error handler with the given error message. + * @return -1 + */ +int _jsonParserError( jsonParserContext* ctx, char* err, ... ); + + +/** + * + * @return 0 on continue, 1 if it goes past the end of the string, -1 on error + */ +int _jsonParserHandleUnicode( jsonParserContext* ctx ); + + +/** + * @param type 0 for null, 1 for true, 2 for false + * @return 0 on continue, 1 if it goes past the end of the string, -1 on error + */ +int _jsonParserHandleMatch( jsonParserContext* ctx, int type ); + +/** + * @return 0 on continue, 1 on end of chunk, -1 on error + */ +int _jsonParserHandleString( jsonParserContext* ctx ); + +/** + * @return 0 on continue, 1 on end of chunk, -1 on error + */ +int _jsonParserHandleNumber( jsonParserContext* ctx ); + + +void _jsonInsertParserItem( jsonInternalParser* p, jsonObject* newo ); + + +/* Utility method. finds any object in the tree that matches the path. + Use this for finding paths that start with '//' */ +jsonObject* _jsonObjectFindPathRecurse( const jsonObject* o, char* root, char* path ); + + +/* returns a list of object whose key is 'root'. These are used as + potential objects when doing a // search */ +jsonObject* __jsonObjectFindPathRecurse( const jsonObject* o, char* root ); + + diff --git a/include/opensrf/osrf_json_xml.h b/include/opensrf/osrf_json_xml.h new file mode 100644 index 0000000..c01b1ac --- /dev/null +++ b/include/opensrf/osrf_json_xml.h @@ -0,0 +1,26 @@ +#ifdef OSRF_JSON_ENABLE_XML_UTILS + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +/** + * Generates an XML representation of a JSON object */ +char* jsonObjectToXML(jsonObject*); + + +/* + * Builds a JSON object from the provided XML + */ +jsonObject* jsonXMLToJSONObject(const char* xml); + +#endif diff --git a/include/opensrf/osrf_legacy_json.h b/include/opensrf/osrf_legacy_json.h new file mode 100644 index 0000000..a79fbbe --- /dev/null +++ b/include/opensrf/osrf_legacy_json.h @@ -0,0 +1,135 @@ +/* +Copyright (C) 2005 Georgia Public Library Service +Bill Erickson + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + + + + +/* --------------------------------------------------------------------------------------- + JSON parser. + * --------------------------------------------------------------------------------------- */ +#ifndef LEGACY_JSON_H +#define LEGACY_JSON_H + +#include +#include + + + +/* Parses the given JSON string and returns the built object. + * returns NULL (and prints parser error to stderr) on error. + */ + +jsonObject* json_parse_string(char* string); + +jsonObject* legacy_jsonParseString(char* string); +jsonObject* legacy_jsonParseStringFmt( char* string, ... ); + +jsonObject* json_parse_file( const char* filename ); + +jsonObject* legacy_jsonParseFile( const char* string ); + + + +/* does the actual parsing work. returns 0 on success. -1 on error and + * -2 if there was no object to build (string was all comments) + */ +int _json_parse_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen); + +/* returns 0 on success and turns obj into a string object */ +int json_parse_json_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen); + +/* returns 0 on success and turns obj into a number or double object */ +int json_parse_json_number(char* string, unsigned long* index, jsonObject* obj, int current_strlen); + +/* returns 0 on success and turns obj into an 'object' object */ +int json_parse_json_object(char* string, unsigned long* index, jsonObject* obj, int current_strlen); + +/* returns 0 on success and turns object into an array object */ +int json_parse_json_array(char* string, unsigned long* index, jsonObject* obj, int current_strlen); + +/* churns through whitespace and increments index as it goes. + * eat_all == true means we should eat newlines, tabs + */ +void json_eat_ws(char* string, unsigned long* index, int eat_all, int current_strlen); + +int json_parse_json_bool(char* string, unsigned long* index, jsonObject* obj, int current_strlen); + +/* removes comments from a json string. if the comment contains a class hint + * and class_hint isn't NULL, an allocated char* with the class name will be + * shoved into *class_hint. returns 0 on success, -1 on parse error. + * 'index' is assumed to be at the second character (*) of the comment + */ +int json_eat_comment(char* string, unsigned long* index, char** class_hint, int parse_class, int current_strlen); + +/* prints a useful error message to stderr. always returns -1 */ +int json_handle_error(char* string, unsigned long* index, char* err_msg); + +int json_parse_json_null(char* string, unsigned long* index, jsonObject* obj, int current_strlen); + + +char* legacy_jsonObjectToJSON( const jsonObject* obj ); + + + +/* LEGACY ITERATOR CODE --------------------------------------------------- + ------------------------------------------------------------------------ */ + +struct _jsonObjectNodeStruct { + unsigned long index; /* our array position */ + char* key; /* our hash key */ + jsonObject* item; /* our object */ +}; +typedef struct _jsonObjectNodeStruct jsonObjectNode; + + + +/* utility object for iterating over hash objects */ +struct _jsonObjectIteratorStruct { + jsonIterator* iterator; + const jsonObject* obj; /* the topic object */ + jsonObjectNode* current; /* the current node within the object */ + int done; +}; +typedef struct _jsonObjectIteratorStruct jsonObjectIterator; + + +/** Allocates a new iterator + @param obj The object over which to iterate. +*/ +jsonObjectIterator* jsonNewObjectIterator(const jsonObject* obj); + +/** + De-allocates an iterator + @param iter The iterator object to free +*/ +void jsonObjectIteratorFree(jsonObjectIterator* iter); + +/** + Returns the object_node currently pointed to by the iterator + and increments the pointer to the next node + @param iter The iterator in question. + */ +jsonObjectNode* jsonObjectIteratorNext(jsonObjectIterator* iter); + +/** + @param iter The iterator. + @return True if there is another node after the current node. + */ +int jsonObjectIteratorHasNext(const jsonObjectIterator* iter); + + +#endif + + diff --git a/include/opensrf/osrf_message.h b/include/opensrf/osrf_message.h index 12e14c6..ca79082 100644 --- a/include/opensrf/osrf_message.h +++ b/include/opensrf/osrf_message.h @@ -1,8 +1,7 @@ #include #include #include -#include -#include +#include /* libxml stuff for the config reader */ diff --git a/include/opensrf/osrf_settings.h b/include/opensrf/osrf_settings.h index 9aa0d47..d0e23df 100644 --- a/include/opensrf/osrf_settings.h +++ b/include/opensrf/osrf_settings.h @@ -12,8 +12,7 @@ #include #include -#include -#include +#include typedef struct { char* hostname; diff --git a/include/opensrf/string_array.h b/include/opensrf/string_array.h index 77b3de0..70b0038 100644 --- a/include/opensrf/string_array.h +++ b/include/opensrf/string_array.h @@ -2,17 +2,20 @@ #include #include +#include -#define STRING_ARRAY_MAX_SIZE 1024 +#define STRING_ARRAY_MAX_SIZE 4096 #ifndef STRING_ARRAY_H #define STRING_ARRAY_H +#define OSRF_STRING_ARRAY_FREE(arr)\ + if(arr) {osrfListFree(arr->list); free(arr);} + + struct string_array_struct { - char** array; - int size; - int arr_size; - int total_string_size; + osrfList* list; + int size; }; typedef struct string_array_struct string_array; typedef struct string_array_struct osrfStringArray; diff --git a/include/opensrf/utils.h b/include/opensrf/utils.h index 7bbade0..c6832cd 100644 --- a/include/opensrf/utils.h +++ b/include/opensrf/utils.h @@ -64,6 +64,10 @@ GNU General Public License for more details. }\ }while(0) +#define OSRF_BUFFER_RESET(gb) \ + memset(gb->buf, 0, gb->size);\ + gb->n_used = 0; + diff --git a/include/opensrf/xml_utils.h b/include/opensrf/xml_utils.h index 2b3a030..e0321a4 100644 --- a/include/opensrf/xml_utils.h +++ b/include/opensrf/xml_utils.h @@ -1,7 +1,7 @@ #ifndef _XML_UTILS_H #define _XML_UTILS_H -#include +#include #include #include diff --git a/install.conf b/install.conf index 35911d6..667f634 100644 --- a/install.conf +++ b/install.conf @@ -12,3 +12,4 @@ export APXS2=/usr/bin/apxs2 export APACHE2_HEADERS=/usr/include/apache2 export APR_HEADERS=/usr/include/apr-1.0/ export LIBXML2_HEADERS=/usr/include/libxml2/ +export OSRF_LEGACY_JSON=1 diff --git a/src/Makefile b/src/Makefile index 14823ab..d377724 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,17 +1,20 @@ # TOP level 'src' makefile for OpenSRF -export TMPDIR = $(TMP)/opensrf -export OPENSRF = opensrf -export BINDIR = $(PREFIX)/bin -export LIBDIR = $(PREFIX)/lib -export PERLDIR = $(LIBDIR)/perl5 -export INCLUDEDIR = $(PREFIX)/include - -export LDLIBS += -export LDFLAGS += -Wl,-rpath=$(LIBDIR) -L $(TMPDIR) -L . -export CFLAGS += -pipe -g -Wall -O2 -fPIC -I ../../include/ -I$(LIBXML2_HEADERS) -I$(APACHE2_HEADERS) \ - -I$(LIBXML2_HEADERS)/libxml -I$(APR_HEADERS) +export TMPDIR = $(TMP)/opensrf +export OPENSRF = opensrf +export BINDIR = $(PREFIX)/bin +export LIBDIR = $(PREFIX)/lib +export PERLDIR = $(LIBDIR)/perl5 +export INCLUDEDIR= $(PREFIX)/include + +export LDLIBS += +export LDFLAGS += -Wl,-rpath=$(LIBDIR) -L $(TMPDIR) -L . +export CFLAGS += -pipe -g -Wall -O2 -fPIC -I ../../include/ -I$(LIBXML2_HEADERS) -I$(APACHE2_HEADERS) -I$(APR_HEADERS) + +ifeq ($(OSRF_LEGACY_JSON), 1) +export LDLIBS += -lobjson +endif all: prep \ opensrf \ @@ -26,8 +29,7 @@ install: install-prep \ router-install \ srfsh-install \ jserver-install \ - perl-install \ - objson-install + perl-install # -------------------------------------------------------------------------------- @@ -37,11 +39,7 @@ install: install-prep \ prep: mkdir -p $(TMPDIR) -objson/libobjson.so: prep - @echo $@ - make -C objson - -opensrf: objson/libobjson.so +opensrf: prep make -C libopensrf make -C c-apps @@ -75,12 +73,8 @@ install-prep: mkdir -p $(INCLUDEDIR) mkdir -p $(ETCDIR) -objson-install: install-prep - @echo $@ - make -C objson install - # installs libopensrf.so, opensrf-c, headers, example configs, and osrf_ctl.sh -opensrf-install: objson-install +opensrf-install: install-prep @echo $@ cp $(TMPDIR)/libopensrf.so $(LIBDIR)/libopensrf.so cp -r ../include/opensrf $(INCLUDEDIR) @@ -90,6 +84,10 @@ opensrf-install: objson-install cp ../examples/opensrf.xml.example $(ETCDIR) cp ../examples/opensrf_core.xml.example $(ETCDIR) cp ../examples/srfsh.xml.example $(ETCDIR) + if [ ! -z "$(OSRF_LEGACY_JSON)" ]; then\ + cp -r ../include/objson $(INCLUDEDIR);\ + cp $(TMPDIR)/libobjson.so $(LIBDIR);\ + fi gateway-install: install-prep opensrf-install @echo $@ @@ -120,7 +118,6 @@ clean: make -C router clean make -C gateway clean make -C jserver clean - make -C objson clean make -C srfsh clean make -C c-apps clean /bin/rm -rf $(TMPDIR) *.o diff --git a/src/c-apps/Makefile b/src/c-apps/Makefile index ceda792..7e99ef1 100644 --- a/src/c-apps/Makefile +++ b/src/c-apps/Makefile @@ -1,4 +1,4 @@ -LDLIBS += -lobjson -lopensrf +LDLIBS += -lopensrf CFLAGS += -DOSRF_LOG_PARAMS all: osrf_math.so osrf_dbmath.so osrf_version.so diff --git a/src/c-apps/osrf_dbmath.c b/src/c-apps/osrf_dbmath.c index 0b930cd..a88db87 100644 --- a/src/c-apps/osrf_dbmath.c +++ b/src/c-apps/osrf_dbmath.c @@ -1,7 +1,7 @@ -#include "opensrf/osrf_app_session.h" -#include "opensrf/osrf_application.h" -#include "objson/object.h" -#include "opensrf/log.h" +#include +#include +#include +#include #define MODULENAME "opensrf.dbmath" diff --git a/src/c-apps/osrf_math.c b/src/c-apps/osrf_math.c index 63d168a..7c3cf29 100644 --- a/src/c-apps/osrf_math.c +++ b/src/c-apps/osrf_math.c @@ -1,7 +1,7 @@ -#include "opensrf/osrf_app_session.h" -#include "opensrf/osrf_application.h" -#include "objson/object.h" -#include "opensrf/log.h" +#include +#include +#include +#include #define MODULENAME "opensrf.math" diff --git a/src/c-apps/osrf_version.c b/src/c-apps/osrf_version.c index 1affb70..9d07874 100644 --- a/src/c-apps/osrf_version.c +++ b/src/c-apps/osrf_version.c @@ -1,6 +1,6 @@ #include "opensrf/osrf_app_session.h" #include "opensrf/osrf_application.h" -#include "objson/object.h" +#include "opensrf/osrf_json.h" #include "opensrf/utils.h" #include "opensrf/log.h" diff --git a/src/gateway/Makefile b/src/gateway/Makefile index 2120b23..3d60411 100644 --- a/src/gateway/Makefile +++ b/src/gateway/Makefile @@ -1,6 +1,6 @@ #CFLAGS += -DASSUME_STATELESS -DOSRF_GATEWAY_NASTY_DEBUG CFLAGS += -DASSUME_STATELESS -LDLIBS += -lobjson -lopensrf +LDLIBS += -lopensrf all: osrf_json_gateway.so copy diff --git a/src/gateway/osrf_json_gateway.c b/src/gateway/osrf_json_gateway.c index 80bf722..9dfa5d3 100644 --- a/src/gateway/osrf_json_gateway.c +++ b/src/gateway/osrf_json_gateway.c @@ -2,9 +2,9 @@ #include "opensrf/osrf_app_session.h" #include "opensrf/osrf_system.h" #include "opensrf/osrfConfig.h" -#include "objson/object.h" -#include "objson/json2xml.h" -#include "objson/xml2json.h" +#include +#include +#include #include #include #include @@ -13,6 +13,8 @@ #define MODULE_NAME "osrf_json_gateway_module" #define GATEWAY_CONFIG "OSRFGatewayConfig" #define CONFIG_CONTEXT "gateway" +#define JSON_PROTOCOL "OSRFGatewayLegacyJSON" +#define GATEWAY_USE_LEGACY_JSON 1 #define GATEWAY_DEFAULT_CONFIG "/openils/conf/opensrf_core.xml" @@ -22,6 +24,11 @@ typedef struct { char* configfile; /* our bootstrap config file */ } osrf_json_gateway_config; +typedef struct { + int legacyJSON; +} osrf_json_gateway_dir_config; + + module AP_MODULE_DECLARE_DATA osrf_json_gateway_module; char* osrf_json_gateway_config_file = NULL; @@ -37,10 +44,18 @@ static const char* osrf_json_gateway_set_config(cmd_parms *parms, void *config, return NULL; } +static const char* osrf_json_gateway_set_json_proto(cmd_parms *parms, void *config, const char *arg) { + osrf_json_gateway_dir_config* cfg = (osrf_json_gateway_dir_config*) config; + cfg->legacyJSON = (!strcasecmp((char*) arg, "false")) ? 0 : 1; + return NULL; +} + /* tell apache about our commands */ static const command_rec osrf_json_gateway_cmds[] = { AP_INIT_TAKE1( GATEWAY_CONFIG, osrf_json_gateway_set_config, NULL, RSRC_CONF, "osrf json gateway config file"), + AP_INIT_TAKE1( JSON_PROTOCOL, osrf_json_gateway_set_json_proto, + NULL, ACCESS_CONF, "osrf json gateway config file"), {NULL} }; @@ -52,6 +67,13 @@ static void* osrf_json_gateway_create_config( apr_pool_t* p, server_rec* s) { return (void*) cfg; } +static void* osrf_json_gateway_create_dir_config( apr_pool_t* p, char* dir) { + osrf_json_gateway_dir_config* cfg = (osrf_json_gateway_dir_config*) + apr_palloc(p, sizeof(osrf_json_gateway_dir_config)); + cfg->legacyJSON = GATEWAY_USE_LEGACY_JSON; + return (void*) cfg; +} + static void osrf_json_gateway_child_init(apr_pool_t *p, server_rec *s) { @@ -84,6 +106,25 @@ static int osrf_json_gateway_method_handler (request_rec *r) { /* make sure we're needed first thing*/ if (strcmp(r->handler, MODULE_NAME )) return DECLINED; + + osrf_json_gateway_dir_config* dir_conf = + ap_get_module_config(r->per_dir_config, &osrf_json_gateway_module); + + + /* provide 2 different JSON parsers and serializers to support legacy JSON */ + jsonObject* (*parseJSONFunc) (char*) = legacy_jsonParseString; + char* (*jsonToStringFunc) (const jsonObject*) = legacy_jsonObjectToJSON; + + if(dir_conf->legacyJSON) { + ap_log_rerror( APLOG_MARK, APLOG_INFO, 0, r, "Using legacy JSON"); + + } else { + ap_log_rerror( APLOG_MARK, APLOG_INFO, 0, r, "Not using legacy JSON"); + parseJSONFunc = jsonParseString; + jsonToStringFunc = jsonObjectToJSON; + } + + osrfLogDebug(OSRF_LOG_MARK, "osrf gateway: entered request handler"); /* verify we are connected */ @@ -169,7 +210,13 @@ static int osrf_json_gateway_method_handler (request_rec *r) { int req_id = -1; if(!strcasecmp(input_format, "json")) { - req_id = osrf_app_session_make_req( session, NULL, method, api_level, mparams ); + jsonObject * arr = jsonNewObject(NULL); + char* str; + int i = 0; + while( (str = osrfStringArrayGetString(mparams, i++)) ) + jsonObjectPush(arr, parseJSONFunc(str)); + + req_id = osrf_app_session_make_req( session, arr, method, api_level, NULL ); } else { @@ -247,7 +294,8 @@ static int osrf_json_gateway_method_handler (request_rec *r) { if (isXML) { output = jsonObjectToXML( res ); } else { - output = jsonObjectToJSON( res ); + //output = jsonObjectToJSON( res ); + output = jsonToStringFunc( res ); if( morethan1 ) ap_rputs(",", r); /* comma between JSON array items */ } ap_rputs(output, r); @@ -293,7 +341,8 @@ static int osrf_json_gateway_method_handler (request_rec *r) { bzero(bb, l); snprintf(bb, l, "%s : %s", statusname, statustext); jsonObject* tmp = jsonNewObject(bb); - char* j = jsonObjectToJSON(tmp); + char* j = jsonToStringFunc(tmp); + //char* j = jsonObjectToJSON(tmp); snprintf( buf, l, ",\"debug\": %s", j); free(j); jsonObjectFree(tmp); @@ -344,7 +393,7 @@ static void osrf_json_gateway_register_hooks (apr_pool_t *p) { module AP_MODULE_DECLARE_DATA osrf_json_gateway_module = { STANDARD20_MODULE_STUFF, - NULL, + osrf_json_gateway_create_dir_config, NULL, osrf_json_gateway_create_config, NULL, diff --git a/src/jserver/Makefile b/src/jserver/Makefile index e21cfe1..33f9ced 100644 --- a/src/jserver/Makefile +++ b/src/jserver/Makefile @@ -1,4 +1,4 @@ -LDLIBS += -lopensrf -lobjson -lxml2 +LDLIBS += -lopensrf -lxml2 CFLAGS += -D_GNU_SOURCE all: chopchop diff --git a/src/libopensrf/Makefile b/src/libopensrf/Makefile index dff2024..c6d5d53 100644 --- a/src/libopensrf/Makefile +++ b/src/libopensrf/Makefile @@ -1,10 +1,17 @@ -# OSRF_LOG_PARAMS log all incoming method params at OSRF_INFO log level. -# OSRF_STRICT_PARAMS instructs the app handler to return an error if the number of method arguments -# provided to any method is not at least as large as the 'argc' setting for the method +# ------------------------------------------------------------------ +# To build the standalone JSON lib libosrf_json.so: +# $ make libosrf_json.so +# To build the standalone JSON lib libosrf_json.so with XML utils +# support, use something like the following: +# $ CFLAGS="-DOSRF_JSON_ENABLE_XML_UTILS -I/usr/include/libxml2" LDLIBS=-lxml2 make libosrf_json.so +# +# The compiler flag -DOSRF_JSON_ALLOW_COMMENTS tells the parser to +# allow legacy JSON comments like /* comment */ +# ------------------------------------------------------------------ -CFLAGS += -DASSUME_STATELESS -DOSRF_LOG_PARAMS -DOSRF_STRICT_PARAMS -rdynamic -fno-strict-aliasing -LDLIBS += -lxml2 -lobjson -ldl -lmemcache -OSRF_INC = ../../include/opensrf/ +CFLAGS += -DASSUME_STATELESS -DOSRF_STRICT_PARAMS -rdynamic -fno-strict-aliasing -I../../include -fPIC -Wall -DOSRF_JSON_ENABLE_XML_UTILS +LDLIBS += -lxml2 -ldl -lmemcache +export OSRF_INC = ../../include/opensrf/ TARGETS = osrf_message.o \ osrf_app_session.o \ @@ -27,12 +34,24 @@ TARGETS = osrf_message.o \ utils.o\ socket_bundle.o\ sha.o\ - string_array.o + string_array.o +JSON_TARGETS = osrf_json_object.o\ + osrf_json_parser.o \ + osrf_json_tools.o \ + osrf_legacy_json.o \ + osrf_json_xml.o -all: opensrf +# use these when building the standalone JSON module +JSON_DEPS = osrf_list.o\ + osrf_hash.o\ + utils.o\ + log.o\ + md5.o\ + string_array.o +all: opensrf # Build the OpenSRF C binary opensrf: opensrf.o libopensrf.so @@ -41,8 +60,26 @@ opensrf: opensrf.o libopensrf.so # Build the OpenSRF library -libopensrf.so: $(TARGETS) - $(CC) -shared -W1 $(LDFLAGS) $(LDLIBS) $(TARGETS) -o $(TMPDIR)/libopensrf.so +libopensrf.so: $(TARGETS) json + if [ ! -z "$(OSRF_LEGACY_JSON)" ]; then\ + $(CC) -shared -W1 $(LDFLAGS) $(LDLIBS) $(TARGETS) -o $(TMPDIR)/libopensrf.so;\ + else\ + $(CC) -shared -W1 $(LDFLAGS) $(LDLIBS) $(TARGETS) $(JSON_TARGETS) -o $(TMPDIR)/libopensrf.so;\ + fi; + + +json: $(JSON_TARGETS) $(JSON_DEPS) + if [ ! -z "$(OSRF_LEGACY_JSON)" ]; then \ + $(CC) -shared -W1 $(CFLAGS) \ + $(LDFLAGS) $(JSON_TARGETS) $(JSON_DEPS) -o $(TMPDIR)/libobjson.so;\ + fi; + +libosrf_json.so: $(JSON_TARGETS) $(JSON_DEPS) + $(CC) -shared -W1 $(CFLAGS) \ + $(LDFLAGS) $(LDLIBS) $(JSON_TARGETS) $(JSON_DEPS) -o $@ + + +osrf_json_test: osrf_json_test.o $(JSON_TARGETS) $(JSON_DEPS) opensrf.o: opensrf.c @@ -66,8 +103,17 @@ utils.o: utils.c $(OSRF_INC)/utils.h socket_bundle.o: socket_bundle.c $(OSRF_INC)/socket_bundle.h sha.o: sha.c $(OSRF_INC)/sha.h string_array.o: string_array.c $(OSRF_INC)/string_array.h +osrf_json_object.o: osrf_json_object.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h +osrf_json_parser.o: osrf_json_parser.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h +osrf_json_tools.o: osrf_json_tools.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h +osrf_legacy_json.o: osrf_legacy_json.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h +osrf_json_xml.o: osrf_json_xml.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_xml.h +osrf_json_test.o: osrf_json_test.c clean: - /bin/rm -f *.o libopensrf.so opensrf + /bin/rm -f *.o *.so opensrf osrf_json_test + + + diff --git a/src/libopensrf/Makefile.json b/src/libopensrf/Makefile.json new file mode 100644 index 0000000..8de20a6 --- /dev/null +++ b/src/libopensrf/Makefile.json @@ -0,0 +1,40 @@ +#-DOSRF_JSON_ALLOW_COMMENTS + +# ------------------------------------------------------------------ +# To build a standalone version of libosrf_json, something +# like the following should work: +# $ CFLAGS="-fPIC -I /usr/include/libxml2 -I ../../include" \ +# OSRF_INC="../../include/opensrf" LDLIBS="-lxml2" \ +# make -f Makefile.json standalone +# ------------------------------------------------------------------ +TARGETS = osrf_json_object.o osrf_json_parser.o osrf_json_tools.o osrf_legacy_json.o osrf_json_xml.o + +# these are only needed when compiling the standalone version +EXT_TARGETS = osrf_list.o osrf_hash.o utils.o log.o md5.o string_array.o + +all: $(TARGETS) + if [ ! -z "$(OSRF_LEGACY_JSON)" ]; then \ + $(CC) -shared -W1 $(LDFLAGS) $(TARGETS) -o $(TMPDIR)/libobjson.so;\ + fi; + +standalone: $(TARGETS) $(EXT_TARGETS) + $(CC) -shared -W1 $(CFLAGS) $(LDFLAGS) $(LDLIBS) $(TARGETS) $(EXT_TARGETS) -o libosrf_json.so + +osrf_json_object.o: osrf_json_object.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h +osrf_json_parser.o: osrf_json_parser.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h +osrf_json_tools.o: osrf_json_tools.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h +osrf_legacy_json.o: osrf_legacy_json.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h +osrf_json_xml.o: osrf_json_xml.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_xml.h + + +osrf_list.o: osrf_list.c $(OSRF_INC)/osrf_list.h +osrf_hash.o: osrf_hash.c $(OSRF_INC)/osrf_hash.h +utils.o: utils.c $(OSRF_INC)/utils.h +md5.o: md5.c $(OSRF_INC)/md5.h +log.o: log.c $(OSRF_INC)/log.h +string_array.o: string_array.c $(OSRF_INC)/string_array.h + + +clean: + rm -f osrf_json*.o osrf_legacy_json.o libosrf_json.so + diff --git a/src/libopensrf/osrf_application.c b/src/libopensrf/osrf_application.c index 1f12b86..b69659f 100644 --- a/src/libopensrf/osrf_application.c +++ b/src/libopensrf/osrf_application.c @@ -1,11 +1,7 @@ #include -#include - -//osrfApplication* __osrfAppList = NULL; osrfHash* __osrfAppHash = NULL; - int osrfAppRegisterApplication( char* appName, char* soFile ) { if(!appName || ! soFile) return -1; char* error; diff --git a/src/libopensrf/osrf_hash.c b/src/libopensrf/osrf_hash.c index e00f09c..5447466 100644 --- a/src/libopensrf/osrf_hash.c +++ b/src/libopensrf/osrf_hash.c @@ -193,7 +193,7 @@ void osrfHashFree( osrfHash* hash ) { } osrfListFree(hash->hash); - osrfStringArrayFree(hash->keys); + OSRF_STRING_ARRAY_FREE(hash->keys); free(hash); } @@ -201,7 +201,6 @@ void osrfHashFree( osrfHash* hash ) { osrfHashIterator* osrfNewHashIterator( osrfHash* hash ) { if(!hash) return NULL; - //osrfHashIterator* itr = safe_malloc(sizeof(osrfHashIterator)); osrfHashIterator* itr; OSRF_MALLOC(itr, sizeof(osrfHashIterator)); itr->hash = hash; @@ -214,8 +213,7 @@ void* osrfHashIteratorNext( osrfHashIterator* itr ) { if(!(itr && itr->hash)) return NULL; if( itr->currentIdx >= itr->keys->size ) return NULL; free(itr->current); - itr->current = strdup( - osrfStringArrayGetString(itr->keys, itr->currentIdx++)); + itr->current = strdup(osrfStringArrayGetString(itr->keys, itr->currentIdx++)); char* val = osrfHashGet( itr->hash, itr->current ); return val; } @@ -223,14 +221,12 @@ void* osrfHashIteratorNext( osrfHashIterator* itr ) { void osrfHashIteratorFree( osrfHashIterator* itr ) { if(!itr) return; free(itr->current); - //osrfStringArrayFree(itr->keys); free(itr); } void osrfHashIteratorReset( osrfHashIterator* itr ) { if(!itr) return; free(itr->current); - //osrfStringArrayFree(itr->keys); itr->keys = osrfHashKeysInc(itr->hash); itr->currentIdx = 0; itr->current = NULL; diff --git a/src/libopensrf/osrf_json_object.c b/src/libopensrf/osrf_json_object.c new file mode 100644 index 0000000..0b1ffd4 --- /dev/null +++ b/src/libopensrf/osrf_json_object.c @@ -0,0 +1,400 @@ +/* +Copyright (C) 2006 Georgia Public Library Service +Bill Erickson + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include +#include + +jsonObject* jsonNewObject(const char* data) { + + jsonObject* o; + OSRF_MALLOC(o, sizeof(jsonObject)); + o->type = JSON_NULL; + + if(data) { + o->type = JSON_STRING; + o->value.s = strdup(data); + } + + return o; +} + +jsonObject* jsonNewObjectFmt(const char* data, ...) { + + jsonObject* o; + OSRF_MALLOC(o, sizeof(jsonObject)); + o->type = JSON_NULL; + + if(data) { + VA_LIST_TO_STRING(data); + o->type = JSON_STRING; + o->value.s = strdup(VA_BUF); + } + + return o; +} + +jsonObject* jsonNewNumberObject( double num ) { + jsonObject* o = jsonNewObject(NULL); + o->type = JSON_NUMBER; + o->value.n = num; + return o; +} + +jsonObject* jsonNewBoolObject(int val) { + jsonObject* o = jsonNewObject(NULL); + o->type = JSON_BOOL; + jsonSetBool(o, val); + return o; +} + +jsonObject* jsonNewObjectType(int type) { + jsonObject* o = jsonNewObject(NULL); + o->type = type; + return o; +} + +void jsonObjectFree( jsonObject* o ) { + + if(!o || o->parent) return; + free(o->classname); + + switch(o->type) { + case JSON_HASH : osrfHashFree(o->value.h); break; + case JSON_ARRAY : osrfListFree(o->value.l); break; + case JSON_STRING : free(o->value.s); break; + } + free(o); +} + +static void _jsonFreeHashItem(char* key, void* item){ + if(!item) return; + jsonObject* o = (jsonObject*) item; + o->parent = NULL; /* detach the item */ + jsonObjectFree(o); +} +static void _jsonFreeListItem(void* item){ + if(!item) return; + jsonObject* o = (jsonObject*) item; + o->parent = NULL; /* detach the item */ + jsonObjectFree(o); +} + +void jsonSetBool(jsonObject* bl, int val) { + if(!bl) return; + JSON_INIT_CLEAR(bl, JSON_BOOL); + bl->value.b = val; +} + +unsigned long jsonObjectPush(jsonObject* o, jsonObject* newo) { + if(!o) return -1; + if(!newo) newo = jsonNewObject(NULL); + JSON_INIT_CLEAR(o, JSON_ARRAY); + newo->parent = o; + osrfListPush( o->value.l, newo ); + o->size = o->value.l->size; + return o->size; +} + +unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj) { + if(!dest) return -1; + if(!newObj) newObj = jsonNewObject(NULL); + JSON_INIT_CLEAR(dest, JSON_ARRAY); + newObj->parent = dest; + osrfListSet( dest->value.l, newObj, index ); + dest->size = dest->value.l->size; + return dest->value.l->size; +} + +unsigned long jsonObjectSetKey( jsonObject* o, const char* key, jsonObject* newo) { + if(!o) return -1; + if(!newo) newo = jsonNewObject(NULL); + JSON_INIT_CLEAR(o, JSON_HASH); + newo->parent = o; + osrfHashSet( o->value.h, newo, key ); + o->size = o->value.h->size; + return o->size; +} + +jsonObject* jsonObjectGetKey( const jsonObject* obj, const char* key ) { + if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL; + return osrfHashGet( obj->value.h, key); +} + +char* jsonObjectToJSON( const jsonObject* obj ) { + jsonObject* obj2 = jsonObjectEncodeClass( (jsonObject*) obj); + char* json = jsonObjectToJSONRaw(obj2); + jsonObjectFree(obj2); + return json; +} + +char* jsonObjectToJSONRaw( const jsonObject* obj ) { + if(!obj) return NULL; + growing_buffer* buf = buffer_init(32); + int i; + char* json; + + switch(obj->type) { + + case JSON_BOOL : + if(obj->value.b) OSRF_BUFFER_ADD(buf, "true"); + else OSRF_BUFFER_ADD(buf, "false"); + break; + + case JSON_NUMBER: { + double x = obj->value.n; + if( x == (int) x ) { + INT_TO_STRING((int)x); + OSRF_BUFFER_ADD(buf, INTSTR); + + } else { + DOUBLE_TO_STRING(x); + OSRF_BUFFER_ADD(buf, DOUBLESTR); + } + break; + } + + case JSON_NULL: + OSRF_BUFFER_ADD(buf, "null"); + break; + + case JSON_STRING: + OSRF_BUFFER_ADD_CHAR(buf, '"'); + char* data = obj->value.s; + int len = strlen(data); + + char* output = uescape(data, len, 1); + OSRF_BUFFER_ADD(buf, output); + free(output); + OSRF_BUFFER_ADD_CHAR(buf, '"'); + break; + + case JSON_ARRAY: { + OSRF_BUFFER_ADD_CHAR(buf, '['); + if( obj->value.l ) { + for( i = 0; i != obj->value.l->size; i++ ) { + json = jsonObjectToJSONRaw(OSRF_LIST_GET_INDEX(obj->value.l, i)); + if(i > 0) OSRF_BUFFER_ADD(buf, ","); + OSRF_BUFFER_ADD(buf, json); + free(json); + } + } + OSRF_BUFFER_ADD_CHAR(buf, ']'); + break; + } + + case JSON_HASH: { + + OSRF_BUFFER_ADD_CHAR(buf, '{'); + osrfHashIterator* itr = osrfNewHashIterator(obj->value.h); + jsonObject* item; + int i = 0; + + while( (item = osrfHashIteratorNext(itr)) ) { + if(i++ > 0) OSRF_BUFFER_ADD(buf, ","); + buffer_fadd(buf, "\"%s\":", itr->current); + char* json = jsonObjectToJSONRaw(item); + OSRF_BUFFER_ADD(buf, json); + free(json); + } + + osrfHashIteratorFree(itr); + OSRF_BUFFER_ADD_CHAR(buf, '}'); + break; + } + } + + return buffer_release(buf); +} + + +jsonIterator* jsonNewIterator(const jsonObject* obj) { + if(!obj) return NULL; + jsonIterator* itr; + OSRF_MALLOC(itr, sizeof(jsonIterator)); + + itr->obj = (jsonObject*) obj; + itr->index = 0; + itr->key = NULL; + + if( obj->type == JSON_HASH ) + itr->hashItr = osrfNewHashIterator(obj->value.h); + + return itr; +} + +void jsonIteratorFree(jsonIterator* itr) { + if(!itr) return; + free(itr->key); + osrfHashIteratorFree(itr->hashItr); + free(itr); +} + +jsonObject* jsonIteratorNext(jsonIterator* itr) { + if(!(itr && itr->obj)) return NULL; + if( itr->obj->type == JSON_HASH ) { + if(!itr->hashItr) return NULL; + jsonObject* item = osrfHashIteratorNext(itr->hashItr); + free(itr->key); + itr->key = strdup(itr->hashItr->current); + return item; + } else { + return jsonObjectGetIndex( itr->obj, itr->index++ ); + } +} + +int jsonIteratorHasNext(const jsonIterator* itr) { + if(!(itr && itr->obj)) return 0; + if( itr->obj->type == JSON_HASH ) + return osrfHashIteratorHasNext( itr->hashItr ); + return (itr->index < itr->obj->size) ? 1 : 0; +} + +jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index ) { + if(!obj) return NULL; + return (obj->type == JSON_ARRAY) ? + (OSRF_LIST_GET_INDEX(obj->value.l, index)) : NULL; +} + + + +unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index) { + if( dest && dest->type == JSON_ARRAY ) { + osrfListRemove(dest->value.l, index); + return dest->value.l->size; + } + return -1; +} + + +unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key) { + if( dest && key && dest->type == JSON_HASH ) { + osrfHashRemove(dest->value.h, key); + return 1; + } + return -1; +} + +char* jsonObjectGetString(const jsonObject* obj) { + return (obj && obj->type == JSON_STRING) ? obj->value.s : NULL; +} + +double jsonObjectGetNumber( const jsonObject* obj ) { + return (obj && obj->type == JSON_NUMBER) ? obj->value.n : 0; +} + +void jsonObjectSetString(jsonObject* dest, const char* string) { + if(!(dest && string)) return; + JSON_INIT_CLEAR(dest, JSON_STRING); + free(dest->value.s); + dest->value.s = strdup(string); +} + +void jsonObjectSetNumber(jsonObject* dest, double num) { + if(!dest) return; + JSON_INIT_CLEAR(dest, JSON_NUMBER); + dest->value.n = num; +} + +void jsonObjectSetClass(jsonObject* dest, const char* classname ) { + if(!(dest && classname)) return; + free(dest->classname); + dest->classname = strdup(classname); +} +const char* jsonObjectGetClass(const jsonObject* dest) { + if(!dest) return NULL; + return dest->classname; +} + +jsonObject* jsonObjectClone( const jsonObject* o ) { + if(!o) return jsonNewObject(NULL); + + int i; + jsonObject* arr; + jsonObject* hash; + jsonIterator* itr; + jsonObject* tmp; + jsonObject* result = NULL; + + switch(o->type) { + case JSON_NULL: + result = jsonNewObject(NULL); + break; + case JSON_STRING: + result = jsonNewObject(jsonObjectGetString(o)); + break; + case JSON_NUMBER: + result = jsonNewNumberObject(jsonObjectGetNumber(o)); + break; + case JSON_BOOL: + result = jsonNewBoolObject(jsonBoolIsTrue((jsonObject*) o)); + break; + case JSON_ARRAY: + arr = jsonNewObject(NULL); + arr->type = JSON_ARRAY; + for(i=0; i < o->size; i++) + jsonObjectPush(arr, jsonObjectClone(jsonObjectGetIndex(o, i))); + result = arr; + break; + case JSON_HASH: + hash = jsonNewObject(NULL); + hash->type = JSON_HASH; + itr = jsonNewIterator(o); + while( (tmp = jsonIteratorNext(itr)) ) + jsonObjectSetKey(hash, itr->key, jsonObjectClone(tmp)); + jsonIteratorFree(itr); + result = hash; + break; + } + + jsonObjectSetClass(result, jsonObjectGetClass(o)); + return result; +} + +int jsonBoolIsTrue( jsonObject* boolObj ) { + if( boolObj && boolObj->type == JSON_BOOL && boolObj->value.b ) + return 1; + return 0; +} + + +char* jsonObjectToSimpleString( const jsonObject* o ) { + if(!o) return NULL; + + char* value = NULL; + + switch( o->type ) { + + case JSON_NUMBER: { + + if( o->value.n == (int) o->value.n ) { + INT_TO_STRING((int) o->value.n); + value = strdup(INTSTR); + + } else { + DOUBLE_TO_STRING(o->value.n); + value = strdup(DOUBLESTR); + } + + break; + } + + case JSON_STRING: + value = strdup(o->value.s); + } + + return value; +} + + diff --git a/src/libopensrf/osrf_json_parser.c b/src/libopensrf/osrf_json_parser.c new file mode 100644 index 0000000..0837652 --- /dev/null +++ b/src/libopensrf/osrf_json_parser.c @@ -0,0 +1,632 @@ +/* +Copyright (C) 2006 Georgia Public Library Service +Bill Erickson + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include +#include +#include + + +/* if the client sets a global error handler, this will point to it */ +static void (*jsonClientErrorCallback) (const char*) = NULL; + +/* these are the handlers for our internal parser */ +static jsonParserHandler jsonInternalParserHandlerStruct = { + _jsonHandleStartObject, + _jsonHandleObjectKey, + _jsonHandleEndObject, + _jsonHandleStartArray, + _jsonHandleEndArray, + _jsonHandleNull, + _jsonHandleString, + _jsonHandleBool, + _jsonHandleNumber, + _jsonHandleError +}; +static jsonParserHandler* + jsonInternalParserHandler = &jsonInternalParserHandlerStruct; + + +jsonParserContext* jsonNewParser( jsonParserHandler* handler, void* userData) { + jsonParserContext* ctx; + OSRF_MALLOC(ctx, sizeof(jsonParserContext)); + ctx->stateStack = osrfNewList(); + ctx->buffer = buffer_init(512); + ctx->utfbuf = buffer_init(5); + ctx->handler = handler; + ctx->state = 0; + ctx->index = 0; + ctx->chunk = NULL; + ctx->userData = userData; + return ctx; +} + +void jsonParserFree( jsonParserContext* ctx ) { + if(!ctx) return; + buffer_free(ctx->buffer); + buffer_free(ctx->utfbuf); + osrfListFree(ctx->stateStack); + free(ctx); +} + + +void jsonSetGlobalErrorHandler(void (*errorHandler) (const char*)) { + jsonClientErrorCallback = errorHandler; +} + + +int _jsonParserError( jsonParserContext* ctx, char* err, ... ) { + if( ctx->handler->handleError ) { + VA_LIST_TO_STRING(err); + int pre = ctx->index - 15; + int post= ctx->index + 15; + while( pre < 0 ) pre++; + while( post >= ctx->chunksize ) post--; + int l = post - pre; + char buf[l]; + memset(buf, 0, l); + snprintf(buf, l, ctx->chunk + pre); + ctx->handler->handleError( ctx->userData, + "*JSON Parser Error\n - char = %c\n " + "- index = %d\n - near => %s\n - %s", + ctx->chunk[ctx->index], ctx->index, buf, VA_BUF ); + } + JSON_STATE_SET(ctx, JSON_STATE_IS_INVALID); + return -1; +} + + +int _jsonParserHandleUnicode( jsonParserContext* ctx ) { + + /* collect as many of the utf characters as we can in this chunk */ + JSON_CACHE_DATA(ctx, ctx->utfbuf, 4); + + /* we ran off the end of the chunk */ + if( ctx->utfbuf->n_used < 4 ) { + JSON_STATE_SET(ctx, JSON_STATE_IN_UTF); + return 1; + } + + ctx->index--; /* push it back to index of the final utf char */ + + /* ----------------------------------------------------------------------- */ + /* We have all of the escaped unicode data. Write it to the buffer */ + /* The following chunk is used with permission from + * json-c http://oss.metaparadigm.com/json-c/ + */ + #define hexdigit(x) ( ((x) <= '9') ? (x) - '0' : ((x) & 7) + 9) + unsigned char utf_out[4]; + memset(utf_out,0,4); + char* buf = ctx->utfbuf->buf; + + unsigned int ucs_char = + (hexdigit(buf[0] ) << 12) + + (hexdigit(buf[1]) << 8) + + (hexdigit(buf[2]) << 4) + + hexdigit(buf[3]); + + if (ucs_char < 0x80) { + utf_out[0] = ucs_char; + OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out); + + } else if (ucs_char < 0x800) { + utf_out[0] = 0xc0 | (ucs_char >> 6); + utf_out[1] = 0x80 | (ucs_char & 0x3f); + OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out); + + } else { + utf_out[0] = 0xe0 | (ucs_char >> 12); + utf_out[1] = 0x80 | ((ucs_char >> 6) & 0x3f); + utf_out[2] = 0x80 | (ucs_char & 0x3f); + OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out); + } + /* ----------------------------------------------------------------------- */ + /* ----------------------------------------------------------------------- */ + + JSON_STATE_REMOVE(ctx, JSON_STATE_IN_UTF); + JSON_STATE_REMOVE(ctx, JSON_STATE_IN_ESCAPE); + OSRF_BUFFER_RESET(ctx->utfbuf); + return 0; +} + + + +/* type : 0=null, 1=true, 2=false */ +int _jsonParserHandleMatch( jsonParserContext* ctx, int type ) { + + switch(type) { + + case 0: /* JSON null */ + + /* first see if we have it all first */ + if( ctx->chunksize > (ctx->index + 3) ) { + if( strncasecmp(ctx->chunk + ctx->index, "null", 4) ) + return _jsonParserError(ctx, "Invalid JSON 'null' sequence"); + if( ctx->handler->handleNull ) + ctx->handler->handleNull(ctx->userData); + ctx->index += 4; + break; + } + + JSON_CACHE_DATA(ctx, ctx->buffer, 4); + if( ctx->buffer->n_used < 4 ) { + JSON_STATE_SET(ctx, JSON_STATE_IN_NULL); + return 1; + } + + if( strncasecmp(ctx->buffer->buf, "null", 4) ) + return _jsonParserError(ctx, "Invalid JSON 'null' sequence"); + if( ctx->handler->handleNull ) + ctx->handler->handleNull(ctx->userData); + break; + + case 1: /* JSON true */ + + /* see if we have it all first */ + if( ctx->chunksize > (ctx->index + 3) ) { + if( strncasecmp(ctx->chunk + ctx->index, "true", 4) ) + return _jsonParserError(ctx, "Invalid JSON 'true' sequence"); + if( ctx->handler->handleBool ) + ctx->handler->handleBool(ctx->userData, 1); + ctx->index += 4; + break; + } + + JSON_CACHE_DATA(ctx, ctx->buffer, 4); + if( ctx->buffer->n_used < 4 ) { + JSON_STATE_SET(ctx, JSON_STATE_IN_TRUE); + return 1; + } + if( strncasecmp( ctx->buffer->buf, "true", 4 ) ) { + return _jsonParserError(ctx, "Invalid JSON 'true' sequence"); + } + if( ctx->handler->handleBool ) + ctx->handler->handleBool(ctx->userData, 1); + break; + + case 2: /* JSON false */ + + /* see if we have it all first */ + if( ctx->chunksize > (ctx->index + 4) ) { + if( strncasecmp(ctx->chunk + ctx->index, "false", 5) ) + return _jsonParserError(ctx, "Invalid JSON 'false' sequence"); + if( ctx->handler->handleBool ) + ctx->handler->handleBool(ctx->userData, 0); + ctx->index += 5; + break; + } + + JSON_CACHE_DATA(ctx, ctx->buffer, 5); + if( ctx->buffer->n_used < 5 ) { + JSON_STATE_SET(ctx, JSON_STATE_IN_FALSE); + return 1; + } + if( strncasecmp( ctx->buffer->buf, "false", 5 ) ) + return _jsonParserError(ctx, "Invalid JSON 'false' sequence"); + if( ctx->handler->handleBool ) + ctx->handler->handleBool(ctx->userData, 0); + break; + + default: + fprintf(stderr, "Invalid type flag\n"); + return -1; + + } + + ctx->index--; /* set it back to the index of the final sequence character */ + OSRF_BUFFER_RESET(ctx->buffer); + JSON_STATE_REMOVE(ctx, JSON_STATE_IN_NULL); + JSON_STATE_REMOVE(ctx, JSON_STATE_IN_TRUE); + JSON_STATE_REMOVE(ctx, JSON_STATE_IN_FALSE); + + return 0; +} + + +int _jsonParserHandleString( jsonParserContext* ctx ) { + + char c = ctx->chunk[ctx->index]; + + if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_ESCAPE) ) { + + if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_UTF) ) { + + return _jsonParserHandleUnicode( ctx ); + + } else { + + switch(c) { + + /* handle all of the escape chars */ + case '\\': OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\\' ); break; + case '"' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\"' ); break; + case 't' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\t' ); break; + case 'b' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\b' ); break; + case 'f' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\f' ); break; + case 'r' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\r' ); break; + case 'n' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\n' ); break; + case 'u' : + ctx->index++; /* progress to the first utf char */ + return _jsonParserHandleUnicode( ctx ); + default : OSRF_BUFFER_ADD_CHAR( ctx->buffer, c ); + } + } + + JSON_STATE_REMOVE(ctx, JSON_STATE_IN_ESCAPE); + return 0; + + } else { + + switch(c) { + + case '"' : /* this string is ending */ + if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_KEY) ) { + + /* object key */ + if(ctx->handler->handleObjectKey) { + ctx->handler->handleObjectKey( + ctx->userData, ctx->buffer->buf); + } + + } else { /* regular json string */ + + if(ctx->handler->handleString) { + ctx->handler->handleString( + ctx->userData, ctx->buffer->buf ); + } + + } + + OSRF_BUFFER_RESET(ctx->buffer); /* flush the buffer and states */ + JSON_STATE_REMOVE(ctx, JSON_STATE_IN_STRING); + JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY); + break; + + case '\\' : JSON_STATE_SET(ctx, JSON_STATE_IN_ESCAPE); break; + default : OSRF_BUFFER_ADD_CHAR( ctx->buffer, c ); + } + } + return 0; +} + + +int _jsonParserHandleNumber( jsonParserContext* ctx ) { + char c = ctx->chunk[ctx->index]; + + do { + OSRF_BUFFER_ADD_CHAR(ctx->buffer, c); + c = ctx->chunk[++(ctx->index)]; + } while( strchr(JSON_NUMBER_CHARS, c) && ctx->index < ctx->chunksize ); + + /* if we're run off the end of the chunk and we're not parsing the last chunk, + * save the number and the state */ + if( ctx->index >= ctx->chunksize && + ! JSON_PARSE_FLAG_CHECK(ctx, JSON_PARSE_LAST_CHUNK) ) { + JSON_STATE_SET(ctx, JSON_STATE_IN_NUMBER); + return 1; + } + + /* make me more strict */ + char* err = NULL; + double d = strtod(ctx->buffer->buf, &err); + if(err && err[0] != '\0') + return _jsonParserError(ctx, "Invalid number sequence"); + JSON_STATE_REMOVE(ctx, JSON_STATE_IN_NUMBER); + OSRF_BUFFER_RESET(ctx->buffer); + if(ctx->handler->handleNumber) + ctx->handler->handleNumber( ctx->userData, d ); + ctx->index--; /* scooch back to the first non-digit number */ + return 0; +} + + + + +int jsonParseChunk( jsonParserContext* ctx, char* data, int datalen, int flags ) { + + if( !( ctx && ctx->handler && data && datalen > 0 )) return -1; + ctx->chunksize = datalen; + ctx->chunk = data; + ctx->flags = flags; + char c; + + if( JSON_STATE_CHECK(ctx, JSON_STATE_IS_INVALID) ) + return _jsonParserError( ctx, "JSON Parser cannot continue after an error" ); + + if( JSON_STATE_CHECK(ctx, JSON_STATE_IS_DONE) ) + return _jsonParserError( ctx, "Extra content at end of JSON data" ); + + for( ctx->index = 0; (ctx->index < ctx->chunksize) && + (c = ctx->chunk[ctx->index]); ctx->index++ ) { + + /* middle of parsing a string */ + if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_STRING)) { + if( _jsonParserHandleString(ctx) == -1 ) + return -1; + continue; + } + + /* middle of parsing a number */ + if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_NUMBER) ) { + if( _jsonParserHandleNumber(ctx) == -1 ) + return -1; + continue; + } + + +#ifdef OSRF_JSON_ALLOW_COMMENTS + /* we just saw a bare '/' character */ + if( JSON_STATE_CHECK(ctx, JSON_STATE_START_COMMENT) ) { + if(c == '*') { + JSON_STATE_REMOVE(ctx, JSON_STATE_START_COMMENT); + JSON_STATE_SET(ctx, JSON_STATE_IN_COMMENT); + continue; + } else { + return _jsonParserError( ctx, "Invalid comment initializer" ); + } + } + + /* we're currently in the middle of a comment block */ + if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_COMMENT) ) { + if(c == '*') { + JSON_STATE_REMOVE(ctx, JSON_STATE_IN_COMMENT); + JSON_STATE_SET(ctx, JSON_STATE_END_COMMENT); + continue; + } else { + continue; + } + } + + /* we're in a comment, and we just saw a '*' character */ + if( JSON_STATE_CHECK(ctx, JSON_STATE_END_COMMENT) ) { + if( c == '/' ) { /* comment is finished */ + JSON_STATE_REMOVE(ctx, JSON_STATE_END_COMMENT); + continue; + } else { + /* looks like this isn't the end of the comment after all */ + JSON_STATE_SET(ctx, JSON_STATE_IN_COMMENT); + JSON_STATE_REMOVE(ctx, JSON_STATE_END_COMMENT); + } + } +#endif + + /* if we're in the middle of parsing a null/true/false sequence */ + if( JSON_STATE_CHECK(ctx, (JSON_STATE_IN_NULL | + JSON_STATE_IN_TRUE | JSON_STATE_IN_FALSE)) ) { + + int type = (JSON_STATE_CHECK(ctx, JSON_STATE_IN_NULL)) ? 0 : + (JSON_STATE_CHECK(ctx, JSON_STATE_IN_TRUE)) ? 1 : 2; + + if( _jsonParserHandleMatch( ctx, type ) == -1 ) + return -1; + continue; + } + + JSON_EAT_WS(ctx); + + /* handle all of the top level characters */ + switch(c) { + + case '{' : /* starting an object */ + if( ctx->handler->handleStartObject) + ctx->handler->handleStartObject( ctx->userData ); + JSON_STATE_PUSH(ctx, JSON_STATE_IN_OBJECT); + JSON_STATE_SET(ctx, JSON_STATE_IN_KEY); + break; + + case '}' : /* ending an object */ + if( ctx->handler->handleEndObject) + ctx->handler->handleEndObject( ctx->userData ); + JSON_STATE_POP(ctx); + if( JSON_STATE_PEEK(ctx) == NULL ) + JSON_STATE_SET(ctx, JSON_STATE_IS_DONE); + break; + + case '[' : /* starting an array */ + if( ctx->handler->handleStartArray ) + ctx->handler->handleStartArray( ctx->userData ); + JSON_STATE_PUSH(ctx, JSON_STATE_IN_ARRAY); + break; + + case ']': /* ending an array */ + if( ctx->handler->handleEndArray ) + ctx->handler->handleEndArray( ctx->userData ); + JSON_STATE_POP(ctx); + if( JSON_STATE_PEEK(ctx) == NULL ) + JSON_STATE_SET(ctx, JSON_STATE_IS_DONE); + break; + + case ':' : /* done with the object key */ + JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY); + break; + + case ',' : /* after object or array item */ + if( JSON_STATE_CHECK_STACK(ctx, JSON_STATE_IN_OBJECT) ) + JSON_STATE_SET(ctx, JSON_STATE_IN_KEY); + break; + + case 'n' : + case 'N' : /* null */ + if( _jsonParserHandleMatch( ctx, 0 ) == -1) + return -1; + break; + + case 't' : + case 'T' : + if( _jsonParserHandleMatch( ctx, 1 ) == -1 ) + return -1; + break; + + case 'f' : + case 'F' : + if( _jsonParserHandleMatch( ctx, 2 ) == -1) + return -1; + break; + + case '"' : + JSON_STATE_SET(ctx, JSON_STATE_IN_STRING); + break; + +#ifdef OSRF_JSON_ALLOW_COMMENTS + case '/' : + JSON_STATE_SET(ctx, JSON_STATE_START_COMMENT); + break; +#endif + + default: + if( strchr(JSON_NUMBER_CHARS, c) ) { + if( _jsonParserHandleNumber( ctx ) == -1 ) + return -1; + } else { + return _jsonParserError( ctx, "Invalid Token" ); + } + } + } + + return 0; +} + + +jsonInternalParser* _jsonNewInternalParser() { + jsonInternalParser* p; + OSRF_MALLOC(p, sizeof(jsonInternalParser)); + p->ctx = jsonNewParser( jsonInternalParserHandler, p ); + p->obj = NULL; + p->lastkey = NULL; + return p; +} + +void _jsonInternalParserFree(jsonInternalParser* p) { + if(!p) return; + jsonParserFree(p->ctx); + free(p->lastkey); + free(p); +} + +static jsonObject* _jsonParseStringImpl(char* str, void (*errorHandler) (const char*) ) { + jsonInternalParser* parser = _jsonNewInternalParser(); + parser->handleError = errorHandler; + jsonParseChunk( parser->ctx, str, strlen(str), JSON_PARSE_LAST_CHUNK ); + jsonObject* obj = parser->obj; + _jsonInternalParserFree(parser); + return obj; +} + +jsonObject* jsonParseStringHandleError( + void (*errorHandler) (const char*), char* str, ... ) { + if(!str) return NULL; + VA_LIST_TO_STRING(str); + return _jsonParseStringImpl(VA_BUF, errorHandler); +} + +jsonObject* jsonParseString( char* str ) { + if(!str) return NULL; + jsonObject* obj = _jsonParseStringImpl(str, NULL); + jsonObject* obj2 = jsonObjectDecodeClass(obj); + jsonObjectFree(obj); + return obj2; +} + +jsonObject* jsonParseStringRaw( char* str ) { + if(!str) return NULL; + return _jsonParseStringImpl(str, NULL); +} + +jsonObject* jsonParseStringFmt( char* str, ... ) { + if(!str) return NULL; + VA_LIST_TO_STRING(str); + return _jsonParseStringImpl(VA_BUF, NULL); +} + + +#define JSON_SHOVE_ITEM(ctx,type) \ + jsonInternalParser* p = (jsonInternalParser*) ctx;\ + _jsonInsertParserItem(p, jsonNewObjectType(type)); + +void _jsonHandleStartObject(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_HASH); } +void _jsonHandleStartArray(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_ARRAY); } +void _jsonHandleNull(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_NULL); } + +void _jsonHandleObjectKey(void* ctx, char* key) { + jsonInternalParser* p = (jsonInternalParser*) ctx; + free(p->lastkey); + p->lastkey = strdup(key); +} + +void _jsonHandleEndObject(void* ctx) { + jsonInternalParser* p = (jsonInternalParser*) ctx; + p->current = p->current->parent; +} + +void _jsonHandleEndArray(void* ctx) { + jsonInternalParser* p = (jsonInternalParser*) ctx; + p->current = p->current->parent; +} + +void _jsonHandleString(void* ctx, char* string) { + jsonInternalParser* p = (jsonInternalParser*) ctx; + _jsonInsertParserItem(p, jsonNewObject(string)); +} + +void _jsonHandleBool(void* ctx, int boolval) { + jsonInternalParser* p = (jsonInternalParser*) ctx; + jsonObject* obj = jsonNewObjectType(JSON_BOOL); + obj->value.b = boolval; + _jsonInsertParserItem(p, obj); +} + +void _jsonHandleNumber(void* ctx, double num) { + jsonInternalParser* p = (jsonInternalParser*) ctx; + _jsonInsertParserItem(p, jsonNewNumberObject(num)); +} + +void _jsonHandleError(void* ctx, char* str, ...) { + jsonInternalParser* p = (jsonInternalParser*) ctx; + VA_LIST_TO_STRING(str); + + if( p->handleError ) + p->handleError(VA_BUF); + else + if( jsonClientErrorCallback ) + jsonClientErrorCallback(VA_BUF); + + else fprintf(stderr, "%s\n", VA_BUF); + jsonObjectFree(p->obj); + p->obj = NULL; +} + + +void _jsonInsertParserItem( jsonInternalParser* p, jsonObject* newo ) { + + if( !p->obj ) { + + /* new parser, set the new object to our object */ + p->obj = p->current = newo; + + } else { + + /* insert the new object into the current container object */ + switch(p->current->type) { + case JSON_HASH : jsonObjectSetKey(p->current, p->lastkey, newo); break; + case JSON_ARRAY: jsonObjectPush(p->current, newo); break; + default: fprintf(stderr, "%s:%d -> how?\n", JSON_LOG_MARK); + } + + /* if the new object is a container object, make it our current container */ + if( newo->type == JSON_ARRAY || newo->type == JSON_HASH ) + p->current = newo; + } +} + + diff --git a/src/libopensrf/osrf_json_test.c b/src/libopensrf/osrf_json_test.c new file mode 100644 index 0000000..b3df610 --- /dev/null +++ b/src/libopensrf/osrf_json_test.c @@ -0,0 +1,56 @@ +/* + * Basic JSON test module. Needs more strenous tests.... + * + */ +#include +#include + +static void speedTest(); + + +int main(int argc, char* argv[]) { + /* XXX add support for command line test type specification */ + speedTest(); + return 0; +} + + + +static void speedTest() { + + /* creates a giant json object, generating JSON strings + * of subobjects as it goes. */ + + int i,k; + int count = 50; + char buf[16]; + char* jsonString; + + jsonObject* array; + jsonObject* dupe; + jsonObject* hash = jsonNewObject(NULL); + + for(i = 0; i < count; i++) { + + memset(buf, 0, 16); + snprintf(buf, 16, "key_%d", i); + + array = jsonNewObject(NULL); + for(k = 0; k < count + i; k++) { + jsonObjectPush(array, jsonNewNumberObject(k)); + jsonObjectPush(array, jsonNewObject(NULL)); + jsonObjectPush(array, jsonNewObjectFmt("str %d-%d", i, k)); + } + jsonObjectSetKey(hash, buf, array); + + jsonString = jsonObjectToJSON(hash); + printf("%s\n\n", jsonString); + dupe = jsonParseString(jsonString); + + jsonObjectFree(dupe); + free(jsonString); + } + + jsonObjectFree(hash); +} + diff --git a/src/libopensrf/osrf_json_tools.c b/src/libopensrf/osrf_json_tools.c new file mode 100644 index 0000000..5476bba --- /dev/null +++ b/src/libopensrf/osrf_json_tools.c @@ -0,0 +1,297 @@ +/* +Copyright (C) 2006 Georgia Public Library Service +Bill Erickson + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include + +jsonObject* _jsonObjectEncodeClass( jsonObject* obj, int ignoreClass ); + + +jsonObject* jsonObjectFindPath( const jsonObject* obj, char* path, ...); +jsonObject* _jsonObjectFindPathRecurse(const jsonObject* obj, char* root, char* path); +jsonObject* __jsonObjectFindPathRecurse(const jsonObject* obj, char* root); + + +static char* __tabs(int count) { + growing_buffer* buf = buffer_init(24); + int i; + for(i=0;itype == JSON_HASH ) { + + /* are we a special class object? */ + if( (classObj = jsonObjectGetKey( obj, JSON_CLASS_KEY )) ) { + + /* do we have a payload */ + if( (payloadObj = jsonObjectGetKey( obj, JSON_DATA_KEY )) ) { + newObj = jsonObjectDecodeClass( payloadObj ); + jsonObjectSetClass( newObj, jsonObjectGetString(classObj) ); + + } else { /* class is defined but there is no payload */ + return NULL; + } + + } else { /* we're a regular hash */ + + jsonIterator* itr = jsonNewIterator(obj); + jsonObject* tmp; + newObj = jsonNewObjectType(JSON_HASH); + while( (tmp = jsonIteratorNext(itr)) ) { + jsonObject* o = jsonObjectDecodeClass(tmp); + jsonObjectSetKey( newObj, itr->key, o ); + } + jsonIteratorFree(itr); + } + + } else { + + if( obj->type == JSON_ARRAY ) { /* we're an array */ + newObj = jsonNewObjectType(JSON_ARRAY); + for( i = 0; i != obj->size; i++ ) { + jsonObject* tmp = jsonObjectDecodeClass(jsonObjectGetIndex( obj, i ) ); + jsonObjectSetIndex( newObj, i, tmp ); + } + + } else { /* not an aggregate type */ + newObj = jsonObjectClone(obj); + } + } + + return newObj; +} + +jsonObject* jsonObjectEncodeClass( jsonObject* obj ) { + return _jsonObjectEncodeClass( obj, 0 ); +} + +jsonObject* _jsonObjectEncodeClass( jsonObject* obj, int ignoreClass ) { + + //if(!obj) return NULL; + if(!obj) return jsonNewObject(NULL); + jsonObject* newObj = NULL; + + if( obj->classname && ! ignoreClass ) { + newObj = jsonNewObjectType(JSON_HASH); + + jsonObjectSetKey( newObj, + JSON_CLASS_KEY, jsonNewObject(obj->classname) ); + + jsonObjectSetKey( newObj, + JSON_DATA_KEY, _jsonObjectEncodeClass(obj, 1)); + + } else if( obj->type == JSON_HASH ) { + + jsonIterator* itr = jsonNewIterator(obj); + jsonObject* tmp; + newObj = jsonNewObjectType(JSON_HASH); + + while( (tmp = jsonIteratorNext(itr)) ) { + jsonObjectSetKey( newObj, itr->key, + _jsonObjectEncodeClass(tmp, 0)); + } + jsonIteratorFree(itr); + + } else if( obj->type == JSON_ARRAY ) { + + newObj = jsonNewObjectType(JSON_ARRAY); + int i; + for( i = 0; i != obj->size; i++ ) { + jsonObjectSetIndex( newObj, i, + _jsonObjectEncodeClass(jsonObjectGetIndex( obj, i ), 0 )); + } + + } else { + newObj = jsonObjectClone(obj); + } + + return newObj; +} + +jsonObject* jsonParseFile( char* filename ) { + if(!filename) return NULL; + char* data = file_to_string(filename); + jsonObject* o = jsonParseString(data); + free(data); + return o; +} + + + +jsonObject* jsonObjectFindPath( const jsonObject* obj, char* format, ...) { + if(!obj || !format || strlen(format) < 1) return NULL; + + VA_LIST_TO_STRING(format); + char* buf = VA_BUF; + char* token = NULL; + char* t = buf; + char* tt; /* strtok storage */ + + /* copy the path before strtok_r destroys it */ + char* pathcopy = strdup(buf); + + /* grab the root of the path */ + token = strtok_r(t, "/", &tt); + if(!token) return NULL; + + /* special case where path starts with // (start anywhere) */ + if(strlen(pathcopy) > 2 && pathcopy[0] == '/' && pathcopy[1] == '/') { + jsonObject* it = _jsonObjectFindPathRecurse(obj, token, pathcopy + 1); + free(pathcopy); + return it; + } + + free(pathcopy); + + t = NULL; + do { + obj = jsonObjectGetKey(obj, token); + } while( (token = strtok_r(NULL, "/", &tt)) && obj); + + return jsonObjectClone(obj); +} + +/* --------------------------------------------------------------- */ + + + +jsonObject* _jsonObjectFindPathRecurse(const jsonObject* obj, char* root, char* path) { + + if(!obj || ! root || !path) return NULL; + + /* collect all of the potential objects */ + jsonObject* arr = __jsonObjectFindPathRecurse(obj, root); + + /* container for fully matching objects */ + jsonObject* newarr = jsonParseString("[]"); + int i; + + /* path is just /root or /root/ */ + if( strlen(root) + 2 >= strlen(path) ) { + return arr; + + } else { + + /* gather all of the sub-objects that match the full path */ + for( i = 0; i < arr->size; i++ ) { + jsonObject* a = jsonObjectGetIndex(arr, i); + jsonObject* thing = jsonObjectFindPath(a , path + strlen(root) + 1); + + if(thing) { //jsonObjectPush(newarr, thing); + if(thing->type == JSON_ARRAY) { + int i; + for( i = 0; i != thing->size; i++ ) + jsonObjectPush(newarr, jsonObjectClone(jsonObjectGetIndex(thing,i))); + jsonObjectFree(thing); + + } else { + jsonObjectPush(newarr, thing); + } + } + } + } + + jsonObjectFree(arr); + return newarr; +} + +jsonObject* __jsonObjectFindPathRecurse(const jsonObject* obj, char* root) { + + jsonObject* arr = jsonParseString("[]"); + if(!obj) return arr; + + int i; + + /* if the current object has a node that matches, add it */ + + jsonObject* o = jsonObjectGetKey(obj, root); + if(o) jsonObjectPush( arr, jsonObjectClone(o) ); + + jsonObject* tmp = NULL; + jsonObject* childarr; + jsonIterator* itr = jsonNewIterator(obj); + + /* recurse through the children and find all potential nodes */ + while( (tmp = jsonIteratorNext(itr)) ) { + childarr = __jsonObjectFindPathRecurse(tmp, root); + if(childarr && childarr->size > 0) { + for( i = 0; i!= childarr->size; i++ ) { + jsonObjectPush( arr, jsonObjectClone(jsonObjectGetIndex(childarr, i)) ); + } + } + jsonObjectFree(childarr); + } + + jsonIteratorFree(itr); + + return arr; +} + + + + diff --git a/src/libopensrf/osrf_json_xml.c b/src/libopensrf/osrf_json_xml.c new file mode 100644 index 0000000..283daf4 --- /dev/null +++ b/src/libopensrf/osrf_json_xml.c @@ -0,0 +1,365 @@ +#include + +#ifdef OSRF_JSON_ENABLE_XML_UTILS + +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; +} + + + + + + +static char* _escape_xml (char*); +static int _recurse_jsonObjectToXML(jsonObject*, growing_buffer*); + +char* jsonObjectToXML(jsonObject* obj) { + + growing_buffer * res_xml; + char * output; + + res_xml = buffer_init(1024); + + if (!obj) + return strdup(""); + + _recurse_jsonObjectToXML( obj, res_xml ); + output = buffer_data(res_xml); + + buffer_free(res_xml); + + return output; + +} + +int _recurse_jsonObjectToXML(jsonObject* obj, growing_buffer* res_xml) { + + char * hint = NULL; + char * bool_val = NULL; + int i = 0; + + if (obj->classname) + hint = strdup(obj->classname); + + if(obj->type == JSON_NULL) { + + if (hint) + buffer_fadd(res_xml, "",hint); + else + buffer_add(res_xml, ""); + + } else if(obj->type == JSON_BOOL) { + + if (obj->value.b) + bool_val = strdup("true"); + else + bool_val = strdup("false"); + + if (hint) + buffer_fadd(res_xml, "", bool_val, hint); + else + buffer_fadd(res_xml, "", bool_val); + + free(bool_val); + + } else if (obj->type == JSON_STRING) { + if (hint) { + char * t = _escape_xml(jsonObjectGetString(obj)); + buffer_fadd(res_xml,"%s", hint, t); + free(t); + } else { + char * t = _escape_xml(jsonObjectGetString(obj)); + buffer_fadd(res_xml,"%s", t); + free(t); + } + + } else if(obj->type == JSON_NUMBER) { + double x = jsonObjectGetNumber(obj); + if (hint) { + if (x == (int)x) + buffer_fadd(res_xml,"%d", hint, (int)x); + else + buffer_fadd(res_xml,"%lf", hint, x); + } else { + if (x == (int)x) + buffer_fadd(res_xml,"%d", (int)x); + else + buffer_fadd(res_xml,"%lf", x); + } + + } else if (obj->type == JSON_ARRAY) { + + if (hint) + buffer_fadd(res_xml,"", hint); + else + buffer_add(res_xml,""); + + for ( i = 0; i!= obj->size; i++ ) + _recurse_jsonObjectToXML(jsonObjectGetIndex(obj,i), res_xml); + + buffer_add(res_xml,""); + + } else if (obj->type == JSON_HASH) { + + if (hint) + buffer_fadd(res_xml,"", hint); + else + buffer_add(res_xml,""); + + jsonIterator* itr = jsonNewIterator(obj); + jsonObject* tmp; + while( (tmp = jsonIteratorNext(itr)) ) { + buffer_fadd(res_xml,"",itr->key); + _recurse_jsonObjectToXML(tmp, res_xml); + buffer_add(res_xml,""); + } + jsonIteratorFree(itr); + + buffer_add(res_xml,""); + } + + if (hint) + free(hint); + + return 1; +} + +char* _escape_xml (char* text) { + char* out; + growing_buffer* b = buffer_init(256); + int len = strlen(text); + int i; + for (i = 0; i < len; i++) { + if (text[i] == '&') + buffer_add(b,"&"); + else if (text[i] == '<') + buffer_add(b,"<"); + else if (text[i] == '>') + buffer_add(b,">"); + else + buffer_add_char(b,text[i]); + } + out = buffer_data(b); + buffer_free(b); + return out; +} + +#endif diff --git a/src/libopensrf/osrf_legacy_json.c b/src/libopensrf/osrf_legacy_json.c new file mode 100644 index 0000000..69b9867 --- /dev/null +++ b/src/libopensrf/osrf_legacy_json.c @@ -0,0 +1,880 @@ +/* +Copyright (C) 2006 Georgia Public Library Service +Bill Erickson + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + + +#include + +/* keep a copy of the length of the current json string so we don't + * have to calculate it in each function + */ +int current_strlen; + + +jsonObject* legacy_jsonParseString( char* string) { + return json_parse_string( string ); +} + +jsonObject* legacy_jsonParseStringFmt( char* string, ... ) { + VA_LIST_TO_STRING(string); + return json_parse_string( VA_BUF ); +} + + +jsonObject* json_parse_string(char* string) { + + if(string == NULL) return NULL; + + current_strlen = strlen(string); + + if(current_strlen == 0) + return NULL; + + unsigned long index = 0; + + json_eat_ws(string, &index, 1, current_strlen); /* remove leading whitespace */ + if(index == current_strlen) return NULL; + + jsonObject* obj = jsonNewObject(NULL); + + int status = _json_parse_string(string, &index, obj, current_strlen); + if(!status) return obj; + + if(status == -2) { + jsonObjectFree(obj); + return NULL; + } + + return NULL; +} + + +int _json_parse_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen) { + if( !string || !index || *index >= current_strlen) return -2; + + int status = 0; /* return code from parsing routines */ + char* classname = NULL; /* object class hint */ + json_eat_ws(string, index, 1, current_strlen); /* remove leading whitespace */ + + char c = string[*index]; + + /* remove any leading comments */ + if( c == '/' ) { + + while(1) { + (*index)++; /* move to second comment char */ + status = json_eat_comment(string, index, &classname, 1, current_strlen); + if(status) return status; + + json_eat_ws(string, index, 1, current_strlen); + c = string[*index]; + if(c != '/') + break; + } + } + + json_eat_ws(string, index, 1, current_strlen); /* remove leading whitespace */ + + if(*index >= current_strlen) + return -2; + + switch(c) { + + /* json string */ + case '"': + (*index)++; + status = json_parse_json_string(string, index, obj, current_strlen); break; + + /* json array */ + case '[': + (*index)++; + status = json_parse_json_array(string, index, obj, current_strlen); + break; + + /* json object */ + case '{': + (*index)++; + status = json_parse_json_object(string, index, obj, current_strlen); + break; + + /* NULL */ + case 'n': + case 'N': + status = json_parse_json_null(string, index, obj, current_strlen); + break; + + + /* true, false */ + case 'f': + case 'F': + case 't': + case 'T': + status = json_parse_json_bool(string, index, obj, current_strlen); + break; + + default: + if(isdigit(c) || c == '.' || c == '-') { /* are we a number? */ + status = json_parse_json_number(string, index, obj, current_strlen); + if(status) return status; + break; + } + + (*index)--; + /* we should never get here */ + return json_handle_error(string, index, "_json_parse_string() final switch clause"); + } + + if(status) return status; + + json_eat_ws(string, index, 1, current_strlen); + + if( *index < current_strlen ) { + /* remove any trailing comments */ + c = string[*index]; + if( c == '/' ) { + (*index)++; + status = json_eat_comment(string, index, NULL, 0, current_strlen); + if(status) return status; + } + } + + if(classname){ + jsonObjectSetClass(obj, classname); + free(classname); + } + + return 0; +} + + +int json_parse_json_null(char* string, unsigned long* index, jsonObject* obj, int current_strlen) { + + if(*index >= (current_strlen - 3)) { + return json_handle_error(string, index, + "_parse_json_null(): invalid null" ); + } + + if(!strncasecmp(string + (*index), "null", 4)) { + (*index) += 4; + obj->type = JSON_NULL; + return 0; + } else { + return json_handle_error(string, index, + "_parse_json_null(): invalid null" ); + } +} + +/* should be at the first character of the bool at this point */ +int json_parse_json_bool(char* string, unsigned long* index, jsonObject* obj, int current_strlen) { + if( ! string || ! obj || *index >= current_strlen ) return -1; + + char* ret = "json_parse_json_bool(): truncated bool"; + + if( *index >= (current_strlen - 5)) + return json_handle_error(string, index, ret); + + if(!strncasecmp( string + (*index), "false", 5)) { + (*index) += 5; + obj->value.b = 0; + obj->type = JSON_BOOL; + return 0; + } + + if( *index >= (current_strlen - 4)) + return json_handle_error(string, index, ret); + + if(!strncasecmp( string + (*index), "true", 4)) { + (*index) += 4; + obj->value.b = 1; + obj->type = JSON_BOOL; + return 0; + } + + return json_handle_error(string, index, ret); +} + + +/* expecting the first character of the number */ +int json_parse_json_number(char* string, unsigned long* index, jsonObject* obj, int current_strlen) { + if( ! string || ! obj || *index >= current_strlen ) return -1; + + growing_buffer* buf = buffer_init(64); + char c = string[*index]; + + int done = 0; + int dot_seen = 0; + + /* negative number? */ + if(c == '-') { buffer_add(buf, "-"); (*index)++; } + + c = string[*index]; + + while(*index < current_strlen) { + + if(isdigit(c)) { + buffer_add_char(buf, c); + } + + else if( c == '.' ) { + if(dot_seen) { + buffer_free(buf); + return json_handle_error(string, index, + "json_parse_json_number(): malformed json number"); + } + dot_seen = 1; + buffer_add_char(buf, c); + } else { + done = 1; break; + } + + (*index)++; + c = string[*index]; + if(done) break; + } + + obj->type = JSON_NUMBER; + obj->value.n = strtod(buf->buf, NULL); + buffer_free(buf); + return 0; +} + +/* index should point to the character directly following the '['. when done + * index will point to the character directly following the ']' character + */ +int json_parse_json_array(char* string, unsigned long* index, jsonObject* obj, int current_strlen) { + + if( ! string || ! obj || ! index || *index >= current_strlen ) return -1; + + int status = 0; + int in_parse = 0; /* true if this array already contains one item */ + obj->type = JSON_ARRAY; + int set = 0; + int done = 0; + + while(*index < current_strlen) { + + json_eat_ws(string, index, 1, current_strlen); + + if(string[*index] == ']') { + (*index)++; + done = 1; + break; + } + + if(in_parse) { + json_eat_ws(string, index, 1, current_strlen); + if(string[*index] != ',') { + return json_handle_error(string, index, + "json_parse_json_array(): array item not followed by a ','"); + } + (*index)++; + json_eat_ws(string, index, 1, current_strlen); + } + + jsonObject* item = jsonNewObject(NULL); + + #ifndef STRICT_JSON_READ + if(*index < current_strlen) { + if(string[*index] == ',' || string[*index] == ']') { + status = 0; + set = 1; + } + } + if(!set) status = _json_parse_string(string, index, item, current_strlen); + + #else + status = _json_parse_string(string, index, item, current_strlen); + #endif + + if(status) { jsonObjectFree(item); return status; } + jsonObjectPush(obj, item); + in_parse = 1; + set = 0; + } + + if(!done) + return json_handle_error(string, index, + "json_parse_json_array(): array not closed"); + + return 0; +} + + +/* index should point to the character directly following the '{'. when done + * index will point to the character directly following the '}' + */ +int json_parse_json_object(char* string, unsigned long* index, jsonObject* obj, int current_strlen) { + if( ! string || !obj || ! index || *index >= current_strlen ) return -1; + + obj->type = JSON_HASH; + int status; + int in_parse = 0; /* true if we've already added one item to this object */ + int set = 0; + int done = 0; + + while(*index < current_strlen) { + + json_eat_ws(string, index, 1, current_strlen); + + if(string[*index] == '}') { + (*index)++; + done = 1; + break; + } + + if(in_parse) { + if(string[*index] != ',') { + return json_handle_error(string, index, + "json_parse_json_object(): object missing ',' between elements" ); + } + (*index)++; + json_eat_ws(string, index, 1, current_strlen); + } + + /* first we grab the hash key */ + jsonObject* key_obj = jsonNewObject(NULL); + status = _json_parse_string(string, index, key_obj, current_strlen); + if(status) return status; + + if(key_obj->type != JSON_STRING) { + return json_handle_error(string, index, + "_json_parse_json_object(): hash key not a string"); + } + + char* key = key_obj->value.s; + + json_eat_ws(string, index, 1, current_strlen); + + if(string[*index] != ':') { + return json_handle_error(string, index, + "json_parse_json_object(): hash key not followed by ':' character"); + } + + (*index)++; + + /* now grab the value object */ + json_eat_ws(string, index, 1, current_strlen); + jsonObject* value_obj = jsonNewObject(NULL); + +#ifndef STRICT_JSON_READ + if(*index < current_strlen) { + if(string[*index] == ',' || string[*index] == '}') { + status = 0; + set = 1; + } + } + if(!set) + status = _json_parse_string(string, index, value_obj, current_strlen); + +#else + status = _json_parse_string(string, index, value_obj, current_strlen); +#endif + + if(status) return status; + + /* put the data into the object and continue */ + jsonObjectSetKey(obj, key, value_obj); + jsonObjectFree(key_obj); + in_parse = 1; + set = 0; + } + + if(!done) + return json_handle_error(string, index, + "json_parse_json_object(): object not closed"); + + return 0; +} + + + +/* when done, index will point to the character after the closing quote */ +int json_parse_json_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen) { + if( ! string || ! index || *index >= current_strlen ) return -1; + + int in_escape = 0; + int done = 0; + growing_buffer* buf = buffer_init(64); + + while(*index < current_strlen) { + + char c = string[*index]; + + switch(c) { + + case '\\': + if(in_escape) { + buffer_add(buf, "\\"); + in_escape = 0; + } else + in_escape = 1; + break; + + case '"': + if(in_escape) { + buffer_add(buf, "\""); + in_escape = 0; + } else + done = 1; + break; + + case 't': + if(in_escape) { + buffer_add(buf,"\t"); + in_escape = 0; + } else + buffer_add_char(buf, c); + break; + + case 'b': + if(in_escape) { + buffer_add(buf,"\b"); + in_escape = 0; + } else + buffer_add_char(buf, c); + break; + + case 'f': + if(in_escape) { + buffer_add(buf,"\f"); + in_escape = 0; + } else + buffer_add_char(buf, c); + break; + + case 'r': + if(in_escape) { + buffer_add(buf,"\r"); + in_escape = 0; + } else + buffer_add_char(buf, c); + break; + + case 'n': + if(in_escape) { + buffer_add(buf,"\n"); + in_escape = 0; + } else + buffer_add_char(buf, c); + break; + + case 'u': + if(in_escape) { + (*index)++; + + if(*index >= (current_strlen - 4)) { + buffer_free(buf); + return json_handle_error(string, index, + "json_parse_json_string(): truncated escaped unicode"); } + + char buff[5]; + memset(buff,0,5); + memcpy(buff, string + (*index), 4); + + + /* ----------------------------------------------------------------------- */ + /* ----------------------------------------------------------------------- */ + /* The following chunk was borrowed with permission from + json-c http://oss.metaparadigm.com/json-c/ */ + unsigned char utf_out[3]; + memset(utf_out,0,3); + + #define hexdigit(x) ( ((x) <= '9') ? (x) - '0' : ((x) & 7) + 9) + + unsigned int ucs_char = + (hexdigit(string[*index] ) << 12) + + (hexdigit(string[*index + 1]) << 8) + + (hexdigit(string[*index + 2]) << 4) + + hexdigit(string[*index + 3]); + + if (ucs_char < 0x80) { + utf_out[0] = ucs_char; + buffer_add(buf, (char*) utf_out); + + } else if (ucs_char < 0x800) { + utf_out[0] = 0xc0 | (ucs_char >> 6); + utf_out[1] = 0x80 | (ucs_char & 0x3f); + buffer_add(buf, (char*) utf_out); + + } else { + utf_out[0] = 0xe0 | (ucs_char >> 12); + utf_out[1] = 0x80 | ((ucs_char >> 6) & 0x3f); + utf_out[2] = 0x80 | (ucs_char & 0x3f); + buffer_add(buf, (char*) utf_out); + } + /* ----------------------------------------------------------------------- */ + /* ----------------------------------------------------------------------- */ + + (*index) += 3; + in_escape = 0; + + } else { + + buffer_add_char(buf, c); + } + + break; + + default: + buffer_add_char(buf, c); + } + + (*index)++; + if(done) break; + } + + jsonObjectSetString(obj, buf->buf); + buffer_free(buf); + return 0; +} + + +void json_eat_ws(char* string, unsigned long* index, int eat_all, int current_strlen) { + if( ! string || ! index ) return; + if(*index >= current_strlen) + return; + + if( eat_all ) { /* removes newlines, etc */ + while(string[*index] == ' ' || + string[*index] == '\n' || + string[*index] == '\t') + (*index)++; + } + + else + while(string[*index] == ' ') (*index)++; +} + + +/* index should be at the '*' character at the beginning of the comment. + * when done, index will point to the first character after the final / + */ +int json_eat_comment(char* string, unsigned long* index, char** buffer, int parse_class, int current_strlen) { + if( ! string || ! index || *index >= current_strlen ) return -1; + + + if(string[*index] != '*' && string[*index] != '/' ) + return json_handle_error(string, index, + "json_eat_comment(): invalid character after /"); + + /* chop out any // style comments */ + if(string[*index] == '/') { + (*index)++; + char c = string[*index]; + while(*index < current_strlen) { + (*index)++; + if(c == '\n') + return 0; + c = string[*index]; + } + return 0; + } + + (*index)++; + + int on_star = 0; /* true if we just saw a '*' character */ + + /* we're just past the '*' */ + if(!parse_class) { /* we're not concerned with class hints */ + while(*index < current_strlen) { + if(string[*index] == '/') { + if(on_star) { + (*index)++; + return 0; + } + } + + if(string[*index] == '*') on_star = 1; + else on_star = 0; + + (*index)++; + } + return 0; + } + + + + growing_buffer* buf = buffer_init(64); + + int first_dash = 0; + int second_dash = 0; + int third_dash = 0; + int fourth_dash = 0; + + int in_hint = 0; + int done = 0; + + /*--S hint--*/ /* <-- Hints look like this */ + /*--E hint--*/ + + while(*index < current_strlen) { + char c = string[*index]; + + switch(c) { + + case '-': + on_star = 0; + if(third_dash) fourth_dash = 1; + else if(in_hint) third_dash = 1; + else if(first_dash) second_dash = 1; + else first_dash = 1; + break; + + case 'S': + on_star = 0; + if(second_dash && !in_hint) { + (*index)++; + json_eat_ws(string, index, 1, current_strlen); + (*index)--; /* this will get incremented at the bottom of the loop */ + in_hint = 1; + break; + } + + if(second_dash && in_hint) { + buffer_add_char(buf, c); + break; + } + + case 'E': + on_star = 0; + if(second_dash && !in_hint) { + (*index)++; + json_eat_ws(string, index, 1, current_strlen); + (*index)--; /* this will get incremented at the bottom of the loop */ + in_hint = 1; + break; + } + + if(second_dash && in_hint) { + buffer_add_char(buf, c); + break; + } + + case '*': + on_star = 1; + break; + + case '/': + if(on_star) + done = 1; + else + on_star = 0; + break; + + default: + on_star = 0; + if(in_hint) + buffer_add_char(buf, c); + } + + (*index)++; + if(done) break; + } + + if( buf->n_used > 0 && buffer) + *buffer = buffer_data(buf); + + buffer_free(buf); + return 0; +} + +int json_handle_error(char* string, unsigned long* index, char* err_msg) { + + char buf[60]; + memset(buf, 0, 60); + + if(*index > 30) + strncpy( buf, string + (*index - 30), 59 ); + else + strncpy( buf, string, 59 ); + + fprintf(stderr, + "\nError parsing json string at charracter %c " + "(code %d) and index %ld\nString length: %d\nMsg:\t%s\nNear:\t%s\nFull String:\t%s\n", + string[*index], string[*index], *index, current_strlen, err_msg, buf, string ); + + return -1; +} + + +jsonObject* legacy_jsonParseFile( const char* filename ) { + return json_parse_file( filename ); +} + +jsonObject* json_parse_file(const char* filename) { + if(!filename) return NULL; + char* data = file_to_string(filename); + jsonObject* o = json_parse_string(data); + free(data); + return o; +} + + + +char* legacy_jsonObjectToJSON( const jsonObject* obj ) { + + if(obj == NULL) return strdup("null"); + + growing_buffer* buf = buffer_init(64); + + /* add class hints if we have a class name */ + if(obj->classname) { + buffer_add(buf,"/*--S "); + buffer_add(buf,obj->classname); + buffer_add(buf, "--*/"); + } + + switch( obj->type ) { + + case JSON_BOOL: + if(obj->value.b) buffer_add(buf, "true"); + else buffer_add(buf, "false"); + break; + + case JSON_NUMBER: { + double x = obj->value.n; + + /* if the number does not need to be a double, + turn it into an int on the way out */ + if( x == (int) x ) { + INT_TO_STRING((int)x); + buffer_add(buf, INTSTR); + + } else { + DOUBLE_TO_STRING(x); + buffer_add(buf, DOUBLESTR); + } + break; + } + + case JSON_NULL: + buffer_add(buf, "null"); + break; + + case JSON_STRING: + buffer_add(buf, "\""); + char* data = obj->value.s; + int len = strlen(data); + + char* output = uescape(data, len, 1); + buffer_add(buf, output); + free(output); + buffer_add(buf, "\""); + break; + + case JSON_ARRAY: + buffer_add(buf, "["); + int i; + for( i = 0; i!= obj->size; i++ ) { + const jsonObject* x = jsonObjectGetIndex(obj,i); + char* data = legacy_jsonObjectToJSON(x); + buffer_add(buf, data); + free(data); + if(i != obj->size - 1) + buffer_add(buf, ","); + } + buffer_add(buf, "]"); + break; + + case JSON_HASH: + + buffer_add(buf, "{"); + jsonIterator* itr = jsonNewIterator(obj); + jsonObject* tmp; + + while( (tmp = jsonIteratorNext(itr)) ) { + + buffer_add(buf, "\""); + + char* key = itr->key; + int len = strlen(key); + char* output = uescape(key, len, 1); + buffer_add(buf, output); + free(output); + + buffer_add(buf, "\":"); + char* data = legacy_jsonObjectToJSON(tmp); + buffer_add(buf, data); + if(jsonIteratorHasNext(itr)) + buffer_add(buf, ","); + free(data); + } + + jsonIteratorFree(itr); + buffer_add(buf, "}"); + break; + + default: + fprintf(stderr, "Unknown object type %d\n", obj->type); + break; + + } + + /* close out the object hint */ + if(obj->classname) { + buffer_add(buf, "/*--E "); + buffer_add(buf, obj->classname); + buffer_add(buf, "--*/"); + } + + char* data = buffer_data(buf); + buffer_free(buf); + return data; +} + + + +static jsonObjectNode* makeNode(jsonObject* obj, unsigned long index, char* key) { + jsonObjectNode* node = safe_malloc(sizeof(jsonObjectNode)); + node->item = obj; + node->index = index; + node->key = key; + return node; +} + +jsonObjectIterator* jsonNewObjectIterator(const jsonObject* obj) { + if(!obj) return NULL; + jsonObjectIterator* itr = safe_malloc(sizeof(jsonObjectIterator)); + itr->iterator = jsonNewIterator(obj); + itr->obj = obj; + itr->done = 0; + itr->current = NULL; + return itr; +} + +jsonObjectNode* jsonObjectIteratorNext( jsonObjectIterator* itr ) { + if(itr == NULL || itr->done) return NULL; + + if(itr->current) free(itr->current); + jsonObject* next = jsonIteratorNext(itr->iterator); + if(next == NULL) { + itr->current = NULL; + itr->done = 1; + return NULL; + } + itr->current = makeNode(next, itr->iterator->index, itr->iterator->key); + return itr->current; +} + +void jsonObjectIteratorFree(jsonObjectIterator* iter) { + if(iter->current) free(iter->current); + jsonIteratorFree(iter->iterator); + free(iter); +} + +int jsonObjectIteratorHasNext(const jsonObjectIterator* itr) { + return (itr && itr->current); +} + + diff --git a/src/libopensrf/string_array.c b/src/libopensrf/string_array.c index 3197718..ca8393f 100644 --- a/src/libopensrf/string_array.c +++ b/src/libopensrf/string_array.c @@ -8,18 +8,11 @@ string_array* init_string_array(int size) { if(size > STRING_ARRAY_MAX_SIZE) osrfLogError( OSRF_LOG_MARK, "init_string_array size is too large"); - /* - string_array* arr = - (string_array*) safe_malloc(sizeof(string_array)); - */ string_array* arr; OSRF_MALLOC( arr, sizeof(string_array)); - - //arr->array = (char**) safe_malloc(size * sizeof(char*)); - OSRF_MALLOC(arr->array, size * sizeof(char*)); - + arr->list = osrfNewListSize(size); + osrfListSetDefaultFree(arr->list); arr->size = 0; - arr->arr_size = size; return arr; } @@ -30,65 +23,39 @@ void osrfStringArrayAdd(osrfStringArray* arr, char* string) { void string_array_add(string_array* arr, char* str) { if(arr == NULL || str == NULL ) return; - if( strlen(str) < 1 ) return; - - arr->size++; - if( arr->size > STRING_ARRAY_MAX_SIZE ) osrfLogError( OSRF_LOG_MARK, "string_array_add size is too large"); - - /* if necessary, double capacity */ - if(arr->size >= arr->arr_size) { - arr->arr_size *= 2; - //char** tmp = (char**) safe_malloc(arr->arr_size * sizeof(char*)); - char** tmp; - OSRF_MALLOC( tmp, arr->arr_size * sizeof(char*)); - int i; - - /* copy the string pointers over */ - for( i = 0; i!= arr->size; i++ ) - tmp[i] = arr->array[i]; - - free(arr->array); - arr->array = tmp; - } - - arr->array[arr->size - 1] = strdup(str); + osrfListPush(arr->list, strdup(str)); + arr->size = arr->list->size; } char* osrfStringArrayGetString(osrfStringArray* arr, int index) { - return string_array_get_string(arr, index); + if(!arr) return NULL; + return OSRF_LIST_GET_INDEX(arr->list, index); } char* string_array_get_string(string_array* arr, int index) { - if(!arr || index < 0 || index >= arr->size ) return NULL; - return arr->array[index]; + if(!arr) return NULL; + return OSRF_LIST_GET_INDEX(arr->list, index); } void osrfStringArrayFree(osrfStringArray* arr) { - string_array_destroy(arr); + OSRF_STRING_ARRAY_FREE(arr); } void string_array_destroy(string_array* arr) { - if(arr) { - int i = 0; - while( i < arr->size ) free(arr->array[i++]); - free(arr->array); - free(arr); - } + OSRF_STRING_ARRAY_FREE(arr); } int osrfStringArrayContains( osrfStringArray* arr, char* string ) { if(!(arr && string)) return 0; - int i; - for( i = 0; i != arr->size; i++ ) { - char* str = osrfStringArrayGetString(arr, i); - if(str) { - if(!strcmp(str, string)) return 1; - } + for( i = 0; i < arr->size; i++ ) { + char* str = OSRF_LIST_GET_INDEX(arr->list, i); + if(str && !strcmp(str, string)) + return 1; } return 0; @@ -97,19 +64,29 @@ int osrfStringArrayContains( osrfStringArray* arr, char* string ) { void osrfStringArrayRemove( osrfStringArray* arr, char* tstr) { if(!(arr && tstr)) return; int i; - for( i = 0; i != arr->size; i++ ) { - char* str = osrfStringArrayGetString(arr, i); - if(str) { - if(!strcmp(str, tstr)) { - free(arr->array[i]); - arr->array[i] = NULL; - break; - } + char* str; + + for( i = 0; i < arr->size; i++ ) { + /* find and remove the string */ + str = OSRF_LIST_GET_INDEX(arr->list, i); + if(str && !strcmp(str, tstr)) { + osrfListRemove(arr->list, i); + break; } } - for( ; i != arr->size; i++ ) - arr->array[i] = arr->array[i+1]; + /* disable automatic item freeing on delete and shift + * items up in the array to fill in the gap + */ + arr->list->freeItem = NULL; + for( ; i < arr->size - 1; i++ ) + osrfListSet(arr->list, OSRF_LIST_GET_INDEX(arr->list, i+1) , i); + + /* remove the last item since it was shifted up */ + osrfListRemove(arr->list, i); + + /* re-enable automatic item freeing in delete */ + osrfListSetDefaultFree(arr->list); arr->size--; } diff --git a/src/perlmods/OpenSRF/Utils/JSON.pm b/src/perlmods/OpenSRF/Utils/JSON.pm index b417425..c78ec48 100644 --- a/src/perlmods/OpenSRF/Utils/JSON.pm +++ b/src/perlmods/OpenSRF/Utils/JSON.pm @@ -85,7 +85,79 @@ sub _json_hint_to_class { return $hint; } + +my $JSON_CLASS_KEY = '__c'; +my $JSON_PAYLOAD_KEY = '__p'; + sub JSON2perl { + my( $class, $string ) = @_; + my $perl = $class->rawJSON2perl($string); + return $class->JSONObject2Perl($perl); +} + +sub perl2JSON { + my( $class, $obj ) = @_; + my $json = $class->perl2JSONObject($obj); + return $class->rawPerl2JSON($json); +} + +sub JSONObject2Perl { + my $class = shift; + my $obj = shift; + my $ref = ref($obj); + if( $ref eq 'HASH' ) { + if( defined($obj->{$JSON_CLASS_KEY})) { + my $cls = $obj->{$JSON_CLASS_KEY}; + $cls =~ s/^\s+//o; + $cls =~ s/\s+$//o; + if( $obj = $class->JSONObject2Perl($obj->{$JSON_PAYLOAD_KEY}) ) { + $cls = $class->lookup_class($cls) || $cls; + return bless(\$obj, $cls) unless ref($obj); + return bless($obj, $cls); + } + return undef; + } + $obj->{$_} = $class->JSONObject2Perl($obj->{$_}) for (keys %$obj); + } elsif( $ref eq 'ARRAY' ) { + $obj->[$_] = $class->JSONObject2Perl($obj->[$_]) for(0..scalar(@$obj) - 1); + } + return $obj; +} + +sub perl2JSONObject { + my $class = shift; + my $obj = shift; + my $ref = ref($obj); + + return $obj unless $ref; + my $newobj; + + if( $ref eq 'HASH' ) { + $newobj = {}; + $newobj->{$_} = $class->perl2JSONObject( $obj->{$_} ) for (keys %$obj); + } elsif( $ref eq 'ARRAY' ) { + $newobj = []; + $newobj->[$_] = $class->perl2JSONObject( $obj->[$_] ) for(0..scalar(@$obj) - 1 ); + } elsif( $ref ) { + if(UNIVERSAL::isa($obj, 'HASH')) { + $newobj = {}; + $newobj->{$_} = $class->perl2JSONObject( $obj->{$_} ) for (keys %$obj); + bless( $newobj, ref($obj) ); + #bless($obj, 'HASH'); # so our parser won't add the hints + } elsif(UNIVERSAL::isa($obj, 'ARRAY')) { + $newobj = []; + $newobj->[$_] = $class->perl2JSONObject( $obj->[$_] ) for(0..scalar(@$obj) - 1); + bless( $newobj, ref($obj) ); + #bless($obj, 'ARRAY'); # so our parser won't add the hints + } + $ref = $class->lookup_hint($ref) || $ref; + $newobj = { $JSON_CLASS_KEY => $ref, $JSON_PAYLOAD_KEY => $newobj }; + } + return $newobj; +} + + +sub rawJSON2perl { my $class = shift; local $_ = shift; @@ -97,8 +169,8 @@ sub JSON2perl { s/\\u([0-9a-fA-F]{4})/chr(hex($1))/esog; # handle class blessings - s/\/\*--\s*S\w*?\s+\S+\s*--\*\// bless(/sog; - s/(\]|\}|")\s*\/\*--\s*E\w*?\s+(\S+)\s*--\*\//$1 => _json_hint_to_class("$1", "$2")) /sog; +# s/\/\*--\s*S\w*?\s+\S+\s*--\*\// bless(/sog; +# s/(\]|\}|")\s*\/\*--\s*E\w*?\s+(\S+)\s*--\*\//$1 => _json_hint_to_class("$1", "$2")) /sog; my $re = qr/((?(?<=\\)"|[^"])*(? /sog; # Do numbers... - #s/\b(-?\d+\.?\d*)\b/ OpenSRF::Utils::JSON::number::new($1) /sog; + #s/\b(-?\d+\.?\d*)\b/ JSON::number::new($1) /sog; # Change javascript stuff to perl... s/null/ undef /sog; - s/true/ bless( {}, "OpenSRF::Utils::JSON::bool::true") /sog; - s/false/ bless( {}, "OpenSRF::Utils::JSON::bool::false") /sog; + s/true/ bless( {}, "JSON::bool::true") /sog; + s/false/ bless( {}, "JSON::bool::false") /sog; my $ret; return eval '$ret = '.$_; } + my $_json_index; sub ___JSON2perl { my $class = shift; @@ -638,7 +711,7 @@ sub old_JSON2perl { } elsif ($element =~ /^\/\*/) { next; } elsif ($element =~ /^\d/) { - $output .= "do { OpenSRF::Utils::JSON::number::new($element) }"; + $output .= "do { JSON::number::new($element) }"; next; } elsif ($element eq '{' or $element eq '[') { $casting_depth++; @@ -654,10 +727,10 @@ sub old_JSON2perl { $output .= ' => '; next; } elsif ($element eq 'true') { - $output .= 'bless( {}, "OpenSRF::Utils::JSON::bool::true")'; + $output .= 'bless( {}, "JSON::bool::true")'; next; } elsif ($element eq 'false') { - $output .= 'bless( {}, "OpenSRF::Utils::JSON::bool::false")'; + $output .= 'bless( {}, "JSON::bool::false")'; next; } @@ -667,26 +740,28 @@ sub old_JSON2perl { return eval $output; } -sub perl2JSON { + +sub rawPerl2JSON { my ($class, $perl, $strict) = @_; my $output = ''; if (!defined($perl)) { $output = '' if $strict; $output = 'null' unless $strict; - } elsif (ref($perl) and ref($perl) =~ /^OpenSRF::Utils::JSON/) { + } elsif (ref($perl) and ref($perl) =~ /^JSON/) { $output .= $perl; - } elsif ( ref($perl) && exists($_class_map{classes}{ref($perl)}) ) { - $output .= '/*--S '.$_class_map{classes}{ref($perl)}{hint}.'--*/'; - if (lc($_class_map{classes}{ref($perl)}{type}) eq 'hash') { - my %hash = %$perl; - $output .= perl2JSON(undef,\%hash, $strict); - } elsif (lc($_class_map{classes}{ref($perl)}{type}) eq 'array') { - my @array = @$perl; - $output .= perl2JSON(undef,\@array, $strict); - } - $output .= '/*--E '.$_class_map{classes}{ref($perl)}{hint}.'--*/'; - } elsif (ref($perl) and ref($perl) =~ /HASH/) { +# } elsif ( ref($perl) && exists($_class_map{classes}{ref($perl)}) ) { +# $output .= '/*--S '.$_class_map{classes}{ref($perl)}{hint}.'--*/'; +# if (lc($_class_map{classes}{ref($perl)}{type}) eq 'hash') { +# my %hash = %$perl; +# $output .= rawPerl2JSON(undef,\%hash, $strict); +# } elsif (lc($_class_map{classes}{ref($perl)}{type}) eq 'array') { +# my @array = @$perl; +# $output .= rawPerl2JSON(undef,\@array, $strict); +# } +# $output .= '/*--E '.$_class_map{classes}{ref($perl)}{hint}.'--*/'; +# } elsif (ref($perl) and ref($perl) =~ /HASH/) { + } elsif (UNIVERSAL::isa($perl, 'HASH')) { $output .= '{'; my $c = 0; for my $key (sort keys %$perl) { @@ -701,27 +776,28 @@ sub perl2JSON { $outkey =~ s/\n/\\n/sgo; $outkey =~ s/([\x{0080}-\x{fffd}])/sprintf('\u%0.4x',ord($1))/sgoe; - $output .= '"'.$outkey.'":'. perl2JSON(undef,$$perl{$key}, $strict); + $output .= '"'.$outkey.'":'. rawPerl2JSON(undef,$$perl{$key}, $strict); $c++; } $output .= '}'; - } elsif (ref($perl) and ref($perl) =~ /ARRAY/) { +# } elsif (ref($perl) and ref($perl) =~ /ARRAY/) { + } elsif (UNIVERSAL::isa($perl, 'ARRAY')) { $output .= '['; my $c = 0; for my $part (@$perl) { $output .= ',' if ($c); - $output .= perl2JSON(undef,$part, $strict); + $output .= rawPerl2JSON(undef,$part, $strict); $c++; } $output .= ']'; } elsif (ref($perl) and ref($perl) =~ /CODE/) { - $output .= perl2JSON(undef,$perl->(), $strict); + $output .= rawPerl2JSON(undef,$perl->(), $strict); } elsif (ref($perl) and ("$perl" =~ /^([^=]+)=(\w+)/o)) { my $type = $2; my $name = $1; OpenSRF::Utils::JSON->register_class_hint(name => $name, hint => $name, type => lc($type)); - $output .= perl2JSON(undef,$perl, $strict); + $output .= rawPerl2JSON(undef,$perl, $strict); } else { $perl = NFC($perl); $perl =~ s{\\}{\\\\}sgo; @@ -750,7 +826,7 @@ sub perl2prettyJSON { if (!defined($perl)) { $output = " "x$depth unless($nospace); $output .= 'null'; - } elsif (ref($perl) and ref($perl) =~ /^OpenSRF::Utils::JSON/) { + } elsif (ref($perl) and ref($perl) =~ /^JSON/) { $output = " "x$depth unless($nospace); $output .= $perl; } elsif ( ref($perl) && exists($_class_map{classes}{ref($perl)}) ) { diff --git a/src/router/Makefile b/src/router/Makefile index e156743..f545b66 100644 --- a/src/router/Makefile +++ b/src/router/Makefile @@ -1,6 +1,6 @@ #MALLOC_CHECK_=1 # XXX debug only -LDLIBS += -lxml2 -lopensrf -lobjson +LDLIBS += -lxml2 -lopensrf CFLAGS += -D_ROUTER all: opensrf_router diff --git a/src/srfsh/Makefile b/src/srfsh/Makefile index 9b04bd5..efea8b2 100644 --- a/src/srfsh/Makefile +++ b/src/srfsh/Makefile @@ -1,6 +1,6 @@ # if EXEC_DEFAULT is defined, then srfsh will send all unknown commands to the shell for execution -LDLIBS += -lobjson -lreadline -lxml2 -lopensrf -lncurses +LDLIBS += -lreadline -lxml2 -lopensrf -lncurses LDFLAGS += -DEXEC_DEFAULT all: srfsh diff --git a/src/srfsh/srfsh.c b/src/srfsh/srfsh.c index 6c4cb54..2d41125 100644 --- a/src/srfsh/srfsh.c +++ b/src/srfsh/srfsh.c @@ -558,7 +558,7 @@ int send_request( char* server, jsonObject* params = NULL; if( !relay ) { if( buffer != NULL && buffer->n_used > 0 ) - params = json_parse_string(buffer->buf); + params = jsonParseString(buffer->buf); } else { if(!last_result || ! last_result->_result_content) { printf("We're not going to call 'relay' with no result params\n"); @@ -850,7 +850,7 @@ static int do_math( int count, int style ) { osrf_app_session* session = osrf_app_client_session_init( "opensrf.math" ); osrf_app_session_connect(session); - jsonObject* params = json_parse_string("[]"); + jsonObject* params = jsonParseString("[]"); jsonObjectPush(params,jsonNewObject("1")); jsonObjectPush(params,jsonNewObject("2")); -- 2.11.0