1. 简介
Groovy 提供了多种处理 XML 的方法,支持对 XML 结构进行解析、修改、增删、替换等操作。本文将介绍如何使用 Groovy 的核心类库,如 XmlParser、XmlSlurper 和 MarkupBuilder,来高效处理 XML 数据。
我们还将展示如何从零构建 XML 结构,以及在实际开发中使用这些类库时的一些常见坑点,帮助你快速上手并避免踩坑。
2. 定义模型
我们先定义一个 XML 示例文件 articles.xml
,内容如下:
<articles>
<article>
<title>First steps in Java</title>
<author id="1">
<firstname>Siena</firstname>
<lastname>Kerr</lastname>
</author>
<release-date>2018-12-01</release-date>
</article>
<article>
<title>Dockerize your SpringBoot application</title>
<author id="2">
<firstname>Jonas</firstname>
<lastname>Lugo</lastname>
</author>
<release-date>2018-12-01</release-date>
</article>
<article>
<title>SpringBoot tutorial</title>
<author id="3">
<firstname>Daniele</firstname>
<lastname>Ferguson</lastname>
</author>
<release-date>2018-06-12</release-date>
</article>
<article>
<title>Java 12 insights</title>
<author id="1">
<firstname>Siena</firstname>
<lastname>Kerr</lastname>
</author>
<release-date>2018-07-22</release-date>
</article>
</articles>
然后在 Groovy 代码中加载这个 XML 文件:
def xmlFile = getClass().getResourceAsStream("articles.xml")
3. XmlParser
XmlParser 是 Groovy 中用于解析 XML 并对其进行操作的常用类之一。
3.1. 读取 XML
使用 XmlParser 解析 XML 文件非常简单:
def articles = new XmlParser().parse(xmlFile)
解析后,我们可以使用 GPath 表达式访问 XML 的属性和值。例如:
✅ 示例测试代码:
def "Should read XML file properly"() {
given: "XML file"
when: "Using XmlParser to read file"
def articles = new XmlParser().parse(xmlFile)
then: "Xml is loaded properly"
articles.'*'.size() == 4
articles.article[0].author.firstname.text() == "Siena"
articles.article[2].'release-date'.text() == "2018-06-12"
articles.article[3].title.text() == "Java 12 insights"
articles.article.find { it.author.'@id'.text() == "3" }.author.firstname.text() == "Daniele"
}
⚠️ 注意:articles.article[0].author.firstname.text()
表示获取第一个 article 的作者名字。
3.2. 添加节点
我们可以使用 NodeBuilder 构建新节点,并通过 append()
方法添加到现有 XML 中:
def "Should add node to existing xml using NodeBuilder"() {
given: "XML object"
def articles = new XmlParser().parse(xmlFile)
when: "Adding node to xml"
def articleNode = new NodeBuilder().article(id: '5') {
title('Traversing XML in the nutshell')
author {
firstname('Martin')
lastname('Schmidt')
}
'release-date'('2019-05-18')
}
articles.append(articleNode)
then: "Node is added to xml properly"
articles.'*'.size() == 5
articles.article[4].title.text() == "Traversing XML in the nutshell"
}
3.3. 修改节点
修改节点值也很简单,直接设置 value
属性即可:
def "Should modify node"() {
given: "XML object"
def articles = new XmlParser().parse(xmlFile)
when: "Changing value of one of the nodes"
articles.article.each { it.'release-date'[0].value = "2019-05-18" }
then: "XML is updated"
articles.article.findAll { it.'release-date'.text() != "2019-05-18" }.isEmpty()
}
3.4. 替换节点
使用 replaceNode()
方法可以替换整个节点:
def "Should replace node"() {
given: "XML object"
def articles = new XmlParser().parse(xmlFile)
when: "Replacing node"
def articleNode = new NodeBuilder().article(id: '5') {
title('Traversing XML in the nutshell')
author {
firstname('Martin')
lastname('Schmidt')
}
'release-date'('2019-05-18')
}
articles.article[0].replaceNode(articleNode)
then: "Node is added to xml properly"
articles.'*'.size() == 4
articles.article[0].title.text() == "Traversing XML in the nutshell"
}
3.5. 删除节点
删除节点时需要注意,不能直接调用 removeAll()
,而应先筛选出符合条件的节点,再逐个删除:
def "Should remove article from xml"() {
given: "XML object"
def articles = new XmlParser().parse(xmlFile)
when: "Removing all articles but the ones with id==3"
articles.article
.findAll { it.author.'@id'.text() != "3" }
.each { articles.remove(it) }
then: "There is only one article left"
articles.children().size() == 1
articles.article[0].author.'@id'.text() == "3"
}
4. XmlSlurper
XmlSlurper 是另一个用于处理 XML 的类,它提供了更简洁的 API。
4.1. 读取 XML
和 XmlParser 类似,使用方式如下:
def "Should read XML file properly"() {
given: "XML file"
when: "Using XmlSlurper to read file"
def articles = new XmlSlurper().parse(xmlFile)
then: "Xml is loaded properly"
articles.'*'.size() == 4
articles.article[0].author.firstname == "Siena"
articles.article[2].'release-date' == "2018-06-12"
articles.article[3].title == "Java 12 insights"
articles.article.find { it.author.'@id' == "3" }.author.firstname == "Daniele"
}
4.2. 添加节点
添加节点方式类似,但更简洁:
def "Should add node to existing xml"() {
given: "XML object"
def articles = new XmlSlurper().parse(xmlFile)
when: "Adding node to xml"
articles.appendNode {
article(id: '5') {
title('Traversing XML in the nutshell')
author {
firstname('Martin')
lastname('Schmidt')
}
'release-date'('2019-05-18')
}
}
articles = new XmlSlurper().parseText(XmlUtil.serialize(articles))
then: "Node is added to xml properly"
articles.'*'.size() == 5
articles.article[4].title == "Traversing XML in the nutshell"
}
⚠️ 注意:结构修改后需重新解析。
4.3. 修改节点
修改节点值更简单,直接赋值即可:
def "Should modify node"() {
given: "XML object"
def articles = new XmlSlurper().parse(xmlFile)
when: "Changing value of one of the nodes"
articles.article.each { it.'release-date' = "2019-05-18" }
then: "XML is updated"
articles.article.findAll { it.'release-date' != "2019-05-18" }.isEmpty()
}
4.4. 替换节点
替换节点使用 replaceNode()
方法:
def "Should replace node"() {
given: "XML object"
def articles = new XmlSlurper().parse(xmlFile)
when: "Replacing node"
articles.article[0].replaceNode {
article(id: '5') {
title('Traversing XML in the nutshell')
author {
firstname('Martin')
lastname('Schmidt')
}
'release-date'('2019-05-18')
}
}
articles = new XmlSlurper().parseText(XmlUtil.serialize(articles))
then: "Node is replaced properly"
articles.'*'.size() == 4
articles.article[0].title == "Traversing XML in the nutshell"
}
4.5. 删除节点
删除节点可以使用 replaceNode({})
实现:
def "Should remove article from xml"() {
given: "XML object"
def articles = new XmlSlurper().parse(xmlFile)
when: "Removing all articles but the ones with id==3"
articles.article
.findAll { it.author.'@id' != "3" }
.replaceNode {}
articles = new XmlSlurper().parseText(XmlUtil.serialize(articles))
then: "There is only one article left"
articles.children().size() == 1
articles.article[0].author.'@id' == "3"
}
5. XmlParser vs XmlSlurper
特性 | XmlParser | XmlSlurper |
---|---|---|
是否构建 DOM | ✅ 是 | ❌ 否 |
支持结构修改 | ✅ | ✅(但需重新解析) |
性能 | 低(内存占用高) | 高(适合大文件) |
易用性 | 一般 | 更简洁 |
适用场景 | 小文件、需频繁修改 | 大文件、读取为主 |
建议:根据 XML 文件大小和操作需求选择合适的类。
6. MarkupBuilder
如果你需要从零构建 XML 结构,MarkupBuilder 是理想选择:
def "Should create XML properly"() {
given: "Node structures"
when: "Using MarkupBuilderTest to create xml structure"
def writer = new StringWriter()
new MarkupBuilder(writer).articles {
article {
title('First steps in Java')
author(id: '1') {
firstname('Siena')
lastname('Kerr')
}
'release-date'('2018-12-01')
}
article {
title('Dockerize your SpringBoot application')
author(id: '2') {
firstname('Jonas')
lastname('Lugo')
}
'release-date'('2018-12-01')
}
}
then: "Xml is created properly"
XmlUtil.serialize(writer.toString()) == XmlUtil.serialize(xmlFile.text)
}
7. 总结
本文介绍了 Groovy 中处理 XML 的三种主要方式:
- ✅ XmlParser:适合需要频繁修改 XML 的场景。
- ✅ XmlSlurper:更适合读取大文件,语法更简洁。
- ✅ MarkupBuilder:构建 XML 结构的利器。
在实际开发中,可以根据需求灵活选择,避免不必要的性能开销和代码复杂度。
完整示例代码可参考:GitHub 仓库(模拟地址)