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