Groovy XMLSlurper - 搜索特定节点

Groovy XMLSlurper - searching sepecific node

我需要使用 Groovy 的 XMLSlurper 查找特定节点。条件应该是子节点的 text/value 必须匹配。在下面的示例中,我想搜索年份为“2003”且价格为“39.95”的图书节点。

<bookstore name="Store A">
  <employee>
      <id>546343</id>
      <name>Dustin Brown</name>
  </employee>
  <employee>
      <id>547547</id>
      <name>Lisa Danton</name>
  </employee>
  <book category="cooking">
    <title lang="en">Everyday Italian</title>
    <author>Giada De Laurentiis</author>
    <year>2005</year>
    <price>30.00</price>
  </book>
  <book category="children">
    <title lang="en">Harry Potter</title>
    <author>J K. Rowling</author>
    <year>2005</year>
    <price>29.99</price>
  </book>
  <book category="web">
    <title lang="en">Learning XML</title>
    <author>Erik T. Ray</author>
    <year>2003</year>
    <price>39.95</price>
  </book>
</bookstore>
<bookstore name="Store B">
...
</bookstore>

鉴于:

def xml = '''<stores>
  <bookstore name="Store A">
      <employee>
          <id>546343</id>
          <name>Dustin Brown</name>
      </employee>
      <employee>
          <id>547547</id>
          <name>Lisa Danton</name>
      </employee>
      <book category="cooking">
        <title lang="en">Everyday Italian</title>
        <author>Giada De Laurentiis</author>
        <year>2005</year>
        <price>30.00</price>
      </book>
      <book category="children">
        <title lang="en">Harry Potter</title>
        <author>J K. Rowling</author>
        <year>2005</year>
        <price>29.99</price>
      </book>
      <book category="web">
        <title lang="en">Learning XML</title>
        <author>Erik T. Ray</author>
        <year>2003</year>
        <price>39.95</price>
      </book>
  </bookstore>
  <bookstore name="Store B">
  </bookstore>
</stores>'''

然后

new XmlSlurper().parseText(xml).bookstore.book.findAll { it.year == '2003' && it.price == '39.95' }

这是实现相同目的的另一种方法。

请注意,用户可以通过添加如下所示轻松更改/添加其他 and 条件

def queryData = [[<element>, <operator>, <element value>], [<element>, <operator>, <element value>], ...]

运算符可以是以下之一:

  • EQ 等于
  • LE 小于等于
  • GE 大于等于
  • GT 大于
  • LT 小于
  • NE 不等于

例如:

def queryData = [['year','EQ', '2003'], ['price', 'LE', '39.95']]

基本上它会根据 queryData 创建一个 closure 并将其传递给 findAll

getQuery closure 构建对上述条件列表的查询

{ it -> it.year.text() == '2003' && it.price.text() <= '39.95' }

我觉得对于一些尝试 groovy 的新手来说,使用上面的列表而不是上面动态构建的闭包会更容易。

脚本如下:

​def xml = """<stores>  <bookstore name="Store A">
      <employee>
          <id>546343</id>
          <name>Dustin Brown</name>
      </employee>
      <employee>
          <id>547547</id>
          <name>Lisa Danton</name>
      </employee>
      <book category="cooking">
        <title lang="en">Everyday Italian</title>
        <author>Giada De Laurentiis</author>
        <year>2005</year>
        <price>30.00</price>
      </book>
      <book category="children">
        <title lang="en">Harry Potter</title>
        <author>J K. Rowling</author>
        <year>2005</year>
        <price>29.99</price>
      </book>
      <book category="web">
        <title lang="en">Learning XML</title>
        <author>Erik T. Ray</author>
        <year>2003</year>
        <price>39.95</price>
      </book>
  </bookstore>
  <bookstore name="Store B">
  </bookstore>
</stores>"""

//You may just add additional conditions into below list
def queryData = [['year','EQ', '2003'], ['price', 'LE', '39.95']]

enum Operator {
  EQ('=='), LE('<='), GE('>='), GT('>'), LT('<'), NE('!=')
  def value
  Operator(String value){
    this.value = value
  }

  def getValue(){
    value
  }
}

def getQuery = { list ->
  def sb = new StringBuffer('{ it -> ') 
  list.eachWithIndex { sublist, index ->
   index == 0 ?: sb.append(' && ')
   Operator operator = sublist[1]
   sb.append("it.${sublist[0]}.text() ${operator.value} '${sublist[2]}'")
  }
  def query = sb.append(' }').toString()
  println "Query formed is : ${query}"
  def sh = new GroovyShell()
  sh.evaluate(query)
}

def getBooks = { stores, closure ->
  stores.'**'.findAll { closure(it) } ?: 'Could not find matching book'
}

def stores = new XmlSlurper().parseText(xml)

def result = getBooks(stores, getQuery(queryData))
println result

你可以快点试试Demo

另外注意目前只做and的条件,不支持or.