Дело в том, что нет способа пройтись по файлу YAML по листам, что мне нужно здесь.
Технически нет листьев, потому что YAML представляет собой граф, а не дерево. В нем могут быть циклы из-за якорей и псевдонимов. Тем не менее, вы, конечно, можете реализовать механизм обхода, который посещает каждый узел самостоятельно, при условии, что вы интегрируете проверку, которая обнаруживает циклы, чтобы вы не попали в бесконечный цикл.
Но я думаю, что более простой стратегией здесь является прохождение потока событий. Вот способ сделать это на 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
}
}
Этот код не проверен!
Существует библиотека Java с именем XModifier , которая выглядит так, как будто она облегчает изменения, но я не знаю, и она не кажется широко распространенной, так что будьте осторожны.