要素数 - python 配列 要素



Sudsは空の要素を生成します。 それらを削除する方法? (5)

[メジャー編集は2日前の最初の投稿からの経験に基づいています。]

私はSudsを使ってPythonのSOAP / XMLスクリプトを作成していますが、サーバーに受け入れられるSOAP / XMLを生成するコードを手に入れるのに苦労しています。 私は問題はSudsが内部要素の接頭辞を生成していないことだと思っていましたが、その後Sh-DataMetaSwitchData要素が宣言しているように接頭辞の欠如( Sh-Dataと内部要素参照)は問題ではないことがMetaSwitchData適切な名前空間(下記参照)

<SOAP-ENV:Envelope xmlns:ns3="http://www.metaswitch.com/ems/soap/sh" xmlns:ns0="http://www.metaswitch.com/ems/soap/sh/userdata" xmlns:ns1="http://www.metaswitch.com/ems/soap/sh/servicedata" xmlns:ns2="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <ns2:Body>
      <ns3:ShUpdate>
         <ns3:UserIdentity>Meribel/TD Test Sub Gateway 3</ns3:UserIdentity>
         <ns3:DataReference>0</ns3:DataReference>
         <ns3:UserData>
            <Sh-Data xmlns="http://www.metaswitch.com/ems/soap/sh/userdata">
               <RepositoryData>
                  <ServiceIndication>Meta_SubG_BaseInformation</ServiceIndication>
                  <SequenceNumber>0</SequenceNumber>
                  <ServiceData>
                     <MetaSwitchData xmlns="http://www.metaswitch.com/ems/soap/sh/servicedata" IgnoreSequenceNumber="False" MetaSwitchVersion="?">
                        <Meta_SubG_BaseInformation Action="apply">
                           <NetworkElementName>Meribel</NetworkElementName>
                           <Description>TD Test Sub Gateway 3</Description>
                           <DomainName>test.datcon.co.uk</DomainName>
                           <MediaGatewayModel>Cisco ATA</MediaGatewayModel>
                           <CallFeatureServerControlStatus/>
                           <CallAgentControlStatus/>
                           <UseStaticNATMapping/>
                           <AuthenticationRequired/>
                           <ProviderStatus/>
                           <DeactivationMode/>
                        </Meta_SubG_BaseInformation>
                     </MetaSwitchData>
                  </ServiceData>
               </RepositoryData>
            </Sh-Data>
         </ns3:UserData>
         <ns3:OriginHost>[email protected]?clientVersion=7.3</ns3:OriginHost>
      </ns3:ShUpdate>
   </ns2:Body>
</SOAP-ENV:Envelope>

しかし、これはまだ失敗しています。 問題は、Sudsがオプション要素に対して空の要素を生成することです(WSDLではMandatory = Noとマークされています)。 しかし、サーバーではオプションの要素が<CallFeatureServerControlStatus/>な値で存在するか存在しないことを要求しており、次のエラーが発生します( <CallFeatureServerControlStatus/>要素は許容値の1つではないため)。

提供されたユーザーデータは、ユーザーデータのMetaSwitch XMLスキーマに対して検証されませんでした。
詳細:cvc-enumeration-valid:値 ''は列挙 'に関してファセット有効ではありません' [管理、放棄、慎重に管理]。 列挙からの値でなければなりません。

生成されたSOAP / XMLをSOAPUIに取り込んで空の要素を削除すれば、要求は正常に機能します。

Sudsにオプションのフィールドに空の要素を生成させないようにする方法、または後でそれらをコードから削除する方法はありますか。

メジャーアップデート

私はこの問題(私が他で見たことがある)を解決しましたが、かなり不正確な方法で。 それで私は私の現在の解決策を投稿していますa)それが他の人を助けることおよび/またはb)誰かがより良い回避策を提案できることを願って。

問題は、Sudsがオプション要素に対して空の要素を生成することではないことが判明しました(WSDLではMandatory = Noとマークされています)。 それどころか、そのSudsはオプションの複雑な要素に対して空の要素を生成します。 たとえば、次のMeta_SubG_BaseInformation要素は単純な要素であり、SudsはSOAP / XMLではそれらに対して何も生成しません。

<xs:element name="CMTS" type="xs:string" minOccurs="0">
    <xs:annotation>
        <xs:documentation>
            <d:DisplayName firstVersion="5.0" lastVersion="7.4">CMTS</d:DisplayName>
            <d:ValidFrom>5.0</d:ValidFrom>
            <d:ValidTo>7.4</d:ValidTo>
            <d:Type firstVersion="5.0" lastVersion="7.4">String</d:Type>
            <d:BaseAccess firstVersion="5.0" lastVersion="7.4">RWRWRW</d:BaseAccess>
            <d:Mandatory firstVersion="5.0" lastVersion="7.4">No</d:Mandatory>
            <d:MaxLength firstVersion="5.0" lastVersion="7.4">1024</d:MaxLength>
        </xs:documentation>
    </xs:annotation>
</xs:element>

<xs:element name="TAGLocation" type="xs:string" minOccurs="0">
    <xs:annotation>
        <xs:documentation>
            <d:DisplayName>Preferred location of Trunk Gateway</d:DisplayName>
            <d:Type>String</d:Type>
            <d:BaseAccess>RWRWRW</d:BaseAccess>
            <d:Mandatory>No</d:Mandatory>
            <d:DefaultValue>None</d:DefaultValue>
            <d:MaxLength>1024</d:MaxLength>
        </xs:documentation>
    </xs:annotation>
</xs:element>

これとは対照的に、次のMeta_SubG_BaseInformation要素は複雑な要素であり、それがオプションで私のコードが値を割り当てない場合でも、生成されたSOAP / XMLになります。

<xs:element name="ProviderStatus" type="tMeta_SubG_BaseInformation_ProviderStatus" minOccurs="0">
    <xs:annotation>
        <xs:documentation>
            <d:DisplayName>Provider status</d:DisplayName>
            <d:Type>Choice of values</d:Type>
            <d:BaseAccess>R-R-R-</d:BaseAccess>
            <d:Mandatory>No</d:Mandatory>
            <d:Values>
                <d:Value>Unavailable</d:Value>
                <d:Value>Available</d:Value>
                <d:Value>Inactive</d:Value>
                <d:Value>Active</d:Value>
                <d:Value>Out of service</d:Value>
                <d:Value>Quiescing</d:Value>
                <d:Value>Unconfigured</d:Value>
                <d:Value>Pending available</d:Value>
            </d:Values>
        </xs:documentation>
    </xs:annotation>
</xs:element>

SudsはProviderStatusに対して以下を生成します(上記の通り)。

<ProviderStatus/>

この問題を回避するには、次のように、親要素を作成した後、値を割り当てる前に、すべてのMeta_SubG_BaseInformation要素をNoneに設定します。 これは単純な要素には不要ですが、割り当てられていない複雑な要素によってSOAP / XMLが生成されないようにするためです。

subGatewayBaseInformation = client.factory.create('ns1:Meta_SubG_BaseInformation')
for (el) in subGatewayBaseInformation:
  subGatewayBaseInformation.__setitem__(el[0], None)
subGatewayBaseInformation._Action            = 'apply'
subGatewayBaseInformation.NetworkElementName = 'Meribel'
etc...

これにより、Sudは空の要素なしでSOAP / XMLを生成します。これは私のサーバーには受け入れられます。

しかし、同じ効果を達成するためのよりクリーンな方法を誰かが知っていますか?

以下の解決策は、下記のdusanとRoland Smithの両方からの回答/コメントに基づいています。

このソリューションではSuds MessagePluginを使用してSudsが要求を送信する前に<SubscriberType/>形式の "空の" XMLを整理します。 ShUpdates(サーバー上のデータを更新している場所)のみを整理する必要があります。ロジック(特に、サービス指示要素リストを取得するための子へのインデックス登録)は、WSDLに固有のものです。 異なるWSDLでは機能しません。

class MyPlugin(MessagePlugin):
  def marshalled(self, context):
    pruned = []
    req = context.envelope.children[1].children[0]
    if (req.name == 'ShUpdate'):
      si = req.children[2].children[0].children[0].children[2].children[0].children[0]
      for el in si.children:
        if re.match('<[a-zA-Z0-9]*/>', Element.plain(el)):
          pruned.append(el)
      for p in pruned:
        si.children.remove(p)

そして、クライアントを作成するときにプラグインを参照するだけです。

client = Client(url, plugins=[MyPlugin()])

Answer #1

Sudsファクトリメソッドは、WSDL型定義にマップする通常のpython属性を持つ通常のPythonオブジェクトを生成します。

あなたは属性を削除するために 'del'組み込み関数を使うことができます。

>>> order_details = c.factory.create('ns2:OrderDetails')
>>> order_details
(OrderDetails){
   Amount = None
   CurrencyCode = None
   OrderChannelType =
      (OrderChannelType){
         value = None
      }
   OrderDeliveryType =
      (OrderDeliveryType){
         value = None
      }
   OrderLines =
      (ArrayOfOrderLine){
         OrderLine[] = <empty>
      }
   OrderNo = None
   TotalOrderValue = None
 }
>>> del order_details.OrderLines
>>> del order_details.OrderDeliveryType
>>> del order_details.OrderChannelType
>>> order_details
(OrderDetails){
   Amount = None
   CurrencyCode = None
   OrderNo = None
   TotalOrderValue = None
 }

Answer #2

None値を持つ複雑な型をスキップするための次のMonkeyPatchについてどう思いますか?

from suds.mx.literal import Typed
old_skip = Typed.skip
def new_skip(self, content):
    x = old_skip(self, content)
    if not x and getattr(content.value, 'value', False) is None:
        x = True
    return x
Typed.skip = new_skip

Answer #3

サーバーに送信される前にXMLを変更するためにプラグインを使用できます(私の答えはRonald Smithのソリューションに基づいています)。

from suds.plugin import MessagePlugin
from suds.client import Client
import re

class MyPlugin(MessagePlugin):
    def sending(self, context):
        context.envelope = re.sub('\s+<.*?/>', '', context.envelope)


client = Client(URL_WSDL, plugins=[MyPlugin()])

documentation引用する:

MessagePluginは現在(5)フックを持っています。
(...)
送信()
メッセージテキストが送信される前に検査または変更する機会をプラグインに提供します。

基本的にSudsはXMLがsendingれる前にsendingを呼び出すので、生成されたXML( context.envelope含まれる)を修正することができます。 これを機能させるには、プラグインクラスMyPluginをClientコンストラクタに渡す必要があります。

編集する

もう1つの方法は、 marshalledを使用してXML構造を変更し、空の要素(未テストのコード)を削除することです。

class MyPlugin(MessagePlugin):
    def marshalled(self, context):
        #remove empty tags inside the Body element
        #context.envelope[0] is the SOAP-ENV:Header element
        context.envelope[1].prune()

Answer #4

私はこれがずっと前に閉じられたことを知っています、しかし個人的に問題に取り組んだ後に私は現在の答えが欠けているのを見つけます。

MessagePluginで送信方法を使用してもうまくいきません。なぜなら、ドキュメントに強く暗示されていることにもかかわらず、そこからメッセージ文字列を実際に変更することはできないからです。 あなたは最終結果だけを検索することができます。

前述のように、整列化された方法はXMLに影響を与えることができるため、これに最適です。 私は自分自身で問題を解決するために次のプラグインを作成しました:

class ClearEmpty(MessagePlugin):
    def clear_empty_tags(self, tags):
        for tag in tags:
            children = tag.getChildren()[:]
            if children:
                self.clear_empty_tags(children)
            if re.match(r'^<[^>]+?/>$', tag.plain()):
                tag.parent.remove(tag)

    def marshalled(self, context):
        self.clear_empty_tags(context.envelope.getChildren()[:])

これは空のタグをすべて削除します。 空のタグをある場所から削除するだけでよい場合は、必要に応じてこれを調整できます。ただし、この再帰関数は機能します(XMLスキーマがPythonの呼び出し深度よりもネストが大きくなるほど見栄えが悪い場合を除く)。問題を起こします。 ここでリストをコピーしているのは、remove()を使用すると、繰り返し処理中にリストが壊れて問題が発生するためです。

さらに注意すると、他の答えによって与えられた正規表現は悪い\s+<.*?/> <test> <thingus/> </test>使用される\s+<.*?/><test> <thingus/> </test>と一致します。ご<thingus/>だけではありません。 これは、 >がによって「任意の文字」と見なされるため. 。 レンダリングされたXMLでこの問題を解決するために本当に正規表現を使用する必要がある場合(注:XMLはレクサーによって処理される複雑な構文です)、正しい構文は<[^>]*/>ます。

そのタグのレンダリングされた出力を調べてそれに対して正規表現を行う以外に、「これはスタンドアロンの空のタグです」というレクサーに尋ねる最も正しい方法を私たちが理解できなかったので、ここでそれを使います。 そのような場合、 ^$トークンも追加しました。これは、このメソッドでタグをレンダリングするとコンテキスト全体がレンダリングされるためです。つまり、特定のタグの下にある空白のタグはすべて一致します。 特定のタグを一致させたいだけなので、それをツリーから削除するようにAPIに指示できます。

最後に、最初にこの質問を促したかもしれないものを探す人を助けるために、私がこのようなエラーメッセージを受け取ったとき、問題は私のために思いつきました:

cvc-enumeration-valid: Value '' is not facet-valid with respect to enumeration

これは、空のタグによってサーバーがそのタグの下にあるすべてのものをヌル値/空の文字列として解釈するためです。


Answer #5

空の要素を正規表現で除外することができます。

XMLデータが文字列xmltextます。

import re
filteredtext = re.sub('\s+<.*?/>', '', xmltext)




suds