YAML XML过滤器

YAML XML过滤器,xml,groovy,yaml,Xml,Groovy,Yaml,我想做以下工作: 我想使用YAML文件来定义XML文件中要更改的值。从理论上讲,这是天作之合,但细节如何。下面的代码在几个级别上都是错误的,但它显示了我所需要的。问题是没有办法一页一页地走YAML文件,我需要在这里。所以我需要借助一些令人讨厌的递归方法来去除“路径” 第二个问题是,即使路径的格式为“parent1[0].parent2[0]。@description“我无法转到xmlIn。”${pathVariable}”=value,因为这只适用于直接子级 #!/usr/bin/env gro

我想做以下工作:

我想使用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
}
#/usr/bin/env groovy
//YAML测试
导入groovy.util.*
导入groovy.text*
导入groovy.xml*
@Grab('org.yaml:snakeyaml:1.18')
//YAML文件
def yamlFile=''''@description:“testParent1”
parent1[0]:
parent2[0]:
@description:“testParent2”
'''
def xmlFile=''
'''
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=xpf.newXPath();
List pathComponents=new ArrayList();
int pos=-1;//根事件将是MappingStartEvent,这将使其提升为0。
for(事件e:new Yaml().parse(yamlFile)){
if(映射开始事件的实例){
if(pos==pathComponents.size()){
抛出RuntimeException(“不支持映射为键!”);
}
pos++;
}else if(映射事件的实例){
pos--;
//从列表中弹出最近的键
如果(位置>=0){
路径组件。移除(pos);
}
}else if(例如ScalarEvent的实例){
if(pos==pathComponents.size()){
//这是一个键,请将其添加到路径
添加(((ScalarEvent)e).value);
}否则{
//这是替换值,请执行替换。
//最后一个给定的键必须指定一个属性。
字符串lastKey=pathComponents.get(pathComponents.size()-1);
if(lastKey.charAt(0)!=“@”){
抛出RuntimeException(“只能更改属性!”);
}
//属性名称不是路径的一部分;将其弹出
路径组件。移除(pos);
Element userElement=(Element)xpath.evaluate(“/”+String.join(“/”,pathComponents),doc,
XPathConstants.NODE);
setAttribute(lastKey.substring(1),((ScalarEvent)e).value);
}
}else if(SequenceStartEvent的e实例或SequenceEndEvent的e实例){
抛出运行时异常(“不允许序列!”);
}else if(e AliasEvent实例){
抛出RuntimeException(“不允许使用别名!”);
}否则{
//我跳过了在此处检查正确的文档StartEvent/DocumentEndEvent
}
}
此代码未经测试


有一个名为的Java库,看起来它使修改更容易,但我不知道,而且它似乎并不普遍,所以要小心。

哦,这很接近,但并不是我想要的。谢谢你,但我现在意识到(有你的意见),这是没有希望的。只是太丑陋了,想让它像我想的那样工作。因此,在尝试之后,我正在设计一个新的解决方案。
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
    }
}