我有一些包含大量配置的 YAML 文件,但我只需要其中的一个标签 (mytag)。
component:
field1:
- name: nnn1
field2:
- name: mmm1
mytag:
description: hello
mycomponents:
- propertyA: aaa
propertyB: bbb
theirtag:
bbb: 111
comps:
- propA: aaa
prop: bbb
我需要加载:
class MyClass {
String description;
List<MyComponent> mycomponents:
}
如何使用 SnakeYaml 做到这一点? 我无法快速找到任何有用的示例。
跟进:如果我的标签嵌套更多怎么办深:(boo / mytag)
boo:
mytag:
description: hello
mycomponents:
- propertyA: aaa
propertyB: bbb
谢谢!
这并非小事。虽然使用自定义构造函数可能是可行的,但对于这样的情况,它们的接口很难处理,我不确定如何做到这一点。
相反,您可以覆盖默认解析器行为,并仅丢弃
mytag
键周围的任何事件,以便解析器发出的事件流仅包含 mytag
键值内的事件。这是一个最小的工作示例:
package droggeljug;
import org.yaml.snakeyaml.parser.ParserImpl;
import org.yaml.snakeyaml.events.*;
import org.yaml.snakeyaml.composer.Composer;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.reader.StreamReader;
import java.io.*;
import java.util.List;
public class Main {
public static class MyParser extends ParserImpl {
private final String containingKey;
private int depth = -1;
private boolean checked = false;
public MyParser(StreamReader reader, boolean emitComments, String containingKey) {
super(reader, emitComments);
this.containingKey = containingKey;
}
@Override
public Event peekEvent() {
if (checked) return super.peekEvent();
Event e;
while (true) {
boolean seenContainingKey = false;
e = super.peekEvent();
switch (e.getEventId()) {
case StreamStart:
case DocumentStart:
case Comment:
case StreamEnd:
case DocumentEnd:
return e;
case Scalar:
if (depth == -1) {
if (containingKey.equals(((ScalarEvent)e).getValue())) {
seenContainingKey = true;
break;
}
}
case Alias:
break;
case MappingStart:
case SequenceStart:
if (depth > -1) {
depth++;
}
break;
case MappingEnd:
case SequenceEnd:
if (depth > -1) {
depth--;
}
break;
}
if (seenContainingKey) {
depth = 0;
} else if (depth > -1) break;
checked = true; super.getEvent(); checked = false; // discard
}
if (depth == 0) depth = -1;
checked = true;
return e;
}
public Event getEvent() {
Event ret = super.getEvent();
checked = false;
return ret;
}
}
public static class MyYaml extends Yaml {
private Object loadContainedFromReader(StreamReader sreader, Class<?> type, String containingKey) {
Composer composer = new Composer(new MyParser(sreader,
loadingConfig.isProcessComments(), containingKey), resolver, loadingConfig);
constructor.setComposer(composer);
return constructor.getSingleData(type);
}
@SuppressWarnings("unchecked")
public <T> T loadContainedAs(Reader io, Class<T> type, String containingKey) {
return (T) loadContainedFromReader(new StreamReader(io), type, containingKey);
}
}
public static class MyClass {
public static class MyComponent {
public String propertyA, propertyB;
}
public String description;
public List<MyComponent> mycomponents;
}
private static final String input =
"component:\n" +
"field1:\n" +
" - name: nnn1\n" +
"field2:\n" +
" - name: mmm1\n" +
"mytag:\n" +
" description: hello\n" +
" mycomponents:\n" +
" - propertyA: aaa\n" +
" propertyB: bbb\n" +
"theirtag:\n" +
" bbb: 111\n" +
" comps:\n" +
" - propA: aaa\n" +
" prop: bbb\n";
public static void main(String args[]) {
MyYaml yaml = new MyYaml();
MyClass data = yaml.loadContainedAs(new StringReader(input), MyClass.class, "mytag");
System.out.println("description = " + data.description);
for (MyClass.MyComponent c : data.mycomponents) {
System.out.println("component: (" + c.propertyA + ", " + c.propertyB + ")");
}
}
}
过滤代码并不完全正确,例如,如果
mytag
是某处的值或序列项,则会失败。您需要跟踪解析器状态以确保当前标量是映射键。我没有实现它,因为代码已经足够长了。
这个比较简单,只需要做:
class MyDoc {
MyClass mytag;
}
class MyClass {
String description;
List<MyComponent> mycomponents:
}
然后加载您自己的 Representer,如...
class SomeParser {
private Yaml snakeyaml = new Yaml(new Constructor(MyDoc.class, getLoaderOptions()), getMyRepresenter());
private static Representer getMyRepresenter() {
DumperOptions options = new DumperOptions();
Representer representer = new Representer(options);
// we aren't that strict
representer.getPropertyUtils().setSkipMissingProperties(true);
return representer;
}
...
}
这样做你只会放弃所有其他你不关心的领域。
如果您希望入口点是动态 key,而不是深度,您应该能够使用
TypeDescription
上的自定义 MyDoc
来映射它:
class SomeParser {
private Yaml snakeyaml = new Yaml(new Constructor(getDescriptor(), getLoaderOptions(), null, getMyRepresenter());
private static TypeDescription getDescriptor() {
TypeDescription myDesc = new TypeDescription(MyDoc.class);
myDesc.substituteProperty("notmytag", MyClass.class, "getMyTag", "setMyTag");
return myDesc
}
...
}
如果您想要动态深度,我认为您可以创建一个自定义构造函数来检查值并根据值名称向下走,但我需要对此进行测试。