Commit 5d592fa9 authored by Jacek Furmankiewicz's avatar Jacek Furmankiewicz

issue 4: improved serialization of object graphs to XML

parent 45f15f6a
...@@ -50,7 +50,7 @@ class ConflictException(RESTException): ...@@ -50,7 +50,7 @@ class ConflictException(RESTException):
class AlreadyExistsException(ConflictException): class AlreadyExistsException(ConflictException):
"""Standard 409 exception when REST resource already exists during a POST""" """Standard 409 exception when REST resource already exists during a POST"""
def __init__(self, resourceName, invalidValue, message): def __init__(self, resourceName, invalidValue, message = None):
ConflictException.__init__(self, resourceName, invalidValue, "%s already exists" % resourceName) ConflictException.__init__(self, resourceName, invalidValue, "%s already exists" % resourceName)
class InternalServerException(RESTException): class InternalServerException(RESTException):
......
...@@ -55,13 +55,51 @@ def traverseDict(dictObject): ...@@ -55,13 +55,51 @@ def traverseDict(dictObject):
def generateXml(obj): def generateXml(obj):
"""Generates basic XML from an object that has already been converted for serialization""" """Generates basic XML from an object that has already been converted for serialization"""
if isinstance(object,dict): if isinstance(obj, dict) or isinstance(obj,DictMixin):
return str(xmlTemplate.render(item=obj.keys())) return getXML_dict(obj, "item")
elif isinstance(obj,collections.Iterable): elif isinstance(obj,collections.Iterable):
return str(xmlListTemplate.render(items=obj)) return "<list>%s</list>" % getXML(obj, "item")
else: else:
raise RuntimeError("Unable to convert to XML: %s" % obj) raise RuntimeError("Unable to convert to XML: %s" % obj)
def isClassInstance(obj): def isClassInstance(obj):
"""Checks if a given obj is a class instance""" """Checks if a given obj is a class instance"""
return getattr(obj, "__class__",None) != None and not isinstance(obj,dict) and not isinstance(obj,tuple) and not isinstance(obj,list) and not isinstance(obj,str) return getattr(obj, "__class__",None) != None and not isinstance(obj,dict) and not isinstance(obj,tuple) and not isinstance(obj,list) and not isinstance(obj,str)
## {{{ http://code.activestate.com/recipes/440595/ (r2)
def getXML(obj, objname=None):
"""getXML(obj, objname=None)
returns an object as XML where Python object names are the tags.
>>> u={'UserID':10,'Name':'Mark','Group':['Admin','Webmaster']}
>>> getXML(u,'User')
'<User><UserID>10</UserID><Name>Mark</Name><Group>Admin</Group><Group>Webmaster</Group></User>'
"""
if obj == None:
return ""
if not objname:
objname = "item"
adapt={
dict: getXML_dict,
list: getXML_list,
tuple: getXML_list,
}
if adapt.has_key(obj.__class__):
return adapt[obj.__class__](obj, objname)
else:
return "<%(n)s>%(o)s</%(n)s>"%{'n':objname,'o':str(obj)}
def getXML_dict(indict, objname=None):
h = "<%s>"%objname
for k, v in indict.items():
h += getXML(v, k)
h += "</%s>"%objname
return h
def getXML_list(inlist, objname=None):
h = ""
for i in inlist:
h += getXML(i, objname)
return h
## end of http://code.activestate.com/recipes/440595/ }}}
...@@ -7,6 +7,9 @@ Feature: REST App ...@@ -7,6 +7,9 @@ Feature: REST App
Background: Background:
Given 'rest_resource' is running Given 'rest_resource' is running
# make sure it is empty
When as user 'None:None' I DELETE 'http://127.0.0.1:8085/customer'
Then I expect HTTP code 200
# add a few default customers # add a few default customers
When as user 'None:None' I POST 'http://127.0.0.1:8085/customer' with 'customerId=d1&firstName=John&lastName=Doe1' When as user 'None:None' I POST 'http://127.0.0.1:8085/customer' with 'customerId=d1&firstName=John&lastName=Doe1'
Then I expect HTTP code 201 Then I expect HTTP code 201
...@@ -147,4 +150,23 @@ Feature: REST App ...@@ -147,4 +150,23 @@ Feature: REST App
Then I expect HTTP code 200 Then I expect HTTP code 200
When as user 'None:None' I GET 'http://127.0.0.1:8085/customer/d1/address/HOME' When as user 'None:None' I GET 'http://127.0.0.1:8085/customer/d1/address/HOME'
Then I expect HTTP code 404 Then I expect HTTP code 404
@customer @xml @issue4
Scenario: Customers as XML (Issue #4)
# all
When I prepare HTTP header 'Accept' = 'application/xml'
When as user 'None:None' I GET 'http://127.0.0.1:8085/customer'
Then I expect HTTP code 200
Then I expect content contains '<list><item><lastName>Doe2</lastName><customerId>d2</customerId><firstName>John</firstName><addresses></addresses></item><item><lastName>Doe3</lastName><customerId>d3</customerId><firstName>John</firstName><addresses></addresses></item><item><lastName>Doe1</lastName><customerId>d1</customerId><firstName>John</firstName><addresses></addresses></item></list>'
# 1
When I prepare HTTP header 'Accept' = 'application/xml'
When as user 'None:None' I GET 'http://127.0.0.1:8085/customer/d2'
Then I expect HTTP code 200
Then I expect content contains '<item><lastName>Doe2</lastName><customerId>d2</customerId><firstName>John</firstName><addresses></addresses></item>'
# 1 with address
When as user 'None:None' I POST 'http://127.0.0.1:8085/customer/d2/address' with 'addressId=HOME&streetNumber=100&streetName=MyStreet&stateCode=CA&countryCode=US'
When I prepare HTTP header 'Accept' = 'application/xml'
When as user 'None:None' I GET 'http://127.0.0.1:8085/customer/d2'
Then I expect HTTP code 200
Then I expect content contains '<item><lastName>Doe2</lastName><customerId>d2</customerId><firstName>John</firstName><addresses><HOME><countryCode>US</countryCode><streetName>MyStreet</streetName><streetNumber>100</streetNumber><stateCode>CA</stateCode></HOME></addresses></item>'
\ No newline at end of file
...@@ -101,6 +101,9 @@ Links ...@@ -101,6 +101,9 @@ Links
Changelog Changelog
````````` `````````
* 0.0.16:
- minor bug fix for issue #4 (serializing object graphs to XML):
https://github.com/jacek99/corepost/issues/3
* 0.0.15: * 0.0.15:
- minor bug fixes in auto-converting responses to JSON and parsing arguments/paths with unexpectec characters - minor bug fixes in auto-converting responses to JSON and parsing arguments/paths with unexpectec characters
* 0.0.14: * 0.0.14:
...@@ -137,7 +140,7 @@ from setuptools import setup ...@@ -137,7 +140,7 @@ from setuptools import setup
setup( setup(
name="CorePost", name="CorePost",
version="0.0.15", version="0.0.16",
author="Jacek Furmankiewicz", author="Jacek Furmankiewicz",
author_email="jacek99@gmail.com", author_email="jacek99@gmail.com",
description=("A Twisted Web REST micro-framework"), description=("A Twisted Web REST micro-framework"),
...@@ -157,7 +160,7 @@ setup( ...@@ -157,7 +160,7 @@ setup(
"Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Software Development :: Libraries :: Python Modules",
], ],
install_requires=[ install_requires=[
'twisted>=11.0.0', 'twisted>=12.0.0',
'formencode>=1.2.4', 'formencode>=1.2.4',
'pyyaml>=3.1.0', 'pyyaml>=3.1.0',
'jinja2>=2.6', 'jinja2>=2.6',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment