I want to read a YAML document to a map of custom objects (instead of maps, which snakeYaml does by default). So this:
19:
typeID: 2
limit: 300
20:
typ
To keep type safety, you need to take control of the root node construction. To do this, you can use the rootTag property of the Constructor to mark the root node, fix the type of the children, and construct the map.
The custom constructor should look like the following
public static class MyConstructor extends Constructor {
private TypeDescription itemType = new TypeDescription(Item.class);
public MyConstructor() {
this.rootTag = new Tag("myRoot");
this.addTypeDescription(itemType);
}
@Override
protected Object constructObject(Node node) {
if ("myRoot".equals(node.getTag().getValue()) && node instanceof MappingNode) {
MappingNode mNode = (MappingNode) node;
return mNode.getValue().stream().collect(
Collectors.toMap(
t -> super.constructObject(t.getKeyNode()),
t -> {
Node child = t.getValueNode();
child.setType(itemType.getType());
return super.constructObject(child);
}
)
);
} else {
return super.constructObject(node);
}
}
}
Here's fully functional example
import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.Tag;
import java.util.Map;
import java.util.stream.Collectors;
public class Foo {
public static void main(String[] args) {
String theYaml = "19:\n" +
" typeID: 2\n" +
" limit: 300\n" +
"20:\n" +
" typeID: 8\n" +
" limit: 100";
Yaml yaml = new Yaml(new MyConstructor());
Map data = yaml.load(theYaml);
System.out.println(data);
}
public static class Item {
private int typeID, limit;
public int getTypeID() {
return typeID;
}
public void setTypeID(int typeID) {
this.typeID = typeID;
}
public int getLimit() {
return limit;
}
public void setLimit(int limit) {
this.limit = limit;
}
}
public static class MyConstructor extends Constructor {
private TypeDescription itemType = new TypeDescription(Item.class);
public MyConstructor() {
this.rootTag = new Tag("myRoot");
this.addTypeDescription(itemType);
}
@Override
protected Object constructObject(Node node) {
if ("myRoot".equals(node.getTag().getValue()) && node instanceof MappingNode) {
MappingNode mNode = (MappingNode) node;
return mNode.getValue().stream().collect(
Collectors.toMap(
t -> super.constructObject(t.getKeyNode()),
t -> {
Node child = t.getValueNode();
child.setType(itemType.getType());
return super.constructObject(child);
}
)
);
} else {
return super.constructObject(node);
}
}
}
}
If you don't care about the types, and just want duck typing (everything is a map), You can load with no settings.
Java
Map data = new Yaml().load(yamldata);
Scala
val data: java.util.Map[String, Any] = new Yaml().load(yamlData)