0

我有以下内容:

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

4

1 回答 1

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

于 2013-04-21T22:22:07.613 回答