Groovy 使用 xpath 引用 xml 中的重复节点值(插值滥用?)
Groovy reference repeating nodes values in xml with xpath (interpolation misuse?)
设置:SOAP UI 5.2.0.,Groovy 步骤生成 XMLs。
我们需要读取一个 CSV,其中包含类似 XPath 的节点位置和要放置的新值以进行采样 XML.
以下答案中的最后一个版本的代码完全符合我们的目标:
只有一个问题:
我们的 XML 包含重复元素并且无法使用,因为它误解了 Body.GetWeather[1].CityName
中的“[1]”
def node = xml
key.split("\.").each {
node = node."${it}"
}
理想情况下,我们还需要使用类似 Body.GetWeather[CountryName="Africa"].CityName
的东西。
我也尝试使用 XMLParser
并尝试使用语法(见下文)。我是 Groovy 的新手,我可能遗漏了一些关于它的内容。
所以,如果我需要以不同的方式处理问题,请告诉我。
下面是第三个例子中描述的实际问题:
// reading XML
def myInputXML = '''
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<web:GetWeather xmlns:web="http://www.webserviceX.NET">
<web:CityName>Cairo</web:CityName>
<web:CountryName>Africa</web:CountryName>
</web:GetWeather>
<web:GetWeather xmlns:web="http://www.webserviceX.NET">
<web:CityName>Heidelberg</web:CityName>
<web:CountryName>Germany</web:CountryName>
</web:GetWeather>
<web:GetWeather xmlns:web="http://www.webserviceX.NET">
<web:CityName>Strasbourg</web:CityName>
<web:CountryName>France</web:CountryName>
</web:GetWeather>
</soapenv:Body>
</soapenv:Envelope>
'''
def xml = new XmlSlurper().parseText( myInputXML )
// Example 1 //
def GetAllCities = xml.Body.GetWeather.CityName
log.info ("Example 1: "+GetAllCities.text()) // references all 3 CityName nodes, prints out - CairoHeidelbergStrasbourg
// Example 2 //
def Get2ndCity = xml.Body.GetWeather[1].CityName
log.info ("Example 2: "+Get2ndCity.text()) // references 2nd node, prints out - Heidelberg
// Example 3 //
def tmpNode1 = "Body"
def tmpNode2 = "GetWeather[0]"
// This problem is with interpolation of GetWeather[0]. tmpNode2 = "GetWeather" would work as Example 1
def tmpNode3 = "CityName"
def GetFirstCity = xml."${tmpNode1}"."${tmpNode2}"."${tmpNode3}"
log.info ("Example 3: "+GetFirstCity.text()) // prints "" - WHY?
log.info ("Interpolation of tmpNodes 1, 2, 3:")
log.info ("${tmpNode1}") // prints Body
log.info ("${tmpNode2}") // prints GetWeather[0]
log.info ("${tmpNode3}") // prints CityName
P.S。如果我的示例与实际问题无关,我深表歉意,我认为它们有些帮助,但目标是改进提到的 Whosebug 答案以支持重复元素。
如果您只想修复脚本,请进行以下更改以获得您期望的数据。
变化自
// Example 3 //
def tmpNode1 = "Body"
def tmpNode2 = "GetWeather[0]"
// This problem is with interpolation of GetWeather[0]. tmpNode2 = "GetWeather" would work as Example 1
def tmpNode3 = "CityName"
def GetFirstCity = xml."${tmpNode1}"."${tmpNode2}"."${tmpNode3}"
log.info ("Example 3: "+GetFirstCity.text()) // prints "" -
到
// Example 3 //
def tmpNode1 = "Body"
//removed index from here
def tmpNode2 = "GetWeather"
def tmpNode3 = "CityName"
//Added index here in below
def GetFirstCity = xml."${tmpNode1}"."${tmpNode2}"[0]."${tmpNode3}"
log.info ("Example 3: "+GetFirstCity.text())
优雅的方法:
但是,这是我从 csv
文件生成请求的方式。它不涉及任何 xml 模板,而是使用 StreamingMarkupBuilder 构建整个 请求 xml。因为这是一种优雅且灵活的方式。此外,csv 的可读性非常好,因为它只包含数据,而不是您提到的任何 xpath's
。
下面的脚本使用了这个非常好的库groovycsv(依赖于opencsv),请按照它的自述文件进行操作。
- 下载 jar 文件。
- 将它们复制到 SOAPUI_HOME/bin/ext 目录下。
- 重启soapui。
这是基于 csv 文件构建请求 xml 的脚本:
import groovy.xml.*
import static com.xlson.groovycsv.CsvParser.parseCsv
//closure which builds the request based on the data provided
def requestBuilder = { csvData ->
def builder = new StreamingMarkupBuilder()
builder.encoding = 'UTF-8'
def soapRequest = builder.bind {
mkp.xmlDeclaration()
namespaces << [soap: 'http://schemas.xmlsoap.org/soap/envelope/',
web : 'http://www.webserviceX.NET']
soap.Envelope {
soap.Header{}
soap.Body {
//loop thru the rows
csvData.each { row ->
//create GetWeather element for each row
web.GetWeather{
web.CityName(row.CityName)
web.CountryName(row.CountryName)
}
}
}
}
}
}
//Used fixed csv data. But you can replace it with reading from file too
def csv = '''CityName,CountryName
Cairo,Africa
Heidelberg,Germany
Strasbourg,France'''
/**
//use this to read from file and remove above statement
def csv = new File('/absolute/csv/file/path').text
**/
//parse the csv using groovy csv library
def data = parseCsv(csv)
//call the above closure get the request and serialize it to string
def request = XmlUtil.serialize(requestBuilder(data))
log.info request
如果你使用print request
而不是log.info
,它会显示漂亮的打印xml(你需要在命令行SOAPUI_HOME/bin/soapui.bat中启动soapui)
设置:SOAP UI 5.2.0.,Groovy 步骤生成 XMLs。
我们需要读取一个 CSV,其中包含类似 XPath 的节点位置和要放置的新值以进行采样 XML.
以下答案中的最后一个版本的代码完全符合我们的目标:
只有一个问题:
我们的 XML 包含重复元素并且无法使用,因为它误解了 Body.GetWeather[1].CityName
def node = xml
key.split("\.").each {
node = node."${it}"
}
理想情况下,我们还需要使用类似 Body.GetWeather[CountryName="Africa"].CityName
的东西。
我也尝试使用 XMLParser
并尝试使用语法(见下文)。我是 Groovy 的新手,我可能遗漏了一些关于它的内容。
所以,如果我需要以不同的方式处理问题,请告诉我。
下面是第三个例子中描述的实际问题:
// reading XML
def myInputXML = '''
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<web:GetWeather xmlns:web="http://www.webserviceX.NET">
<web:CityName>Cairo</web:CityName>
<web:CountryName>Africa</web:CountryName>
</web:GetWeather>
<web:GetWeather xmlns:web="http://www.webserviceX.NET">
<web:CityName>Heidelberg</web:CityName>
<web:CountryName>Germany</web:CountryName>
</web:GetWeather>
<web:GetWeather xmlns:web="http://www.webserviceX.NET">
<web:CityName>Strasbourg</web:CityName>
<web:CountryName>France</web:CountryName>
</web:GetWeather>
</soapenv:Body>
</soapenv:Envelope>
'''
def xml = new XmlSlurper().parseText( myInputXML )
// Example 1 //
def GetAllCities = xml.Body.GetWeather.CityName
log.info ("Example 1: "+GetAllCities.text()) // references all 3 CityName nodes, prints out - CairoHeidelbergStrasbourg
// Example 2 //
def Get2ndCity = xml.Body.GetWeather[1].CityName
log.info ("Example 2: "+Get2ndCity.text()) // references 2nd node, prints out - Heidelberg
// Example 3 //
def tmpNode1 = "Body"
def tmpNode2 = "GetWeather[0]"
// This problem is with interpolation of GetWeather[0]. tmpNode2 = "GetWeather" would work as Example 1
def tmpNode3 = "CityName"
def GetFirstCity = xml."${tmpNode1}"."${tmpNode2}"."${tmpNode3}"
log.info ("Example 3: "+GetFirstCity.text()) // prints "" - WHY?
log.info ("Interpolation of tmpNodes 1, 2, 3:")
log.info ("${tmpNode1}") // prints Body
log.info ("${tmpNode2}") // prints GetWeather[0]
log.info ("${tmpNode3}") // prints CityName
P.S。如果我的示例与实际问题无关,我深表歉意,我认为它们有些帮助,但目标是改进提到的 Whosebug 答案以支持重复元素。
如果您只想修复脚本,请进行以下更改以获得您期望的数据。
变化自
// Example 3 //
def tmpNode1 = "Body"
def tmpNode2 = "GetWeather[0]"
// This problem is with interpolation of GetWeather[0]. tmpNode2 = "GetWeather" would work as Example 1
def tmpNode3 = "CityName"
def GetFirstCity = xml."${tmpNode1}"."${tmpNode2}"."${tmpNode3}"
log.info ("Example 3: "+GetFirstCity.text()) // prints "" -
到
// Example 3 //
def tmpNode1 = "Body"
//removed index from here
def tmpNode2 = "GetWeather"
def tmpNode3 = "CityName"
//Added index here in below
def GetFirstCity = xml."${tmpNode1}"."${tmpNode2}"[0]."${tmpNode3}"
log.info ("Example 3: "+GetFirstCity.text())
优雅的方法:
但是,这是我从 csv
文件生成请求的方式。它不涉及任何 xml 模板,而是使用 StreamingMarkupBuilder 构建整个 请求 xml。因为这是一种优雅且灵活的方式。此外,csv 的可读性非常好,因为它只包含数据,而不是您提到的任何 xpath's
。
下面的脚本使用了这个非常好的库groovycsv(依赖于opencsv),请按照它的自述文件进行操作。
- 下载 jar 文件。
- 将它们复制到 SOAPUI_HOME/bin/ext 目录下。
- 重启soapui。
这是基于 csv 文件构建请求 xml 的脚本:
import groovy.xml.*
import static com.xlson.groovycsv.CsvParser.parseCsv
//closure which builds the request based on the data provided
def requestBuilder = { csvData ->
def builder = new StreamingMarkupBuilder()
builder.encoding = 'UTF-8'
def soapRequest = builder.bind {
mkp.xmlDeclaration()
namespaces << [soap: 'http://schemas.xmlsoap.org/soap/envelope/',
web : 'http://www.webserviceX.NET']
soap.Envelope {
soap.Header{}
soap.Body {
//loop thru the rows
csvData.each { row ->
//create GetWeather element for each row
web.GetWeather{
web.CityName(row.CityName)
web.CountryName(row.CountryName)
}
}
}
}
}
}
//Used fixed csv data. But you can replace it with reading from file too
def csv = '''CityName,CountryName
Cairo,Africa
Heidelberg,Germany
Strasbourg,France'''
/**
//use this to read from file and remove above statement
def csv = new File('/absolute/csv/file/path').text
**/
//parse the csv using groovy csv library
def data = parseCsv(csv)
//call the above closure get the request and serialize it to string
def request = XmlUtil.serialize(requestBuilder(data))
log.info request
如果你使用print request
而不是log.info
,它会显示漂亮的打印xml(你需要在命令行SOAPUI_HOME/bin/soapui.bat中启动soapui)