]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/python/osrf/json.py
198ffb56434a140fdec9cc43ca11008353a0f40a
[OpenSRF.git] / src / python / osrf / json.py
1 import simplejson, types 
2 from osrf.net_obj import NetworkObject, parse_net_object
3 from osrf.const import OSRF_JSON_PAYLOAD_KEY, OSRF_JSON_CLASS_KEY
4 import osrf.log
5
6 try:
7     # if available, use the faster cjson module for encoding/decoding JSON
8     import cjson
9     _use_cjson = True
10 except ImportError:
11     _use_cjson = False
12
13 _use_cjson = False
14
15 class NetworkEncoder(simplejson.JSONEncoder):
16     ''' Encoder used by simplejson '''
17     def default(self, obj):
18
19         if isinstance(obj, NetworkObject):
20             reg = obj.get_registry()
21             data = obj.get_data()
22
23             # re-encode the object as an array if necessary
24             if reg.protocol == 'array':
25                 objarray = []
26                 for key in reg.keys:
27                     objarray.append(data.get(key)) 
28                 data = objarray
29
30             return { 
31                 OSRF_JSON_CLASS_KEY: reg.hint,
32                 OSRF_JSON_PAYLOAD_KEY: self.default(data)
33             }   
34         return obj
35
36
37 def encode_object(obj):
38     ''' Generic opensrf object encoder, used by cjson '''
39
40     if isinstance(obj, dict):
41         newobj = {}
42         for k,v in obj.iteritems():
43             newobj[k] = encode_object(v)
44         return newobj
45
46     elif isinstance(obj, list):
47         return [encode_object(v) for v in obj]
48
49     elif isinstance(obj, NetworkObject):
50         reg = obj.get_registry()
51         data = obj.get_data()
52
53         if reg.protocol == 'array':
54             objarray = []
55             for key in reg.keys:
56                 objarray.append(data.get(key)) 
57             data = objarray
58
59         return {
60             OSRF_JSON_CLASS_KEY: reg.hint,
61             OSRF_JSON_PAYLOAD_KEY: encode_object(data)
62         }
63
64     return obj
65         
66
67
68 def to_json(obj):
69     """Turns a python object into a wrapped JSON object"""
70     if _use_cjson:
71         return cjson.encode(encode_object(obj))
72     return simplejson.dumps(obj, cls=NetworkEncoder)
73
74
75 def to_object(json):
76     """Turns a JSON string into python objects"""
77     if _use_cjson:
78         return parse_net_object(cjson.decode(json))
79     return parse_net_object(simplejson.loads(json))
80
81 def parse_json_raw(json):
82     """Parses JSON the old fashioned way."""
83     if _use_cjson:
84         return cjson.decode(json)
85     return simplejson.loads(json)
86
87 def to_json_raw(obj):
88     """Stringifies an object as JSON with no additional logic."""
89     if _use_cjson:
90         return cjson.encode(json)
91     return simplejson.dumps(obj)
92
93 def __tabs(depth):
94     space = ''
95     for i in range(depth):
96         space += '   '
97     return space
98
99 def debug_net_object(obj, depth=1):
100     """Returns a debug string for a given object.
101
102     If it's an NetworkObject and has registered keys, key/value pairs
103     are returned.  Otherwise formatted JSON is returned"""
104
105     debug_str = ''
106     if isinstance(obj, NetworkObject):
107         reg = obj.get_registry()
108         keys = list(reg.keys) # clone it, so sorting won't break the original
109         keys.sort()
110
111         for k in keys:
112
113             key = str(k)
114             while len(key) < 24:
115                 key += '.' # pad the names to make the values line up somewhat
116             val = getattr(obj, k)()
117
118             subobj = val and not (isinstance(val, unicode) or isinstance(val, str) or \
119                 isinstance(val, int) or isinstance(val, float) or isinstance(val, long))
120
121             debug_str += __tabs(depth) + key + ' = '
122
123             if subobj:
124                 debug_str += '\n'
125                 val = debug_net_object(val, depth+1)
126
127             debug_str += str(val)
128
129             if not subobj: debug_str += '\n'
130
131     else:
132         osrf.log.log_internal("Pretty-printing NetworkObject")
133         debug_str = pprint(to_json(obj))
134     return debug_str
135
136 def pprint(json):
137     """JSON pretty-printer"""
138     r = ''
139     t = 0
140     instring = False
141     inescape = False
142     done = False
143     eatws = False
144
145     for c in json:
146
147         if eatws and not _use_cjson: # simpljson adds a pesky space after array and object items
148             if c == ' ': 
149                 continue
150
151         eatws = False
152         done = False
153         if (c == '{' or c == '[') and not instring:
154             t += 1
155             r += c + '\n' + __tabs(t)
156             done = True
157
158         if (c == '}' or c == ']') and not instring:
159             t -= 1
160             r += '\n' + __tabs(t) + c
161             done = True
162
163         if c == ',' and not instring:
164             r += c + '\n' + __tabs(t)
165             done = True
166             eatws = True
167
168         if c == ':' and not instring:
169             eatws = True
170
171         if c == '"' and not inescape:
172             instring = not instring
173
174         if inescape: 
175             inescape = False
176
177         if c == '\\':
178             inescape = True
179
180         if not done:
181             r += c
182
183     return r