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 );
24 static char* _tabs(int count) {
25 growing_buffer* buf = buffer_init(24);
27 for(i=0;i<count;i++) OSRF_BUFFER_ADD(buf, " ");
28 return buffer_release(buf);
31 char* jsonFormatString( const char* string ) {
32 if(!string) return strdup("");
34 growing_buffer* buf = buffer_init(64);
40 for(i=0; i!= strlen(string); i++) {
43 if( c == '{' || c == '[' ) {
46 buffer_fadd( buf, "%c\n%s", c, tab);
49 } else if( c == '}' || c == ']' ) {
52 buffer_fadd( buf, "\n%s%c", tab, c);
55 if(string[i+1] != ',') {
57 buffer_fadd( buf, "%s", tab );
61 } else if( c == ',' ) {
64 buffer_fadd(buf, ",\n%s", tab);
67 } else { buffer_add_char(buf, c); }
71 return buffer_release(buf);
76 jsonObject* jsonObjectDecodeClass( const jsonObject* obj ) {
77 if(!obj) return jsonNewObject(NULL);
79 jsonObject* newObj = NULL;
80 const jsonObject* classObj = NULL;
81 const jsonObject* payloadObj = NULL;
84 if( obj->type == JSON_HASH ) {
86 /* are we a special class object? */
87 if( (classObj = jsonObjectGetKeyConst( obj, JSON_CLASS_KEY )) ) {
89 /* do we have a payload */
90 if( (payloadObj = jsonObjectGetKeyConst( obj, JSON_DATA_KEY )) ) {
91 newObj = jsonObjectDecodeClass( payloadObj );
92 jsonObjectSetClass( newObj, jsonObjectGetString(classObj) );
94 } else { /* class is defined but there is no payload */
98 } else { /* we're a regular hash */
100 jsonIterator* itr = jsonNewIterator(obj);
102 newObj = jsonNewObjectType(JSON_HASH);
103 while( (tmp = jsonIteratorNext(itr)) ) {
104 jsonObject* o = jsonObjectDecodeClass(tmp);
105 jsonObjectSetKey( newObj, itr->key, o );
107 jsonIteratorFree(itr);
112 if( obj->type == JSON_ARRAY ) { /* we're an array */
113 newObj = jsonNewObjectType(JSON_ARRAY);
114 for( i = 0; i != obj->size; i++ ) {
115 jsonObject* tmp = jsonObjectDecodeClass(jsonObjectGetIndex( obj, i ) );
116 jsonObjectSetIndex( newObj, i, tmp );
119 } else { /* not an aggregate type */
120 newObj = jsonObjectClone(obj);
127 jsonObject* jsonObjectEncodeClass( const jsonObject* obj ) {
128 return _jsonObjectEncodeClass( obj, 0 );
131 static jsonObject* _jsonObjectEncodeClass( const jsonObject* obj, int ignoreClass ) {
133 //if(!obj) return NULL;
134 if(!obj) return jsonNewObject(NULL);
135 jsonObject* newObj = NULL;
137 if( obj->classname && ! ignoreClass ) {
138 newObj = jsonNewObjectType(JSON_HASH);
140 jsonObjectSetKey( newObj,
141 JSON_CLASS_KEY, jsonNewObject(obj->classname) );
143 jsonObjectSetKey( newObj,
144 JSON_DATA_KEY, _jsonObjectEncodeClass(obj, 1));
146 } else if( obj->type == JSON_HASH ) {
148 jsonIterator* itr = jsonNewIterator(obj);
150 newObj = jsonNewObjectType(JSON_HASH);
152 while( (tmp = jsonIteratorNext(itr)) ) {
153 jsonObjectSetKey( newObj, itr->key,
154 _jsonObjectEncodeClass(tmp, 0));
156 jsonIteratorFree(itr);
158 } else if( obj->type == JSON_ARRAY ) {
160 newObj = jsonNewObjectType(JSON_ARRAY);
162 for( i = 0; i != obj->size; i++ ) {
163 jsonObjectSetIndex( newObj, i,
164 _jsonObjectEncodeClass(jsonObjectGetIndex( obj, i ), 0 ));
168 newObj = jsonObjectClone(obj);
174 jsonObject* jsonParseFile( const char* filename ) {
175 if(!filename) return NULL;
176 char* data = file_to_string(filename);
177 jsonObject* o = jsonParseString(data);
184 jsonObject* jsonObjectFindPath( const jsonObject* obj, const char* format, ...) {
185 if(!obj || !format || strlen(format) < 1) return NULL;
187 VA_LIST_TO_STRING(format);
190 char* tt; /* strtok storage */
192 /* special case where path starts with // (start anywhere) */
193 if(buf[0] == '/' && buf[1] == '/' && buf[2] != '\0') {
195 /* copy the path before strtok_r destroys it */
196 char* pathcopy = strdup(buf);
198 /* grab the root of the path */
199 token = strtok_r(buf, "/", &tt);
205 jsonObject* it = findMultiPath(obj, token, pathcopy + 1);
211 /* grab the root of the path */
212 token = strtok_r(buf, "/", &tt);
213 if(!token) return NULL;
216 obj = jsonObjectGetKeyConst(obj, token);
217 } while( (token = strtok_r(NULL, "/", &tt)) && obj);
219 return jsonObjectClone(obj);
223 /* --------------------------------------------------------------- */
225 /* Utility method. finds any object in the tree that matches the path.
226 Use this for finding paths that start with '//' */
227 static jsonObject* findMultiPath(const jsonObject* obj,
228 const char* root, const char* path) {
230 if(!obj || ! root || !path) return NULL;
232 /* collect all of the potential objects */
233 jsonObject* arr = findMultiPathRecurse(obj, root);
235 /* path is just /root or /root/ */
236 if( strlen(root) + 2 >= strlen(path) ) {
241 /* container for fully matching objects */
242 jsonObject* newarr = jsonNewObjectType(JSON_ARRAY);
245 /* gather all of the sub-objects that match the full path */
246 for( i = 0; i < arr->size; i++ ) {
247 const jsonObject* a = jsonObjectGetIndex(arr, i);
248 jsonObject* thing = jsonObjectFindPath(a , path + strlen(root) + 1);
250 if(thing) { //jsonObjectPush(newarr, thing);
251 if(thing->type == JSON_ARRAY) {
253 for( i = 0; i != thing->size; i++ )
254 jsonObjectPush(newarr, jsonObjectClone(jsonObjectGetIndex(thing,i)));
255 jsonObjectFree(thing);
257 jsonObjectPush(newarr, thing);
267 /* returns a list of object whose key is 'root'. These are used as
268 potential objects when doing a // search */
269 static jsonObject* findMultiPathRecurse(const jsonObject* obj, const char* root) {
271 jsonObject* arr = jsonNewObjectType(JSON_ARRAY);
276 /* if the current object has a node that matches, add it */
278 const jsonObject* o = jsonObjectGetKeyConst(obj, root);
279 if(o) jsonObjectPush( arr, jsonObjectClone(o) );
281 jsonObject* tmp = NULL;
282 jsonObject* childarr;
283 jsonIterator* itr = jsonNewIterator(obj);
285 /* recurse through the children and find all potential nodes */
286 while( (tmp = jsonIteratorNext(itr)) ) {
287 childarr = findMultiPathRecurse(tmp, root);
288 if(childarr && childarr->size > 0) {
289 for( i = 0; i!= childarr->size; i++ ) {
290 jsonObjectPush( arr, jsonObjectClone(jsonObjectGetIndex(childarr, i)) );
293 jsonObjectFree(childarr);
296 jsonIteratorFree(itr);