我试图在一个groovy脚本中使用XmlHolder从SoapUI中的XML响应中提取值。但xpath并没有像我期望的那样工作。
这是我要查询的回复:
<OTA_HotelRatePlanNotifRQ MessageContentCode="8" TimeStamp="2016-03-02T21:08:10.912Z" Version="6" CorrelationID="aut0mat3-th15-g00d-n1c3-ch01c37udsvb" xmlns="http://www.opentravel.org/OTA/2003/05">
<POS>
<Source>
<RequestorID Type="10" ID="ACME"/>
<BookingChannel Type="4">
<CompanyName Code="ACME"/>
</BookingChannel>
</Source>
</POS>
<UniqueID Type="10" ID_Context="HI123" ID="HI123"/>
<RatePlans ChainCode="EC" HotelCode="HI123">
<RatePlan RatePlanNotifType="Delta" RatePlanNotifScopeType="RateOnly" CurrencyCode="USD" RatePlanCode="COOL">
<Rates>
<Rate InvCode="QQQ" InvTypeCode="QQQ" Mon="false" Tue="false" Weds="false" Thur="false" Fri="true" Sat="true" Sun="true" Start="2016-03-03" End="2016-04-02">
<BaseByGuestAmts>
<BaseByGuestAmt NumberOfGuests="1" AgeQualifyingCode="10" AmountBeforeTax="1099.0"/>
<BaseByGuestAmt NumberOfGuests="2" AgeQualifyingCode="10" AmountBeforeTax="1199.0"/>
</BaseByGuestAmts>
</Rate>
<Rate InvCode="QQQ" InvTypeCode="QQQ" Mon="true" Tue="true" Weds="true" Thur="true" Fri="false" Sat="false" Sun="false" Start="2016-03-03" End="2016-04-02">
<BaseByGuestAmts>
<BaseByGuestAmt NumberOfGuests="1" AgeQualifyingCode="10" AmountBeforeTax="899.0"/>
<BaseByGuestAmt NumberOfGuests="2" AgeQualifyingCode="10" AmountBeforeTax="999.0"/>
</BaseByGuestAmts>
</Rate>
</Rates>
</RatePlan>
</RatePlans>
这是我的Groovy脚本中的代码:
import com.eviware.soapui.support.XmlHolder;
XmlHolder holder = new XmlHolder(context.getProperty("response"))
// These work as expected
log.info(holder.getNodeValue("//@MessageContentCode")); // 8
log.info(holder.getNodeValue("//@Version")); // 6
log.info(holder.getNodeValue("//@Type")); // 10
log.info(holder.getNodeValue("//@ID")); // ACME
log.info(holder.getNodeValue("//@Code")); // ACME
// These do NOT work as expected. Instead they log null.
log.info(holder.getNodeValue("/OTA_HotelRatePlanNotifRQ/@Version")); // null
log.info(holder.getNodeValue("/OTA_HotelRatePlanNotifRQ/POS/Source/RequestorID/@Type")); // null
//etc
我在这里做错了吗?
答案 0 :(得分:2)
这是因为响应中使用了默认命名空间,就像您预期的那样。
但是,它的处理方式与答案中提到的不同。
这适用于以下两个条件
在为XmlHolder
定义对象后,必须设置查询数据所需的所有命名空间。而下面的语句也是如此,完整的脚本也很好。
如何定义/设置命名空间
在您的回复中,只使用了一个命名空间,即http://www.opentravel.org/OTA/2003/05
。
设置命名空间映射,你可以在这里自由定义你自己的前缀,不用担心它在原始响应中有什么前缀(考虑上面提到的第二种情况)并使用这个前缀(&#39; ns&# 39;)使用xpath
获取数据时。
holder.declareNamespace('ns',"http://www.opentravel.org/OTA/2003/05")
Groovy脚本
import com.eviware.soapui.support.XmlHolder
def xml = '''<OTA_HotelRatePlanNotifRQ MessageContentCode="8" TimeStamp="2016-03-02T21:08:10.912Z" Version="6" CorrelationID="aut0mat3-th15-g00d-n1c3-ch01c37udsvb" xmlns="http://www.opentravel.org/OTA/2003/05">
<POS>
<Source>
<RequestorID Type="10" ID="ACME"/>
<BookingChannel Type="4">
<CompanyName Code="ACME"/>
</BookingChannel>
</Source>
</POS>
<UniqueID Type="10" ID_Context="HI123" ID="HI123"/>
<RatePlans ChainCode="EC" HotelCode="HI123">
<RatePlan RatePlanNotifType="Delta" RatePlanNotifScopeType="RateOnly" CurrencyCode="USD" RatePlanCode="COOL">
<Rates>
<Rate InvCode="QQQ" InvTypeCode="QQQ" Mon="false" Tue="false" Weds="false" Thur="false" Fri="true" Sat="true" Sun="true" Start="2016-03-03" End="2016-04-02">
<BaseByGuestAmts>
<BaseByGuestAmt NumberOfGuests="1" AgeQualifyingCode="10" AmountBeforeTax="1099.0"/>
<BaseByGuestAmt NumberOfGuests="2" AgeQualifyingCode="10" AmountBeforeTax="1199.0"/>
</BaseByGuestAmts>
</Rate>
<Rate InvCode="QQQ" InvTypeCode="QQQ" Mon="true" Tue="true" Weds="true" Thur="true" Fri="false" Sat="false" Sun="false" Start="2016-03-03" End="2016-04-02">
<BaseByGuestAmts>
<BaseByGuestAmt NumberOfGuests="1" AgeQualifyingCode="10" AmountBeforeTax="899.0"/>
<BaseByGuestAmt NumberOfGuests="2" AgeQualifyingCode="10" AmountBeforeTax="999.0"/>
</BaseByGuestAmts>
</Rate>
</Rates>
</RatePlan>
</RatePlans>
</OTA_HotelRatePlanNotifRQ>'''
def holder = new XmlHolder(xml)
//set the namespace, add more where there are more namespaces
holder.declareNamespace('ns',"http://www.opentravel.org/OTA/2003/05")
//and use the above defined prefix while doing xpath to get the data
log.info holder.getNodeValue('//ns:OTA_HotelRatePlanNotifRQ/@Version')
log.info(holder.getNodeValue("//ns:OTA_HotelRatePlanNotifRQ/ns:POS/ns:Source/ns:RequestorID/@Type"))
如果感兴趣,可以找到有关命名空间here的更多信息。
Xml以下,包含多个namcespaces,包括默认值。
从上面的链接中取这个样本。添加以下详细信息,因为它可以清除并有助于更好地了解命名空间。
<Company xmlns="http://www.company.org" xmlns:pro="http://www.product.org" xmlns:per="http://www.person.org" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.company.org Company.xsd">
<Person>
<per:Name>John Doe</per:Name>
<per:SSN>123-45-6789</per:SSN>
</Person>
<Product>
<pro:Type>Widget</pro:Type>
</Product>
</Company>
如果您注意到上述内容:
per
pro
修复。在下面的脚本示例中,将定义具有不同前缀或相同前缀的命名空间(仅用于说明用户在执行xpath时可以自由使用不同的前缀)
具有多个命名空间的Groovy脚本
import com.eviware.soapui.support.XmlHolder
def xml = '''
<Company xmlns="http://www.company.org" xmlns:pro="http://www.product.org" xmlns:per="http://www.person.org" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.company.org Company.xsd">
<Person>
<per:Name>John Doe</per:Name>
<per:SSN>123-45-6789</per:SSN>
</Person>
<Product>
<pro:Type>Widget</pro:Type>
</Product>
</Company>'''
def holder = new XmlHolder(xml)
//declaring multiple namespaces on the holder object
holder.declareNamespace('com',"http://www.company.org")
holder.declareNamespace('prod',"http://www.product.org")
holder.declareNamespace('per',"http://www.person.org")
//below snippets shows using different namespaces prefixes while doing xpath
//which may or may not be the same as defined prefixes in actualxml
//per - prefix is same as in xml and rest are different prefixes
assert 'John Doe'== holder.getNodeValue('//com:Person/per:Name'), "Person Name is not matching"
assert '123-45-6789' == holder.getNodeValue('//com:Person/per:SSN'), "Person SSN is not matching"
assert 'Widget' == holder.getNodeValue('//com:Product/prod:Type'), "Product Type is not matching"
答案 1 :(得分:0)
它是命名空间中的xmlns属性。
<OTAHotelRatePlanNotifRQ ... @xmlns="http://www.opentravel.org/OTA/2003/05" ... >
显然,如果它有任何值(空字符串除外),它会破坏很多XmlHolder的xpath功能。
从消息中删除xmlns属性会导致xpath正常工作。
这可以在Groovy中完成,如下所示:
response = response.replaceAll('xmlns="[^"]*"', "");