Неверный XML, созданный SUDS
Я пытаюсь общаться с веб-службой SOAP, используя SUDS и Python. После большого количества возни с изучением Python (да, я новичок в этом) и разработкой, как использовать SUDS, я столкнулся с проблемой.
Подпись веб-метода, который я вызываю, согласно suds,
(FWTCaseCreate){
ClassificationEventCode = None
Priority = None
Title = None
Description = None
Queue = None
DueDate = None
AssociatedObject =
(FWTObjectBriefDetails){
ObjectID =
(FWTObjectID){
ObjectType = None
ObjectReference[] = <empty>
}
ObjectDescription = None
Details = None
Category = None
}
Form =
(FWTCaseForm){
FormField[] = <empty>
FormName = None
FormKey = None
}
Internal = None
InteractionID = None
XCoord = None
YCoord = None
}
Поэтому я использую SUDS для создания нужных мне классов и отправляю их в метод. Однако я получаю ошибку. Поэтому я включил вход в систему и вижу, что отправляемый XML-файл неверен, что вызывает ошибку десериализации. Пакет SOAP выглядит следующим образом
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:ns0="http://www.CRM.com/wsdl/FLTypes" xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<wsse:Security>
<wsse:BinarySecurityToken>eaadf1ddff99a8</wsse:BinarySecurityToken>
</wsse:Security>
</SOAP-ENV:Header>
<ns1:Body>
<ns0:FWTCaseCreate>
<ClassificationEventCode>
<ClassificationEventCode>2000023</ClassificationEventCode>
<Priority>1</Priority>
<Title>testing</Title>
<Description>testing</Description>
<Queue/>
<Internal>True</Internal>
<XCoord>356570</XCoord>
<YCoord>168708</YCoord>
</ClassificationEventCode>
</ns0:FWTCaseCreate>
</ns1:Body>
Как видите, вокруг всех остальных элементов есть элемент ClassificationEventCode, его там быть не должно. Если я вырезал и вставил этот xml в SOAPUI и сначала удалил этот элемент, а затем опубликовал его непосредственно в веб-сервисе, он успешно работает.
Вот код, который я использую, чтобы сделать звонок
client = Client(url)
#Add a header for the security
ssnns = ('wsse', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd')
ssn = Element('BinarySecurityToken', ns=ssnns).setText(binaryKey)
ssn1 = Element('Security',ns=ssnns)
ssn1.append(ssn)
client.set_options(soapheaders=ssn1)
newCase = client.factory.create('ns1:FWTCaseCreate')
classEventCode = client.factory.create('ns1:FWTEventCode')
classEventCode.value = 2000023
newCase.ClassificationEventCode = classEventCode
newCase.Priority = 1
#optional
newCase.AssociatedObject = None
#optional
newCase.Form = None
#optional
newCase.Internal = None
#optional
newCase.InteractionID = None
#optional
newCase.DueDate = None
#optional
newCase.Queue = None
newCase.Title = 'Title'
newCase.Description = 'description'
newCase.XCoord = '356570'
newCase.YCoord = '168708'
caseID = client.service.createCase(newCase)
У кого-нибудь есть идеи, почему это происходит? Я думаю, SUDS думает, что это должно быть там на основе WSDL.
Благодарю.
5 ответов
У меня была точно такая же проблема. Последовательность параметров в моем запросе SOAP заключена в элемент с тем же именем, что и первый параметр. например
....
<ns0:Body>
<ns1:CreationReq>
<ReqType>
<ReqType>1</ReqType>
<Title>Mr</Title>
....
</ReqType>
</ns1:CreationReq>
</ns0:Body>
....
Я проверил WSDL, чтобы убедиться, что с ним нет проблем.
Кажется, проблема в том, что я создал объект CreationReq, используя метод client.factory.create. Проверка клиента путем печати показывает, что метод, который я вызываю, не принимает этот объект в качестве параметра. Скорее, это займет список именованных аргументов.
Итак, мой код был:
req = client.factory.create('CreationReq')
req.ReqType = 1
req.Title = 'Mr'
resp = client.service.Create(req)
Теперь есть:
req = {}
req['ReqType'] = 1
req['Title'] = 'Mr'
resp = client.service.Create(**req)
Если вы создаете клиента для своих служб suds, вы можете увидеть некоторые атрибуты, чтобы определить, какие объекты необходимо передать в вызов службы.
Например:
import suds
client = suds.Client(url)
for a in client.sd: #print the whole service definition
print a
Это должно показать вам префиксы, порты с методами и типами. Для своего кода вы должны увидеть, что нужно передать в вызове сервиса для createCase. Несмотря на то, что WSDL может определять метод как требующий "FWTCaseCreate", suds может выбирать определение для createCase, чтобы ему требовались ClassificationEventCode, Priority, Title type и т. Д.
Поэтому вы не захотите делать: (который передает newCase для первого аргумента, помещая все детали под этим тегом)
newCase = client.factory.create('ns1:FWTCaseCreate')
caseID = client.service.createCase(newCase)
Но вместо этого вызывайте сервис следующим образом: (на основе определения сервиса)
newCase = client.factory.create('ns1:FWTCaseCreate')
caseID = client.service.createCase(newCase.ClassificationEventCode, newCase.Priority, ...)
Или, может быть:
newCase = client.factory.create('ns1:FWTCaseCreate')
caseID = client.service.createCase(*[getattr(newCase,a) for a in newCase.__keylist__])
Передача в список аргументов, которые необходимы для вызова службы.
Я не знаю, почему определение вызова службы неправильно разрешено как то, что оно есть, но передача правильного объекта не расширяется автоматически до нужного списка нужных аргументов. Возможно, чтение источника suds ( http://jortel.fedorapeople.org/suds/doc/) поможет обнародовать ответ.
Вы создали элемент дважды. Удали это:
classEventCode = client.factory.create('ns1:FWTEventCode')
classEventCode.value = 2000023
И измени это:
newcase.ClassificationEventCode = 2000023
Это должно удалить этот дополнительный тег.
Вы собираетесь использовать это в качестве файла конфигурации или для хранения информации. Или это для отправки данных через Интернет?
Хорошо, тогда, если это так, то почему бы не использовать json или json-rpc, они, как говорят, намного быстрее, легче разбираются и намного легче читаются. XML является гибким типом данных, и я лично не могу ждать, пока он умрет, если вы ищете отправку данных, то стоит использовать json.
Я нашел эту ветку в поисках решения той же проблемы. До сих пор я исследовал, что это происходит только тогда, когда вы передаете объект, созданный на заводе, непосредственно методу сервиса. И только с типами данных wsdl, использующими расширение (наследование).
Есть больше решений, о которых я мог бы подумать.
- вообще не используйте фабрику для типа верхнего уровня.
- написать плагин suds, изменяющий xml после генерации
- переписать wsdl, чтобы не использовать наследование (расширение тега)
- изменить тип объекта перед переходом в сервисный метод
Я выбрал последний, так как это самый простой способ. Итак, есть код.
def sudsToDict(data):
return dict([(str(key),val) for key,val in data])
Используйте как это.
data = client.factory.create('wsdl_data_type')
# now fill with values and then
data = sudsToDict(data)
client.service.some_method(**data)