2 Copyright (C) 2006 Georgia Public Library Service
3 Bill Erickson <billserickson@gmail.com>
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
16 #include <opensrf/osrf_json.h>
17 #include <opensrf/osrf_json_utils.h>
19 static jsonObject* findMultiPath( const jsonObject* o,
20 const char* root, const char* path );
21 static jsonObject* findMultiPathRecurse( const jsonObject* o, const char* root );
22 static jsonObject* _jsonObjectEncodeClass( const jsonObject* obj, int ignoreClass );
25 @brief Append some spaces to a growing_buffer, for indentation.
26 @param buf Pointer to the growing_buffer.
27 @param depth Degree of indentation.
29 We append 2 spaces per degree of indentation.
31 static void append_indentation( growing_buffer* buf, int depth ) {
34 memset( indent, ' ', n );
35 buffer_add_n( buf, indent, n );
39 @brief Make a prettyprint copy of a JSON string.
40 @param string Pointer to the JSON string to be formatted.
41 @return Pointer to a newly allocated and reformatted JSON string.
43 Create a copy of the input JSON string, with newlines and
44 indentation for readability.
46 If the input pointer is NULL, return an empty string.
48 The calling code is responsible for freeing the formatted copy.
50 char* jsonFormatString( const char* string ) {
51 if(!string) return strdup("");
53 growing_buffer* buf = buffer_init(64);
58 for(i = 0; string[i]; i++) {
61 if( c == '{' || c == '[' ) {
63 OSRF_BUFFER_ADD_CHAR( buf, c );
64 OSRF_BUFFER_ADD_CHAR( buf, '\n' );
65 append_indentation( buf, ++depth );
67 } else if( c == '}' || c == ']' ) {
69 OSRF_BUFFER_ADD_CHAR( buf, '\n' );
70 append_indentation( buf, --depth );
71 OSRF_BUFFER_ADD_CHAR( buf, c );
73 } else if( c == ',' ) {
75 OSRF_BUFFER_ADD_CHAR( buf, ',' );
76 OSRF_BUFFER_ADD_CHAR( buf, '\n' );
77 append_indentation( buf, depth );
80 OSRF_BUFFER_ADD_CHAR(buf, c);
83 return buffer_release(buf);
88 jsonObject* jsonObjectDecodeClass( const jsonObject* obj ) {
89 if(!obj) return jsonNewObject(NULL);
91 jsonObject* newObj = NULL;
92 const jsonObject* classObj = NULL;
93 const jsonObject* payloadObj = NULL;
96 if( obj->type == JSON_HASH ) {
98 /* are we a special class object? */
99 if( (classObj = jsonObjectGetKeyConst( obj, JSON_CLASS_KEY )) ) {
101 /* do we have a payload */
102 if( (payloadObj = jsonObjectGetKeyConst( obj, JSON_DATA_KEY )) ) {
103 newObj = jsonObjectDecodeClass( payloadObj );
104 jsonObjectSetClass( newObj, jsonObjectGetString(classObj) );
106 } else { /* class is defined but there is no payload */
110 } else { /* we're a regular hash */
112 jsonIterator* itr = jsonNewIterator(obj);
114 newObj = jsonNewObjectType(JSON_HASH);
115 while( (tmp = jsonIteratorNext(itr)) ) {
116 jsonObject* o = jsonObjectDecodeClass(tmp);
117 jsonObjectSetKey( newObj, itr->key, o );
119 jsonIteratorFree(itr);
121 jsonObjectSetClass( newObj, obj->classname );
126 if( obj->type == JSON_ARRAY ) { /* we're an array */
127 newObj = jsonNewObjectType(JSON_ARRAY);
128 for( i = 0; i != obj->size; i++ ) {
129 jsonObject* tmp = jsonObjectDecodeClass(jsonObjectGetIndex( obj, i ) );
130 jsonObjectSetIndex( newObj, i, tmp );
133 jsonObjectSetClass( newObj, obj->classname );
135 } else { /* not an aggregate type */
136 newObj = jsonObjectClone(obj);
143 jsonObject* jsonObjectEncodeClass( const jsonObject* obj ) {
144 return _jsonObjectEncodeClass( obj, 0 );
147 static jsonObject* _jsonObjectEncodeClass( const jsonObject* obj, int ignoreClass ) {
149 //if(!obj) return NULL;
150 if(!obj) return jsonNewObject(NULL);
151 jsonObject* newObj = NULL;
153 if( obj->classname && ! ignoreClass ) {
154 newObj = jsonNewObjectType(JSON_HASH);
156 jsonObjectSetKey( newObj,
157 JSON_CLASS_KEY, jsonNewObject(obj->classname) );
159 jsonObjectSetKey( newObj,
160 JSON_DATA_KEY, _jsonObjectEncodeClass(obj, 1));
162 } else if( obj->type == JSON_HASH ) {
164 jsonIterator* itr = jsonNewIterator(obj);
166 newObj = jsonNewObjectType(JSON_HASH);
168 while( (tmp = jsonIteratorNext(itr)) ) {
169 jsonObjectSetKey( newObj, itr->key,
170 _jsonObjectEncodeClass(tmp, 0));
172 jsonIteratorFree(itr);
174 } else if( obj->type == JSON_ARRAY ) {
176 newObj = jsonNewObjectType(JSON_ARRAY);
178 for( i = 0; i != obj->size; i++ ) {
179 jsonObjectSetIndex( newObj, i,
180 _jsonObjectEncodeClass(jsonObjectGetIndex( obj, i ), 0 ));
184 newObj = jsonObjectClone(obj);
190 jsonObject* jsonObjectFindPath( const jsonObject* obj, const char* format, ...) {
191 if(!obj || !format || strlen(format) < 1) return NULL;
193 VA_LIST_TO_STRING(format);
196 char* tt; /* strtok storage */
198 /* special case where path starts with // (start anywhere) */
199 if(buf[0] == '/' && buf[1] == '/' && buf[2] != '\0') {
201 /* copy the path before strtok_r destroys it */
202 char* pathcopy = strdup(buf);
204 /* grab the root of the path */
205 token = strtok_r(buf, "/", &tt);
211 jsonObject* it = findMultiPath(obj, token, pathcopy + 1);
217 /* grab the root of the path */
218 token = strtok_r(buf, "/", &tt);
219 if(!token) return NULL;
222 obj = jsonObjectGetKeyConst(obj, token);
223 } while( (token = strtok_r(NULL, "/", &tt)) && obj);
225 return jsonObjectClone(obj);
229 /* --------------------------------------------------------------- */
231 /* Utility method. finds any object in the tree that matches the path.
232 Use this for finding paths that start with '//' */
233 static jsonObject* findMultiPath(const jsonObject* obj,
234 const char* root, const char* path) {
236 if(!obj || ! root || !path) return NULL;
238 /* collect all of the potential objects */
239 jsonObject* arr = findMultiPathRecurse(obj, root);
241 /* path is just /root or /root/ */
242 if( strlen(root) + 2 >= strlen(path) ) {
247 /* container for fully matching objects */
248 jsonObject* newarr = jsonNewObjectType(JSON_ARRAY);
251 /* gather all of the sub-objects that match the full path */
252 for( i = 0; i < arr->size; i++ ) {
253 const jsonObject* a = jsonObjectGetIndex(arr, i);
254 jsonObject* thing = jsonObjectFindPath(a , path + strlen(root) + 1);
256 if(thing) { //jsonObjectPush(newarr, thing);
257 if(thing->type == JSON_ARRAY) {
259 for( i = 0; i != thing->size; i++ )
260 jsonObjectPush(newarr, jsonObjectClone(jsonObjectGetIndex(thing,i)));
261 jsonObjectFree(thing);
263 jsonObjectPush(newarr, thing);
273 /* returns a list of object whose key is 'root'. These are used as
274 potential objects when doing a // search */
275 static jsonObject* findMultiPathRecurse(const jsonObject* obj, const char* root) {
277 jsonObject* arr = jsonNewObjectType(JSON_ARRAY);
282 /* if the current object has a node that matches, add it */
284 const jsonObject* o = jsonObjectGetKeyConst(obj, root);
285 if(o) jsonObjectPush( arr, jsonObjectClone(o) );
287 jsonObject* tmp = NULL;
288 jsonObject* childarr;
289 jsonIterator* itr = jsonNewIterator(obj);
291 /* recurse through the children and find all potential nodes */
292 while( (tmp = jsonIteratorNext(itr)) ) {
293 childarr = findMultiPathRecurse(tmp, root);
294 if(childarr && childarr->size > 0) {
295 for( i = 0; i!= childarr->size; i++ ) {
296 jsonObjectPush( arr, jsonObjectClone(jsonObjectGetIndex(childarr, i)) );
299 jsonObjectFree(childarr);
302 jsonIteratorFree(itr);