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