覆盖数组内容中的命名空间

时间:2013-04-19 12:27:52

标签: soap python-2.7 lxml django-1.5 spyne

我有以下内容:

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()

get_my_bag的结果如下:

<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>

但我想跟随:

<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>

那么,如何在不定义新子类的情况下自定义数组内容的类型名称?我试过了     complex.Array(CandyModel.customize(type_name='specialCandy'))

但这不起作用。使用静态alias方法会得到一个空的<ns0:candies/>,也许是因为我仍然将CandyModel个实例放到candies列表中,但这是我的目标。

其次,为什么有xmlns:ns1="None"以及如何为ns0修复它?

顺便说一句。有没有办法自定义名称空间前缀?


修改

class Candies(complex.Array):
    __namespace__ = 'http://xml.candyshop.com/ns/candies/'

candies = Candies(CandyModel)

解决了命名空间的问题,但它是一种解决方法而不是解决方案。我更喜欢内联自定义或一些mixin与我的命名空间ComplexModel

1 个答案:

答案 0 :(得分:1)

为什么要返回AnyXml而不是返回正确的对象?

这是您重构的代码,用于返回您想要的XML文档:

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()

这是输出:

<?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>

修改

如果您需要提取部分答案,可以使用挂钩。我已经更新了代码。此处还有许多事件示例:https://github.com/arskom/spyne/blob/master/examples/events.py