1 # -----------------------------------------------------------------------
2 # Copyright (C) 2007 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.
14 # -----------------------------------------------------------------------
17 import simplejson, types
19 JSON_PAYLOAD_KEY = '__p'
20 JSON_CLASS_KEY = '__c'
22 class osrfNetworkObject(object):
23 """Base class for serializable network objects."""
25 """Returns a dict of data contained by this object"""
29 class __unknown(osrfNetworkObject):
30 """Default class for un-registered network objects."""
31 def __init__(self, data=None):
34 setattr(__unknown,'__keys', [])
35 setattr(osrfNetworkObject,'__unknown', __unknown)
38 def osrfNetworkRegisterHint(hint, keys, type='hash'):
39 """Register a network hint.
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
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
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"
57 estr += "\t\t\tpass\n"
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"
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)
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"
80 estr += "\tdef %s(self, *args):\n"\
81 "\t\tif len(args) != 0:\n"\
82 "\t\t\tself.data['%s'] = args[0]\n"\
84 "\t\ttry: val = self.data['%s']\n"\
85 "\t\texcept: return None\n"\
86 "\t\treturn val\n" % (i, i, i)
88 estr += "setattr(osrfNetworkObject, '%s', %s)\n" % (hint,hint)
89 estr += "setattr(osrfNetworkObject.%s, '__keys', keys)" % hint
94 # -------------------------------------------------------------------
95 # Define the custom object parsing behavior
96 # -------------------------------------------------------------------
97 def __parseNetObject(obj):
101 hint = obj[JSON_CLASS_KEY]
102 obj = obj[JSON_PAYLOAD_KEY]
104 if isinstance(obj,list):
106 for i in range(len(obj)):
107 obj[i] = __parseNetObject(obj[i])
109 if isinstance(obj,dict):
110 for k,v in obj.iteritems():
111 obj[k] = __parseNetObject(v)
113 if hint: # Now, "bless" the object into an osrfNetworkObject
114 estr = 'obj = osrfNetworkObject.%s(obj)' % hint
117 except AttributeError:
118 # this object has not been registered, shove it into the default container
119 obj = osrfNetworkObject.__unknown(obj)
124 # -------------------------------------------------------------------
125 # Define the custom object encoding behavior
126 # -------------------------------------------------------------------
127 class osrfJSONNetworkEncoder(simplejson.JSONEncoder):
128 def default(self, obj):
129 if isinstance(obj, osrfNetworkObject):
131 JSON_CLASS_KEY: obj.__class__.__name__,
132 JSON_PAYLOAD_KEY: self.default(obj.getData())
137 def osrfObjectToJSON(obj):
138 """Turns a python object into a wrapped JSON object"""
139 return simplejson.dumps(obj, cls=osrfJSONNetworkEncoder)
142 def osrfJSONToObject(json):
143 """Turns a JSON string into python objects"""
144 obj = simplejson.loads(json)
145 return __parseNetObject(obj)
147 def osrfParseJSONRaw(json):
148 """Parses JSON the old fashioned way."""
149 return simplejson.loads(json)
151 def osrfToJSONRaw(obj):
152 """Stringifies an object as JSON with no additional logic."""
153 return simplejson.dumps(obj)
157 for i in range(t): r += ' '
160 def osrfDebugNetworkObject(obj, t=1):
161 """Returns a debug string for a given object.
163 If it's an osrfNetworkObject and has registered keys, key/value p
164 pairs are returned. Otherwise formatted JSON is returned"""
167 if isinstance(obj, osrfNetworkObject) and len(obj.__keys):
173 while len(key) < 24: key += '.' # pad the names to make the values line up somewhat
174 val = getattr(obj, k)()
176 subobj = val and not (isinstance(val,unicode) or \
177 isinstance(val, int) or isinstance(val, float) or isinstance(val, long))
180 s += __tabs(t) + key + ' = '
184 val = osrfDebugNetworkObject(val, t+1)
188 if not subobj: s += '\n'
191 s = osrfFormatJSON(osrfObjectToJSON(obj))
194 def osrfFormatJSON(json):
195 """JSON pretty-printer"""
205 if (c == '{' or c == '[') and not instring:
207 r += c + '\n' + __tabs(t)
210 if (c == '}' or c == ']') and not instring:
212 r += '\n' + __tabs(t) + c
215 if c == ',' and not instring:
216 r += c + '\n' + __tabs(t)
219 if c == '"' and not inescape:
220 instring = not instring