227
230
serializer = Serializer(request.environ, _metadata)
228
231
return serializer.to_content_type(data)
233
def _deserialize(self, data, request):
235
Deserialize the request body to the response type requested in request.
236
Uses self._serialization_metadata if it exists, which is a dict mapping
237
MIME types to information needed to serialize to that type.
239
_metadata = getattr(type(self), "_serialization_metadata", {})
240
serializer = Serializer(request.environ, _metadata)
241
return serializer.deserialize(data)
231
243
class Serializer(object):
233
Serializes a dictionary to a Content Type specified by a WSGI environment.
245
Serializes and deserializes dictionaries to certain MIME types.
236
248
def __init__(self, environ, metadata=None):
239
251
'metadata' is an optional dict mapping MIME types to information
240
252
needed to serialize a dictionary to that type.
242
self.environ = environ
243
254
self.metadata = metadata or {}
245
'application/json': self._to_json,
246
'application/xml': self._to_xml}
255
req = webob.Request(environ)
256
suffix = req.path_info.split('.')[-1].lower()
258
self.handler = self._to_json
259
elif suffix == 'xml':
260
self.handler = self._to_xml
261
elif 'application/json' in req.accept:
262
self.handler = self._to_json
263
elif 'application/xml' in req.accept:
264
self.handler = self._to_xml
266
self.handler = self._to_json # default
248
268
def to_content_type(self, data):
250
Serialize a dictionary into a string. The format of the string
251
will be decided based on the Content Type requested in self.environ:
252
by Accept: header, or by URL suffix.
254
mimetype = 'application/xml'
255
# TODO(gundlach): determine mimetype from request
256
return self._methods.get(mimetype, repr)(data)
270
Serialize a dictionary into a string.
272
The format of the string will be decided based on the Content Type
273
requested in self.environ: by Accept: header, or by URL suffix.
275
return self.handler(data)
277
def deserialize(self, datastring):
279
Deserialize a string to a dictionary.
281
The string must be in the format of a supported MIME type.
283
datastring = datastring.strip()
285
is_xml = (datastring[0] == '<')
287
return json.loads(datastring)
288
return self._from_xml(datastring)
292
def _from_xml(self, datastring):
293
xmldata = self.metadata.get('application/xml', {})
294
plurals = set(xmldata.get('plurals', {}))
295
node = minidom.parseString(datastring).childNodes[0]
296
return {node.nodeName: self._from_xml_node(node, plurals)}
298
def _from_xml_node(self, node, listnames):
300
Convert a minidom node to a simple Python type.
302
listnames is a collection of names of XML nodes whose subnodes should
303
be considered list items.
305
if len(node.childNodes) == 1 and node.childNodes[0].nodeType == 3:
306
return node.childNodes[0].nodeValue
307
elif node.nodeName in listnames:
308
return [self._from_xml_node(n, listnames) for n in node.childNodes]
311
for attr in node.attributes.keys():
312
result[attr] = node.attributes[attr].nodeValue
313
for child in node.childNodes:
314
if child.nodeType != node.TEXT_NODE:
315
result[child.nodeName] = self._from_xml_node(child, listnames)
258
318
def _to_json(self, data):
260
319
return json.dumps(data)
262
321
def _to_xml(self, data):
263
322
metadata = self.metadata.get('application/xml', {})
264
323
# We expect data to contain a single key which is the XML root.
265
324
root_key = data.keys()[0]
266
from xml.dom import minidom
267
325
doc = minidom.Document()
268
326
node = self._to_xml_node(doc, metadata, root_key, data[root_key])
269
327
return node.toprettyxml(indent=' ')