我有大量包含URL的XML文件.我正在编写一个groovy实用程序来查找每个URL并将其替换为更新版本.
给定example.xml:
/some/old/url /some/old/url /a/different/old/url?with=specialChars&escaped=true
脚本运行后,example.xml应包含:
/a/new/and/improved/url /a/new/and/improved/url /a/different/new/and/improved/url?with=specialChars&stillEscaped=true
这很容易使用groovy的优秀xml支持,除了我想要更改URL而不是文件的其他内容.
我的意思是:
空格不得更改(文件可能包含空格,制表符或两者)
必须保留评论
必须保留windows与unix样式的行分隔符
不得添加或删除顶部的xml声明
标签中的属性必须保留其顺序
到目前为止,在尝试了XmlParser,DOMBuilder,XmlNodePrinter,XmlUtil.serialize()等的许多组合之后,我已经逐行阅读每个文件,并应用了xml实用程序和正则表达式的丑陋混合.
读写每个文件:
files.each { File file -> def lineEnding = file.text.contains('\r\n') ? '\r\n' : '\n' def newLineAtEof = file.text.endsWith(lineEnding) def lines = file.readLines() file.withWriter { w -> lines.eachWithIndex { line, index -> line = update(line) w.write(line) if (index < lines.size-1) w.write(lineEnding) else if (newLineAtEof) w.write(lineEnding) } } }
搜索和更新一行中的URL:
def matcher = (line =~ urlTagRegexp) //matches aelement and its contents matcher.each { groups -> def urlNode = new XmlParser().parseText(line) def url = urlNode.text() def newUrl = translate(url) if (newUrl) { urlNode.value = newUrl def replacement = nodeToString(urlNode) line = matcher.replaceAll(replacement) } } def nodeToString(node) { def writer = new StringWriter() writer.withPrintWriter { printWriter -> def printer = new XmlNodePrinter(printWriter) printer.preserveWhitespace = true printer.print(node) } writer.toString().replaceAll(/[\r\n]/, '') }
这主要是有效的,除了它无法处理分割成多行的标记,并且在将文件写回时弄乱换行是很麻烦的.
我是groovy的新手,但我觉得必须有一种更加时髦的方式来做这件事.
我刚刚在https://gist.github.com/akhikhl/8070808上创建了gist,以演示如何使用Groovy和JDOM2完成此类转换.
重要笔记:
Groovy在技术上允许使用任何java库.如果使用Groovy JDK无法完成某些操作,可以使用其他库完成.
应该明确地包含jaxen库(实现XPath)(通过@Grab或通过maven/gradle),因为它是JDOM2的可选依赖项.
@ Grab/@ GrabExclude指令的序列修复了jaxen对JDOM-1.0的古怪依赖性.
XPathFactory.compile还支持变量绑定和过滤器(参见在线javadoc).
XPathExpression(由compile返回)支持两个主要功能 - evaluate和evaluateFirst.evaluate总是返回所有XML节点的列表,满足指定的谓词,而evaluateFirst只返回第一个匹配的XML节点.
更新
以下代码:
new XMLOutputter().with { format = Format.getRawFormat() format.setLineSeparator(LineSeparator.NONE) output(doc, System.out) }
解决了保留空格和行分隔符的问题.getRawFormat构造一个保留空格的格式对象.LineSeparator.NONE指示格式对象,它不应转换行分隔符.
上面提到的要点也包含这个新代码.
有一个没有任何第三方库的解决方案.
def xml = file.text
def document = groovy.xml.DOMBuilder.parse(new StringReader(xml))
def root = document.documentElement
use(groovy.xml.dom.DOMCategory) {
// manipulate the XML here, i.e. root.someElement?.each { it.value = 'new value'}
}
def result = groovy.xml.dom.DOMUtil.serialize(root)
file.withWriter { w ->
w.write(result)
}
摘自http://jonathan-whywecanthavenicethings.blogspot.de/2011/07/keep-your-hands-off-of-my-whitespace.html