xml - YAML XML 过滤器
问题描述
我想做以下事情:
我想使用 YAML 文件来定义要在 XML 文件中更改的值。理论上是天作之合,但细节如何运作。下面的代码在几个级别上都是错误的,但它显示了我需要的内容。问题是没有办法逐叶遍历 YAML 文件,我在这里需要它。所以我需要求助于一些令人讨厌的递归东西来去除“路径”。
第二个问题是,即使我的路径形式为“parent1[0].parent2[0].@description”,我也无法使用 xmlIn."${pathVariable}" = value,因为这只适用于直接孩子们。
#!/usr/bin/env groovy
// Test for YAML
import groovy.util.*
import groovy.text.*
import groovy.xml.*
@Grab('org.yaml:snakeyaml:1.18')
// YAML file
def yamlFile = '''"@description": "testParent1"
parent1[0]:
parent2[0]:
"@description": "testParent2"
'''
def xmlFile = '''
<root>
<parent1 description="test0">
<parent2 description="test1" />
</parent1>
<parent1>
<parent2 description="test2" />
</parent1>
</root>
'''
def config = new org.yaml.snakeyaml.Yaml().load(yamlFile)
def xmlIn = new XmlParser().parseText(xmlFile)
config.each {
println "${it.key} = ${it.value}"
xmlIn."${it.key}" = it.value
}
解决方案
问题是没有办法逐叶遍历 YAML 文件,我在这里需要它。
从技术上讲,没有叶子,因为 YAML 表示的是一个图,而不是一棵树。由于锚点和别名,它可能有循环。但是,您当然仍然可以实现一个自己访问每个节点的 walker,只要您集成一个检测循环的检查,这样您就不会陷入无限循环。
但我认为这里更直接的策略是走事件流。这是一种用 Java 实现的方法(请原谅,我不知道 Groovy;但我认为它很容易翻译):
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(xmlFile);
XPathFactory xpf = XPathFactory.newInstance();
XPath xpath = xpf.newXPath();
List<String> pathComponents = new ArrayList<String>();
int pos = -1; // root event will be MappingStartEvent, which will lift this to 0.
for (Event e: new Yaml().parse(yamlFile)) {
if (e instanceof MappingStartEvent) {
if (pos == pathComponents.size()) {
throw RuntimeException("Mapping as key not supported!");
}
pos++;
} else if (e instanceof MappingEndEvent) {
pos--;
// pop recent key from list
if (pos >= 0) {
pathComponents.remove(pos);
}
} else if (e instanceof ScalarEvent) {
if (pos == pathComponents.size()) {
// this is a key, add it to the path
pathComponents.add(((ScalarEvent) e).value);
} else {
// this is a replacement value, do the replacement.
// last given key must specify an attribute.
String lastKey = pathComponents.get(pathComponents.size() - 1);
if (lastKey.charAt(0) != '@') {
throw RuntimeException("can only change attributes!");
}
// attribute name not part of path; pop it
pathComponents.remove(pos);
Element userElement = (Element) xpath.evaluate("/" + String.join("/", pathComponents), doc,
XPathConstants.NODE);
userElement.setAttribute(lastKey.substring(1), ((ScalarEvent) e).value);
}
} else if (e instanceof SequenceStartEvent or e instanceof SequenceEndEvent) {
throw RuntimeException("sequences not allowed!");
} else if (e instanceof AliasEvent) {
throw RuntimeException("aliases not allowed!");
} else {
// I skip checking for proper DocumentStartEvent / DocumentEndEvent here
}
}
此代码未经测试!
有一个名为XModifier的 Java 库,看起来它使修改更容易,但我不知道它似乎并不普遍,所以要小心。
推荐阅读
- python - Python Pandas Drop 在使用 drop 时重新排序我的数据框
- python - TypeError: int() 参数必须是字符串、对象或数字,而不是“时间戳”
- c# - 而不是一组新的数据,我怎么能只添加项目的数量
- javascript - Django:onchange后什么脚本函数会重新加载模型?
- nativescript - 旋转设备时应用程序布局中断。有解决办法吗?
- adobecreativesdk - Adobe SDK 图像编辑器在 IE11 中不起作用
- git - Firefox 出现“证书尚未生效”错误
- laravel - .htaccess laravel 删除公共并重定向到 https
- twitter-bootstrap - 如何添加独立按钮以在引导文件输入上上传所选文件
- java - 由于 RaceCondtion,使用流迭代 LinkedList 正在改变结果列表