【问题标题】:Groovy reference repeating nodes values in xml with xpath (interpolation misuse?)Groovy 使用 xpath 在 xml 中引用重复节点值(插值滥用?)
【发布时间】:2016-06-22 01:00:36
【问题描述】:

设置:SOAP UI 5.2.0.,生成 XML 的 Groovy 步骤。 我们需要读取一个 CSV,其中包含类似 XPath 的节点位置和要放置以采样 XML 的新值。
以下答案中的最后一个版本的代码完全符合我们的目标:Groovy replace node values in xml with xpath
只有一个问题: 我们的 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

附:抱歉,如果我的示例与实际问题无关,我认为它们有些帮助,但目标是改进提到的 stackoverflow 答案以支持重复元素。

【问题讨论】:

    标签: xml xpath groovy soapui interpolation


    【解决方案1】:

    如果您只想修复脚本,请进行以下更改以获得您期望的数据。

    变化自

    // 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 构建整个 request 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)

    【讨论】:

    • 谢谢。我用示例 3 测试了第一个建议,但它对我不起作用……如果这样或类似的东西起作用,那就太好了,因为这样我就可以使用我们现有的设置了。
    • 你知道,可能是什么问题 - Groovy 版本/打印错误?关于第二个建议:我将重构我的脚本以使用 openCSV,但是在不使用示例 XML 的情况下创建 XML 对我们的目标没有帮助:我们在 XML 中有数百个元素,我们只需要始终在相同的位置更新几个元素,但在各种不同的 XML 文件中。我们需要使用这些类似 XPath 的位置,并且无法为重复元素的不同实例设置不同值的限制是现在的主要问题......
    • @Zaplatki,实际上我犯了拼写错误,已修复。 xml."${tmpNode1}"."${tmpNode2}"[0]."$tmpNode3"。请重试。
    • 我不知道您如何维护这些数据。拥有 csv 并创建 xml 会干净利落。
    • 现在可以了,太好了,谢谢!当最终用户将数据输入 UI 时,另一个应用程序会生成示例 XML,但即使生成 1 也需要很长时间。然后该系统将它们触发到我们正在处理的系统中。我们从测试人员那里收到示例 XML 并对其稍作修改以测试我们系统中的某些功能 - 我们不需要生成所有内容,只需修改与某些功能相关的部分即可。
    猜你喜欢
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多