简体   繁体   English

SoapUI Groovy脚本中的XPath不能按预期工作

[英]XPath in SoapUI Groovy Script not working as expected

I am attempting to extract values out of an XML response in SoapUI, using XmlHolder in a groovy script. 我试图在groovy脚本中使用XmlHolder从SoapUI的XML响应中提取值。 But the xpath is not working the way I expect it to. 但是xpath无法按照我期望的方式工作。

This is the response that I'm querying: 这是我正在查询的响应:

<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>

This is the code in my Groovy Script: 这是我的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

Am I doing something wrong here? 我在这里做错什么了吗?

That is because of the default namespace being used in the response, like you anticipated. 这是因为响应中使用了默认的名称空间,正如您所预期的那样。

However, it needs to be handled differently than you mentioned in the answer. 但是,它的处理方式与您在答案中提到的方式不同。

And this applies to the both below conditions 这适用于以下两种情况

  1. default namespace 默认名称空间
  2. namespace with prefix 带有前缀的名称空间

After defining the object for the XmlHolder , have to set all the namespaces that are required to query the data. 在为XmlHolder定义对象之后,必须设置查询数据所需的所有名称空间。 And below statement does the same and putting the complete script has well. 下面的语句也一样,并且完整的脚本也很好。

How to define / set the namespaces 如何定义/设置名称空间

In your response, there is only one namespace used ie, http://www.opentravel.org/OTA/2003/05 . 在您的响应中,仅使用了一个名称空间,即http://www.opentravel.org/OTA/2003/05

Set namespaces map, you are free to define your own prefix here, need to not to worry about what prefix does it have in original response (considering above mentioned 2 nd case) and use this prefix('ns') while getting data using xpath . 设置名称空间映射,您可以在此处自由定义自己的前缀,不必担心它在原始响应中具有什么前缀(考虑到上述第二种情况),并在使用xpath获取数据时使用此前缀('ns') 。

holder.declareNamespace('ns',"http://www.opentravel.org/OTA/2003/05")

Groovy Script 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"))

May be you can find more information on namespaces here if interested. 如果感兴趣的话,也许可以在这里找到有关名称空间的更多信息。

Below Xml with Multiple namcespaces including default. 在Xml以下,带有多个namcespaces,包括默认值。
Taken this sample from above link. 从上面的链接中获取此样本。 Adding the below details because if it can clear and helps understanding a bit more on the namespace aware. 添加以下详细信息,因为它可以清除并有助于进一步了解名称空间。

<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>

If you notice above: 如果您在上面注意到:

  1. Company, Product, Person are in the same namespace and also using default namespace. 公司,产品,人员位于同一名称空间中,并且还使用默认名称空间。
  2. The elements of Person are using different namespace, also with prefix per Person的元素使用不同的命名空间, per前缀也带有前缀
  3. The elements of Product are using another namespace, with pro fix. 产品的元素使用另一个命名空间,与pro修复。

In the below script example, going to define namespaces with different prefix or same prefix(just to illustrate that user can freely use different prefix while doing xpath) 在以下脚本示例中,将定义具有不同前缀或相同前缀的命名空间(仅说明用户在执行xpath时可以自由使用不同的前缀)

Groovy Script With Multiple Namespaces 具有多个命名空间的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"

It's the xmlns attribute in the namespace. 它是名称空间中的xmlns属性。

<OTAHotelRatePlanNotifRQ ... @xmlns="http://www.opentravel.org/OTA/2003/05" ... >

Apparently, if it has any value (other than an empty string), it breaks a lot of XmlHolder's xpath functionality. 显然,如果它具有任何值(而不是空字符串),它将破坏XmlHolder的xpath功能。

Removing the xmlns attribute from the message causes the xpaths to work as normal. 从消息中删除xmlns属性会使xpath正常工作。

This can be done in Groovy like so: 可以这样在Groovy中完成:

response = response.replaceAll('xmlns="[^"]*"', "");

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM