[Основное правление основано на опыте с 1-го поста два дня назад.]
Я создаю Python SOAP/XML script с помощью Suds, но изо всех сил пытаюсь получить код для создания SOAP/XML, приемлемого для сервера. Я думал, что проблема в том, что Suds не генерировал префиксы для внутренних элементов, но впоследствии выясняется, что отсутствие префиксов (см. Sh-Data
и внутренние элементы) не является проблемой, так как 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 генерирует пустые элементы для необязательных элементов (помеченных как Mandatory = No
в WSDL). Но сервер требует, чтобы дополнительный элемент присутствовал с разумным значением или отсутствовал, и я получаю следующую ошибку (поскольку элемент <CallFeatureServerControlStatus/>
не является одним из допустимых значений.
Предоставленные пользовательские данные не подтверждают соответствие XML-схемы MetaSwitch для пользовательских данных.
Подробности: cvc-enumeration-valid: Значение '' не является фасетным в отношении перечисления '[Controlling, Abandoned, Caututionly control]'. Это должно быть значение из перечисления.
Если я возьму сгенерированный SOAP/XML в SOAPUI и удалю пустые элементы, запрос будет работать нормально.
Есть ли способ заставить Suds либо не генерировать пустые элементы для необязательных полей, либо для меня впоследствии удалить их в код?
Основное обновление
Я решил эту проблему (которую я видел в другом месте), но довольно неэлегантно. Поэтому я отправляю свое текущее решение в надежде, что: а) он помогает другим и/или б) кто-то может предложить лучшую работу.
Оказывается, проблема заключалась не в том, что Suds генерирует пустые элементы для необязательных элементов (помеченных как Mandatory = No
в WSDL). Но скорее, что 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...
Это приводит к тому, что Suds генерирует SOAP/XML без пустых элементов, что приемлемо для моего сервера.
Но кто-нибудь знает о более чистом способе достижения такого же эффекта?
Нижеприведенное решение основано на ответах/комментариях как Дусана, так и Роланда Смита ниже.
Это решение использует Suds MessagePlugin, чтобы обрезать "пустой" XML формы <SubscriberType/>
, прежде чем Suds отправит запрос на провод. Нам нужно только обрезать 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()])