我正在使用xmltodict库解析SOAP响应。
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns2:MultiAvailabilityResponse xmlns:ns2="http://www.derbysoft.com/doorway" Status="Successful" Token="187be58c62c2f2515b5d78ee">
<ns2:Availabilities>
<ns2:Availability CurrencyCode="GBP" HotelCode="HY-LONGE">
<ns2:GuestCount AdultCount="1" ChildCount="0"/>
<ns2:RoomTypes>
<ns2:RoomType RoomTypeCode="KING" RoomTypeName="Andaz King">
<ns2:RoomTypeDescription>A 29-square-metre room ,Modern furnishings include oversized work desk, plus bathroom with fast-fill tub and heated towel rail.</ns2:RoomTypeDescription>
</ns2:RoomType>
<ns2:RoomType RoomTypeCode="TWIN" RoomTypeName="Andaz Twin">
<ns2:RoomTypeDescription>A 29-square-metre room ,Modern furnishings include oversized work desk, plus bathroom with fast-fill tub and heated towel rail.</ns2:RoomTypeDescription>
</ns2:RoomType>
<ns2:RoomType RoomTypeCode="QUEN" RoomTypeName="Andaz Queen">
<ns2:RoomTypeDescription>A 26-square-metre room ,Modern furnishings include oversized work desk, plus bathroom with fast-fill tub and heated towel rail.</ns2:RoomTypeDescription>
</ns2:RoomType>
</ns2:RoomTypes>
<ns2:RatePlans>
<ns2:RatePlan RatePlanCode="49584WADPF2" RatePlanName="Advance Purchase">
<ns2:RatePlanDescription>Advance Purchase</ns2:RatePlanDescription>
</ns2:RatePlan>
<ns2:RatePlan RatePlanCode="49584WADPF" RatePlanName="Advance Purchase">
<ns2:RatePlanDescription>Advance Purchase</ns2:RatePlanDescription>
</ns2:RatePlan>
<ns2:RatePlan RatePlanCode="49584IPRTF" RatePlanName="Partner Rate">
<ns2:RatePlanDescription>Partner Rate</ns2:RatePlanDescription>
</ns2:RatePlan>
</ns2:RatePlans>
<ns2:RoomRates>
<ns2:RoomRate RatePlanCode="49584WADPF2" RoomTypeCode="KING">
<ns2:Rates>
<ns2:Rate AmountAfterTax="249.900" AmountBeforeTax="249.900" EffectiveDate="2016-05-05" ExpireDate="2016-05-06"/>
</ns2:Rates>
<ns2:Fees>
<ns2:Fee Amount="0.000" ChargeType="Tax" Type="Exclusive" Unit="PER_ROOM_PER_NIGHT"/>
</ns2:Fees>
</ns2:RoomRate>
<ns2:RoomRate RatePlanCode="49584WADPF2" RoomTypeCode="TWIN">
<ns2:Rates>
<ns2:Rate AmountAfterTax="249.900" AmountBeforeTax="249.900" EffectiveDate="2016-05-05" ExpireDate="2016-05-06"/>
</ns2:Rates>
<ns2:Fees>
<ns2:Fee Amount="0.000" ChargeType="Tax" Type="Exclusive" Unit="PER_ROOM_PER_NIGHT"/>
</ns2:Fees>
</ns2:RoomRate>
<ns2:RoomRate RatePlanCode="49584WADPF" RoomTypeCode="QUEN">
<ns2:Rates>
<ns2:Rate AmountAfterTax="249.900" AmountBeforeTax="249.900" EffectiveDate="2016-05-05" ExpireDate="2016-05-06"/>
</ns2:Rates>
<ns2:Fees>
<ns2:Fee Amount="0.000" ChargeType="Tax" Type="Exclusive" Unit="PER_ROOM_PER_NIGHT"/>
</ns2:Fees>
</ns2:RoomRate>
</ns2:RoomRates>
</ns2:Availability>
<ns2:Availability CurrencyCode="USD" HotelCode="HY-CHIRC">
<ns2:GuestCount AdultCount="1" ChildCount="0"/>
<ns2:RoomTypes>
<ns2:RoomType RoomTypeCode="JRSQ" RoomTypeName="JR SUITE 2 QUEEN BEDS">
<ns2:RoomTypeDescription>Rest in sublime comfort on one of two queen signature Hyatt Grand Beds®, fitted with fine linens, down blanket and plump pillows.</ns2:RoomTypeDescription>
</ns2:RoomType>
<ns2:RoomType RoomTypeCode="CLBD" RoomTypeName="REG CLUB 2 DOUBLE BEDS">
<ns2:RoomTypeDescription>one King or two double-sized Hyatt Grand Beds, fitted with luxurious linens, a down blanket and plush pillows</ns2:RoomTypeDescription>
</ns2:RoomType>
</ns2:RoomTypes>
<ns2:RatePlans>
<ns2:RatePlan RatePlanCode="49584IPRTF" RatePlanName="Partner Rate">
<ns2:RatePlanDescription>Partner Rate</ns2:RatePlanDescription>
<ns2:CancelPolicy NonRefundable="true">
<ns2:CancelPenalties/>
</ns2:CancelPolicy>
</ns2:RatePlan>
<ns2:RatePlan RatePlanCode="49584WPAWAF" RatePlanName="Bed and Breakfast">
<ns2:RatePlanDescription>Bed and Breakfast</ns2:RatePlanDescription>
<ns2:CancelPolicy NonRefundable="true">
<ns2:CancelPenalties/>
</ns2:CancelPolicy>
</ns2:RatePlan>
</ns2:RatePlans>
<ns2:RoomRates>
<ns2:RoomRate RatePlanCode="49584IPRTF" RoomTypeCode="JRSQ">
<ns2:Rates>
<ns2:Rate AmountAfterTax="543.134" AmountBeforeTax="466.650" EffectiveDate="2016-05-05" ExpireDate="2016-05-06"/>
</ns2:Rates>
<ns2:Fees>
<ns2:Fee ChargeType="Tax" Percent="16.390" Type="Exclusive"/>
</ns2:Fees>
</ns2:RoomRate>
<ns2:RoomRate RatePlanCode="49584IPRTF" RoomTypeCode="CLBD">
<ns2:Rates>
<ns2:Rate AmountAfterTax="370.004" AmountBeforeTax="317.900" EffectiveDate="2016-05-05" ExpireDate="2016-05-06"/>
</ns2:Rates>
<ns2:Fees>
<ns2:Fee ChargeType="Tax" Percent="16.390" Type="Exclusive"/>
</ns2:Fees>
</ns2:RoomRate>
<ns2:RoomRate RatePlanCode="49584IPRTF" RoomTypeCode="VW2Q">
<ns2:Rates>
<ns2:Rate AmountAfterTax="325.485" AmountBeforeTax="279.650" EffectiveDate="2016-05-05" ExpireDate="2016-05-06"/>
</ns2:Rates>
<ns2:Fees>
<ns2:Fee ChargeType="Tax" Percent="16.390" Type="Exclusive"/>
</ns2:Fees>
</ns2:RoomRate>
</ns2:RoomRates>
</ns2:Availability>
</ns2:Availabilities>
</ns2:MultiAvailabilityResponse>
</SOAP-ENV:Body>
以下是我从SOAP响应中获取所需详细信息的代码:
def listify(obj):
"""To convert each element of SOAP into a list, so processing of response would be easy."""
if isinstance(obj, list):
return obj
return [obj]
def search_hotels_formatted_response(soap):
"""Parse the response."""
soap = xmltodict.parse(soap, process_namespaces=True)
# Deal with namespaces
env = 'http://schemas.xmlsoap.org/soap/envelope/:'
doorway = 'http://www.derbysoft.com/doorway:'
availability = listify(
soap[env + 'Envelope']
[env + 'Body']
[doorway + 'MultiAvailabilityResponse']
[doorway + 'Availabilities']
[doorway + 'Availability'])
# Intermediate data structure to hold room names
names = {
roomtype['@RoomTypeCode']: roomtype['@RoomTypeName']
for _availability in availability
for roomtype in listify(_availability
[doorway + 'RoomTypes']
[doorway + 'RoomType'])
}
return_dict = {
'ibp': 'dbs',
'rL': sorted([
{
'rtc': roomrate['@RoomTypeCode'],
'rpc': roomrate['@RatePlanCode'],
'rtn': names[roomrate['@RoomTypeCode']],
'rmt': rate['@AmountBeforeTax'],
'cur': _availability['@CurrencyCode'],
'ttc': float(rate['@AmountAfterTax']) - float(rate['@AmountBeforeTax']),
'egc': 0,
'long': ''
}
for _availability in availability
for roomrate in listify(_availability
[doorway + 'RoomRates']
[doorway + 'RoomRate'])
for rate in listify(roomrate
[doorway + 'Rates']
[doorway + 'Rate'])],
key=lambda x: float(x['rmt'])),
'hc': _availability['@HotelCode'],
'hn': ''
}
return return_dict
我能够将SOAP消息中所需的详细信息转换为所需的Dictionary格式。但是,我在获取上面共享的此特定SOAP消息的详细信息时遇到了问题。在所有其他情况下,我只是得到了具有不同组合数量的单个酒店的响应,但在这个SOAP响应中我有两个酒店,我的解析代码同时从两个酒店获取房间的价格并对其进行排序。但是,我想在第一家酒店选择排序,然后在第二家酒店选择。酒店的数量可能会有所不同,因此尽量提供通用的解决方案,无论酒店数量多少都可以使用。
任何帮助都会受到重视。谢谢!
答案 0 :(得分:1)
考虑使用XSLT简化XML,这是专门用于转换XML文档的声明性编程语言。 Python可以使用lxml
模块运行XSLT 1.0。从那里将变换后的树解析为带有xmltodict
的字典,可以进一步解析为json
。所有三个输出都包含在下面:
XSLT 脚本(另存为.xsl文件,在.py脚本中引用)
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns2="http://www.derbysoft.com/doorway">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<!-- Response Level -->
<xsl:template match="ns2:MultiAvailabilityResponse">
<Responses>
<xsl:apply-templates select="ns2:Availabilities"/>
</Responses>
</xsl:template>
<xsl:template match="ns2:Availabilities">
<xsl:apply-templates select="ns2:Availability">
<xsl:sort select="@CurrencyCode" order="descending"/>
</xsl:apply-templates>
</xsl:template>
<!-- Hotel Level -->
<xsl:template match="ns2:Availability">
<Hotel>
<xsl:apply-templates select="ns2:RoomRates"/>
</Hotel>
</xsl:template>
<!-- Room Rates Level -->
<xsl:template match="ns2:RoomRates">
<xsl:apply-templates select="ns2:RoomRate">
<xsl:sort select="descendant::ns2:Rate/@AmountBeforeTax"
order="ascending" data-type="number"/>
</xsl:apply-templates>
</xsl:template>
<!-- Room Rate Level -->
<xsl:template match="ns2:RoomRate">
<RoomRate>
<ibp>dbs</ibp>
<rL><xsl:apply-templates select="ns2:Rates"/></rL>
<hc><xsl:value-of select="ancestor::ns2:Availability/@HotelCode"/></hc>
<hn></hn>
</RoomRate>
</xsl:template>
<!-- Rates Level -->
<xsl:template match="ns2:Rates/*">
<xsl:variable name="rtc" select="ancestor::ns2:RoomRate/@RoomTypeCode"/>
<rtc><xsl:value-of select="$rtc"/></rtc>
<rpc><xsl:value-of select="ancestor::ns2:RoomRate/@RatePlanCode"/></rpc>
<rtn><xsl:value-of select="ancestor::ns2:Availability/ns2:RoomTypes/
ns2:RoomType[@RoomTypeCode=$rtc]/@RoomTypeName"/></rtn>
<rmt><xsl:value-of select="@AmountBeforeTax"/></rmt>
<cur><xsl:value-of select="ancestor::ns2:Availability/@CurrencyCode"/></cur>
<ttc><xsl:value-of select="@AmountAfterTax - @AmountBeforeTax"/></ttc>
<egc>0</egc>
<long></long>
</xsl:template>
</xsl:transform>
Python 脚本(输出到xml,dict和json类型)
import lxml.etree as ET
import xmltodict
import json
# LOAD XML AND XSL
dom = ET.parse('soap.xml')
xslt = ET.parse('XSLTScript.xsl')
# TRANSFORM
transform = ET.XSLT(xslt)
newdom = transform(dom)
# NEW TREE OUTPUT
return_xml = ET.tostring(newdom, encoding='UTF-8', pretty_print=True, xml_declaration=True)
print(return_xml.decode("utf-8"))
return_dict = xmltodict.parse(return_xml)
print(return_dict)
return_json = json.dumps(return_dict, indent=4)
print(return_json)
转换的XML输出
<?xml version='1.0' encoding='UTF-8'?>
<Responses xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns2="http://www.derbysoft.com/doorway">
<Hotel>
<RoomRate>
<ibp>dbs</ibp>
<rL>
<rtc>VW2Q</rtc>
<rpc>49584IPRTF</rpc>
<rtn/>
<rmt>279.650</rmt>
<cur>USD</cur>
<ttc>45.835</ttc>
<egc>0</egc>
<long/>
</rL>
<hc>HY-CHIRC</hc>
<hn/>
</RoomRate>
<RoomRate>
<ibp>dbs</ibp>
<rL>
<rtc>CLBD</rtc>
<rpc>49584IPRTF</rpc>
<rtn>REG CLUB 2 DOUBLE BEDS</rtn>
<rmt>317.900</rmt>
<cur>USD</cur>
<ttc>52.104</ttc>
<egc>0</egc>
<long/>
</rL>
<hc>HY-CHIRC</hc>
<hn/>
</RoomRate>
<RoomRate>
<ibp>dbs</ibp>
<rL>
<rtc>JRSQ</rtc>
<rpc>49584IPRTF</rpc>
<rtn>JR SUITE 2 QUEEN BEDS</rtn>
<rmt>466.650</rmt>
<cur>USD</cur>
<ttc>76.484</ttc>
<egc>0</egc>
<long/>
</rL>
<hc>HY-CHIRC</hc>
<hn/>
</RoomRate>
</Hotel>
<Hotel>
<RoomRate>
<ibp>dbs</ibp>
<rL>
<rtc>KING</rtc>
<rpc>49584WADPF2</rpc>
<rtn>Andaz King</rtn>
<rmt>249.900</rmt>
<cur>GBP</cur>
<ttc>0</ttc>
<egc>0</egc>
<long/>
</rL>
<hc>HY-LONGE</hc>
<hn/>
</RoomRate>
<RoomRate>
<ibp>dbs</ibp>
<rL>
<rtc>TWIN</rtc>
<rpc>49584WADPF2</rpc>
<rtn>Andaz Twin</rtn>
<rmt>249.900</rmt>
<cur>GBP</cur>
<ttc>0</ttc>
<egc>0</egc>
<long/>
</rL>
<hc>HY-LONGE</hc>
<hn/>
</RoomRate>
<RoomRate>
<ibp>dbs</ibp>
<rL>
<rtc>QUEEN</rtc>
<rpc>49584WADPF</rpc>
<rtn>Andaz Queen</rtn>
<rmt>249.900</rmt>
<cur>GBP</cur>
<ttc>0</ttc>
<egc>0</egc>
<long/>
</rL>
<hc>HY-LONGE</hc>
<hn/>
</RoomRate>
</Hotel>
</Responses>
Python Dictionary
OrderedDict([('Responses', OrderedDict([('@xmlns:SOAP-ENV', 'http://schemas.xmlsoap.org/soap/envelope/'),
('@xmlns:ns2', 'http://www.derbysoft.com/doorway'), ('Hotel', [OrderedDict([('RoomRate', [OrderedDict([('ibp', 'dbs'),
('rL', OrderedDict([('rtc', 'VW2Q'), ('rpc', '49584IPRTF'), ('rtn', None), ('rmt', '279.650'), ('cur', 'USD'),
('ttc', '45.835'), ('egc', '0'), ('long', None)])), ('hc', 'HY-CHIRC'), ('hn', None)]), OrderedDict([('ibp', 'dbs'),
('rL', OrderedDict([('rtc', 'CLBD'), ('rpc', '49584IPRTF'), ('rtn', 'REG CLUB 2 DOUBLE BEDS'), ('rmt', '317.900'),
('cur', 'USD'), ('ttc', '52.104'), ('egc', '0'), ('long', None)])), ('hc', 'HY-CHIRC'), ('hn', None)]),
OrderedDict([('ibp', 'dbs'), ('rL', OrderedDict([('rtc', 'JRSQ'), ('rpc', '49584IPRTF'), ('rtn', 'JR SUITE 2 QUEEN BEDS'),
('rmt', '466.650'), ('cur', 'USD'), ('ttc', '76.484'), ('egc', '0'), ('long', None)])), ('hc', 'HY-CHIRC'),
('hn', None)])])]), OrderedDict([('RoomRate', [OrderedDict([('ibp', 'dbs'), ('rL', OrderedDict([('rtc', 'KING'),
('rpc', '49584WADPF2'), ('rtn', 'Andaz King'), ('rmt', '249.900'), ('cur', 'GBP'), ('ttc', '0'), ('egc', '0'),
('long', None)])), ('hc', 'HY-LONGE'), ('hn', None)]), OrderedDict([('ibp', 'dbs'), ('rL', OrderedDict([('rtc', 'TWIN'),
('rpc', '49584WADPF2'), ('rtn', 'Andaz Twin'), ('rmt', '249.900'), ('cur', 'GBP'), ('ttc', '0'), ('egc', '0'),
('long', None)])), ('hc', 'HY-LONGE'), ('hn', None)]), OrderedDict([('ibp', 'dbs'), ('rL', OrderedDict([('rtc', 'QUEEN'),
('rpc', '49584WADPF'), ('rtn', 'Andaz Queen'), ('rmt', '249.900'), ('cur', 'GBP'), ('ttc', '0'), ('egc', '0'),
('long', None)])), ('hc', 'HY-LONGE'), ('hn', None)])])])])]))])
JSON输出
{
"Responses": {
"@xmlns:SOAP-ENV": "http://schemas.xmlsoap.org/soap/envelope/",
"@xmlns:ns2": "http://www.derbysoft.com/doorway",
"Hotel": [
{
"RoomRate": [
{
"ibp": "dbs",
"rL": {
"rtc": "VW2Q",
"rpc": "49584IPRTF",
"rtn": null,
"rmt": "279.650",
"cur": "USD",
"ttc": "45.835",
"egc": "0",
"long": null
},
"hc": "HY-CHIRC",
"hn": null
},
{
"ibp": "dbs",
"rL": {
"rtc": "CLBD",
"rpc": "49584IPRTF",
"rtn": "REG CLUB 2 DOUBLE BEDS",
"rmt": "317.900",
"cur": "USD",
"ttc": "52.104",
"egc": "0",
"long": null
},
"hc": "HY-CHIRC",
"hn": null
},
{
"ibp": "dbs",
"rL": {
"rtc": "JRSQ",
"rpc": "49584IPRTF",
"rtn": "JR SUITE 2 QUEEN BEDS",
"rmt": "466.650",
"cur": "USD",
"ttc": "76.484",
"egc": "0",
"long": null
},
"hc": "HY-CHIRC",
"hn": null
}
]
},
{
"RoomRate": [
{
"ibp": "dbs",
"rL": {
"rtc": "KING",
"rpc": "49584WADPF2",
"rtn": "Andaz King",
"rmt": "249.900",
"cur": "GBP",
"ttc": "0",
"egc": "0",
"long": null
},
"hc": "HY-LONGE",
"hn": null
},
{
"ibp": "dbs",
"rL": {
"rtc": "TWIN",
"rpc": "49584WADPF2",
"rtn": "Andaz Twin",
"rmt": "249.900",
"cur": "GBP",
"ttc": "0",
"egc": "0",
"long": null
},
"hc": "HY-LONGE",
"hn": null
},
{
"ibp": "dbs",
"rL": {
"rtc": "QUEEN",
"rpc": "49584WADPF",
"rtn": "Andaz Queen",
"rmt": "249.900",
"cur": "GBP",
"ttc": "0",
"egc": "0",
"long": null
},
"hc": "HY-LONGE",
"hn": null
}
]
}
]
}
}
答案 1 :(得分:0)
您正在从所有房价结果(尽管所有酒店)创建列表理解,但酒店代码不在任何类型的循环之内,与列表位于同一级别。 所以,你总会得到一家(最后一家)酒店。
在代码中:
'hc': _availability['@HotelCode'],
变量_可用性是前一循环的最后一次出现&#34;对于可用性的#availability&#34;。循环是正常的&#39;对于块,它甚至不会在那里定义。
在我看来,这种复杂的数据结构非常难以阅读和使用,也许您应该考虑OOP方法与对象属性中的数据。
如果你坚持使用这样的数据结构,你可以这样做:
def search_hotels_formatted_response(soap):
"""Parse the response."""
soap = xmltodict.parse(soap, process_namespaces=True)
# Deal with namespaces
env = 'http://schemas.xmlsoap.org/soap/envelope/:'
doorway = 'http://www.derbysoft.com/doorway:'
availability = listify(
soap[env + 'Envelope']
[env + 'Body']
[doorway + 'MultiAvailabilityResponse']
[doorway + 'Availabilities']
[doorway + 'Availability'])
# Intermediate data structure to hold room names
names = {
roomtype['@RoomTypeCode']: roomtype['@RoomTypeName']
for _availability in availability
for roomtype in listify(_availability
[doorway + 'RoomTypes']
[doorway + 'RoomType'])
}
# Intermediate data structure to hold rates
rates = {
rate['@RoomTypeCode']: rate
for _availability in availability
for rate in listify(_availability
[doorway + 'RoomRates']
[doorway + 'RoomRate'])
}
return_list = []
for _availability in availability:
return_list.append({
'ibp': 'dbs',
'rL': sorted([
{
'rtc': rates[roomtype['@RoomTypeCode']]['@RoomTypeCode'],
'rpc': rates[roomtype['@RoomTypeCode']]['@RatePlanCode'],
'rtn': names[rates[roomtype['@RoomTypeCode']]['@RoomTypeCode']],
'rmt': rates[roomtype['@RoomTypeCode']][doorway + 'Rates'][doorway + 'Rate']['@AmountBeforeTax'],
'cur': _availability['@CurrencyCode'],
'ttc': float(rates[roomtype['@RoomTypeCode']][doorway + 'Rates'][doorway + 'Rate']['@AmountAfterTax']) - float(rates[roomtype['@RoomTypeCode']][doorway + 'Rates'][doorway + 'Rate']['@AmountBeforeTax']),
'egc': 0,
'long': '',
}
for roomtype in listify(_availability
[doorway + 'RoomTypes']
[doorway + 'RoomType'])],
key=lambda x: float(x['rmt'])),
'hc': _availability['@HotelCode'],
'hn': ''
})
return return_list