I have following:
from spyne.service import ServiceBase
from spyne.util import xml
from spyne.model import complex, primitive
class ComplexModel(complex.ComplexModelBase):
__namespace__ = 'http://xml.candyshop.com/ns/candies/'
__metaclass__ = complex.ComplexModelMeta
class CandyModel(ComplexModel):
__type_name__ = 'candy'
flavor = complex.XmlAttribute(primitive.Unicode)
class BagModel(ComplexModel):
__type_name__ = 'bag'
candies = complex.Array(CandyModel)
class CandyShop(ServiceBase):
__tns__ = 'http://xml.candyshop.com/ns/shop/'
@rpc(_returns=primitive.AnyXml)
def get_my_bag(ctx):
bag = BagModel()
bag.candies = [CandyModel(flavor='choco')]
return xml.get_object_as_xml(
bag,
cls=BagModel,
root_tag_name='bag',
)
@classmethod
def dispatch(cls):
from django.views.decorators.csrf import csrf_exempt
from spyne.application import Application
from spyne.server.django import DjangoApplication
application = Application([cls],
tns=cls.__tns__,
in_protocol=Soap11(validator='lxml'),
out_protocol=Soap11(cleanup_namespaces=True)
)
return csrf_exempt(DjangoApplication(application))
shop_service = CandyShop.dispatch()
And result for get_my_bag
is like:
<tns:get_my_bagResult xmlns:tns="http://xml.candyshop.com/ns/shop/">
<ns0:bag xmlns:ns0="http://xml.candyshop.com/ns/candies/">
<ns0:candies>
<ns1:candy xmlns:ns1="None" flavor="choco"/>
</ns0:candies>
</ns0:bag>
</tns:get_my_bagResult>
But I want following:
<tns:get_my_bagResult xmlns:tns="http://xml.candyshop.com/ns/shop/">
<ns0:bag xmlns:ns0="http://xml.candyshop.com/ns/candies/">
<ns0:candies>
<ns0:specialCandy flavor="choco"/>
</ns0:candies>
</ns0:bag>
</tns:get_my_bagResult>
So, how to customize type name for array content without definning new subclass? I tried
complex.Array(CandyModel.customize(type_name='specialCandy'))
but this not works. Using of static alias
method gives an empty <ns0:candies/>
, maybe for that I'm still put CandyModel
instances to the candies
list, but this is my goal.
Second, why there is xmlns:ns1="None"
and how to fix it for ns0
?
BTW. Is there a way to customize namespace prefixes?
class Candies(complex.Array):
__namespace__ = 'http://xml.candyshop.com/ns/candies/'
and
candies = Candies(CandyModel)
solves problem with namespaces, but its a workaround rather than solution. I prefer inline customization or some of mixin with my namespaced ComplexModel
.
Why are you returning AnyXml instead of returning a proper object?
Here's your code refactored to return the XML document that you want:
import logging
logging.basicConfig(level=logging.DEBUG)
logging.getLogger('spyne.protocol.xml').setLevel(logging.DEBUG)
from spyne.decorator import rpc
from spyne.service import ServiceBase
from spyne.util import xml
from spyne.model import complex, primitive
from spyne.application import Application
from spyne.protocol.soap import Soap11
from spyne.server.wsgi import WsgiApplication
from lxml import etree
class ComplexModel(complex.ComplexModelBase):
__namespace__ = 'http://xml.candyshop.com/ns/candies/'
__metaclass__ = complex.ComplexModelMeta
class CandyModel(ComplexModel):
__type_name__ = 'candy'
flavor = complex.XmlAttribute(primitive.Unicode)
class BagModel(ComplexModel):
__type_name__ = 'bag'
candies = complex.Array(CandyModel)
class Bag(ComplexModel):
bag = BagModel
class CandyShop(ServiceBase):
__tns__ = 'http://xml.candyshop.com/ns/shop/'
@rpc(_returns=Bag)
def get_my_bag(ctx):
bag = BagModel()
bag.candies = [CandyModel(flavor='choco')]
return Bag(bag=bag)
@classmethod
def dispatch(cls):
application = Application([cls],
tns=cls.__tns__,
in_protocol=Soap11(validator='lxml'),
out_protocol=Soap11(cleanup_namespaces=True)
)
return application
# In case you need to extract parts of your response, you can use hooks:
def _on_method_return_document(ctx):
ns = ctx.app.interface.nsmap
elt = ctx.out_document.xpath('//s0:bag',namespaces=ns)[0]
output = etree.tostring(elt, pretty_print=True)
print output # do what you want with it.
CandyShop.event_manager.add_listener('method_return_document',
_on_method_return_document)
if __name__ == '__main__':
from wsgiref.simple_server import make_server
service_app = CandyShop.dispatch()
application = WsgiApplication(service_app)
server = make_server('0.0.0.0', 8080, application)
server.serve_forever()
Here's the output:
<?xml version='1.0' encoding='ASCII'?>
<senv:Envelope xmlns:tns="http://xml.candyshop.com/ns/shop/"
xmlns:s0="http://xml.candyshop.com/ns/candies/"
xmlns:senv="http://schemas.xmlsoap.org/soap/envelope/">
<senv:Body>
<tns:get_my_bagResponse>
<tns:get_my_bagResult>
<s0:bag>
<s0:candies>
<s0:candy flavor="choco"/>
</s0:candies>
</s0:bag>
</tns:get_my_bagResult>
</tns:get_my_bagResponse>
</senv:Body>
</senv:Envelope>
EDIT:
In case you need to extract parts of your response, you can use hooks. I've updated the code. There are also many event examples here: https://github.com/arskom/spyne/blob/master/examples/events.py