]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/python/osrf/json.py
adding python libs
[OpenSRF.git] / src / python / osrf / json.py
1 # -----------------------------------------------------------------------
2 # Copyright (C) 2007  Georgia Public Library Service
3 # Bill Erickson <billserickson@gmail.com>
4
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.
9
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.
14 # -----------------------------------------------------------------------
15
16
17 import simplejson, types
18
19 JSON_PAYLOAD_KEY = '__p'
20 JSON_CLASS_KEY = '__c'
21
22 class osrfNetworkObject(object):
23         """Base class for serializable network objects."""
24         def getData(self):
25                 """Returns a dict of data contained by this object"""
26                 return self.data
27
28
29 class __unknown(osrfNetworkObject):
30         """Default class for un-registered network objects."""
31         def __init__(self, data=None):
32                 self.data = data
33
34 setattr(__unknown,'__keys', [])
35 setattr(osrfNetworkObject,'__unknown', __unknown)
36
37
38 def osrfNetworkRegisterHint(hint, keys, type='hash'):
39         """Register a network hint.  
40         
41                 This creates a new class at osrfNetworkObject.<hint> with 
42                 methods for accessing/mutating the object's data.  
43                 Method names will match the names found in the keys array
44
45                 hint - The hint name to encode with the object
46                 type - The data container type.  
47                 keys - An array of data keys.  If type is an 'array', the order of
48                 the keys will determine how the data is accessed
49         """
50
51         estr = "class %s(osrfNetworkObject):\n" % hint
52         estr += "\tdef __init__(self, data=None):\n"
53         estr += "\t\tself.data = data\n"
54         estr += "\t\tif data:\n"
55
56         if type == 'hash': 
57                 estr += "\t\t\tpass\n"
58         else:
59                 # we have to make sure the array is large enough        
60                 estr += "\t\t\twhile len(data) < %d:\n" % len(keys)
61                 estr += "\t\t\t\tdata.append(None)\n"
62
63         estr += "\t\telse:\n"
64
65         if type == 'array':
66                 estr += "\t\t\tself.data = []\n"
67                 estr += "\t\t\tfor i in range(%s):\n" % len(keys)
68                 estr += "\t\t\t\tself.data.append(None)\n"
69                 for i in range(len(keys)):
70                         estr += "\tdef %s(self, *args):\n"\
71                                                 "\t\tif len(args) != 0:\n"\
72                                                 "\t\t\tself.data[%s] = args[0]\n"\
73                                                 "\t\treturn self.data[%s]\n" % (keys[i], i, i)
74
75         if type == 'hash':
76                 estr += "\t\t\tself.data = {}\n"
77                 estr += "\t\t\tfor i in %s:\n" % str(keys)
78                 estr += "\t\t\t\tself.data[i] = None\n"
79                 for i in keys:
80                         estr += "\tdef %s(self, *args):\n"\
81                                                 "\t\tif len(args) != 0:\n"\
82                                                 "\t\t\tself.data['%s'] = args[0]\n"\
83                                                 "\t\tval = None\n"\
84                                                 "\t\ttry: val = self.data['%s']\n"\
85                                                 "\t\texcept: return None\n"\
86                                                 "\t\treturn val\n" % (i, i, i)
87
88         estr += "setattr(osrfNetworkObject, '%s', %s)\n" % (hint,hint)
89         estr += "setattr(osrfNetworkObject.%s, '__keys', keys)" % hint
90         exec(estr)
91         
92                 
93
94 # -------------------------------------------------------------------
95 # Define the custom object parsing behavior 
96 # -------------------------------------------------------------------
97 def __parseNetObject(obj):
98         hint = None
99         islist = False
100         try:
101                 hint = obj[JSON_CLASS_KEY]
102                 obj = obj[JSON_PAYLOAD_KEY]
103         except: pass
104         if isinstance(obj,list):
105                 islist = True
106                 for i in range(len(obj)):
107                         obj[i] = __parseNetObject(obj[i])
108         else: 
109                 if isinstance(obj,dict):
110                         for k,v in obj.iteritems():
111                                 obj[k] = __parseNetObject(v)
112
113         if hint: # Now, "bless" the object into an osrfNetworkObject
114                 estr = 'obj = osrfNetworkObject.%s(obj)' % hint
115                 try:
116                         exec(estr)
117                 except AttributeError:
118                         # this object has not been registered, shove it into the default container
119                         obj = osrfNetworkObject.__unknown(obj)
120
121         return obj;
122
123
124 # -------------------------------------------------------------------
125 # Define the custom object encoding behavior 
126 # -------------------------------------------------------------------
127 class osrfJSONNetworkEncoder(simplejson.JSONEncoder):
128         def default(self, obj):
129                 if isinstance(obj, osrfNetworkObject):
130                         return { 
131                                 JSON_CLASS_KEY: obj.__class__.__name__,
132                                 JSON_PAYLOAD_KEY: self.default(obj.getData())
133                         }       
134                 return obj
135
136
137 def osrfObjectToJSON(obj):
138         """Turns a python object into a wrapped JSON object"""
139         return simplejson.dumps(obj, cls=osrfJSONNetworkEncoder)
140
141
142 def osrfJSONToObject(json):
143         """Turns a JSON string into python objects"""
144         obj = simplejson.loads(json)
145         return __parseNetObject(obj)
146
147 def osrfParseJSONRaw(json):
148         """Parses JSON the old fashioned way."""
149         return simplejson.loads(json)
150
151 def osrfToJSONRaw(obj):
152         """Stringifies an object as JSON with no additional logic."""
153         return simplejson.dumps(obj)
154
155 def __tabs(t):
156         r=''
157         for i in range(t): r += '   '
158         return r
159
160 def osrfDebugNetworkObject(obj, t=1):
161         """Returns a debug string for a given object.
162
163         If it's an osrfNetworkObject and has registered keys, key/value p
164         pairs are returned.  Otherwise formatted JSON is returned"""
165
166         s = ''
167         if isinstance(obj, osrfNetworkObject) and len(obj.__keys):
168                 obj.__keys.sort()
169
170                 for k in obj.__keys:
171
172                         key = k
173                         while len(key) < 24: key += '.' # pad the names to make the values line up somewhat
174                         val = getattr(obj, k)()
175
176                         subobj = val and not (isinstance(val,unicode) or \
177                                         isinstance(val, int) or isinstance(val, float) or isinstance(val, long))
178
179
180                         s += __tabs(t) + key + ' = '
181
182                         if subobj:
183                                 s += '\n'
184                                 val = osrfDebugNetworkObject(val, t+1)
185
186                         s += str(val)
187
188                         if not subobj: s += '\n'
189
190         else:
191                 s = osrfFormatJSON(osrfObjectToJSON(obj))
192         return s
193
194 def osrfFormatJSON(json):
195         """JSON pretty-printer"""
196         r = ''
197         t = 0
198         instring = False
199         inescape = False
200         done = False
201
202         for c in json:
203
204                 done = False
205                 if (c == '{' or c == '[') and not instring:
206                         t += 1
207                         r += c + '\n' + __tabs(t)
208                         done = True
209
210                 if (c == '}' or c == ']') and not instring:
211                         t -= 1
212                         r += '\n' + __tabs(t) + c
213                         done = True
214
215                 if c == ',' and not instring:
216                         r += c + '\n' + __tabs(t)
217                         done = True
218
219                 if c == '"' and not inescape:
220                         instring = not instring
221
222                 if inescape: 
223                         inescape = False
224
225                 if c == '\\':
226                         inescape = True
227
228                 if not done:
229                         r += c
230
231         return r
232
233