0

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?


EDIT

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.

kbec
  • 3,415
  • 3
  • 27
  • 42
  • Please split your questions, there's already three here, i'm answering only the one about extracting parts of your return document. – Burak Arslan Apr 23 '13 at 11:18
  • @BurakArslan I'm put new question about [name customization](http://stackoverflow.com/q/16187055/854291) specific for spyne 2.10. I hope your answer for that resolve my problem with naming in this unsplitted question. – kbec Apr 24 '13 at 08:30

1 Answers1

1

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

Burak Arslan
  • 7,671
  • 2
  • 15
  • 24
  • I want to extract returned xml to own file without declaring its variable structure. This file has special meaning in my app and I can't hardcode its schema in wsdl. – kbec Apr 22 '13 at 13:14