Reputation:
I'm looking at using custom constructs with SnakeYAML and am not sure how to implement nesting. I'm using this example as reference.
In the linked example, the relevant YAML and Construct is,
- !circle
center: {x: 73, y: 129}
radius: 7
private class ConstructCircle extends AbstractConstruct {
@SuppressWarnings("unchecked")
public Object construct(Node node) {
MappingNode mnode = (MappingNode) node;
Map<Object, Object> values = constructMapping(mnode);
Circle circle = new Circle((Map<String, Integer>) values.get("center"), (Integer) values.get("radius"));
return circle;
}
}
Now, let's change the YAML to,
- !circle
center: !point
x: 73
y: 129
radius: 7
I would like to use another AbstractConstruct
to parse that !point
object, but do it within the ConstructCircle
context. My understanding of the Construct/Node
relationship is pretty shaky and I'm at a loss for how to use a custom constructor within a custom constructor. Any thoughts or resources?
Upvotes: 6
Views: 5560
Reputation: 129
Alright after doing a few more projects with SnakeYaml. I think I finally understand your question. Nesting is taken care of automatically by SnakeYaml. You do not need to worry about that. All you would need to do is create another Construct for !point, and add it to the map yamlConstructors in your custom constructor class. This will enable the !point tag anywhere.
the point construct could look something like this:
class PointConstruct extends AbstractConstruct{
public Object construct(Node node){
String line = (String)constructScalar((ScalarNode)node);
Pattern pointPattern = Pattern.compile("\\((\\d+),(\\d+\\)");
Matcher m = pointPattern.matcher(line);
if(m.find())
return new Point(m.group(1),m.group(2));
throw new RuntimeException("Could not parse a point");
}
}
Your Yaml file would then look like this:
!circle
center: !point (73,179)
radius: 7
I think this output looks a lot nicer. If you add an ImplicitResolver to the yaml:
yaml.addImplicitResolver(new Tag("!point"), Pattern.compile("\\((\\d+),(\\d+\\)"),"(");
Then the yaml would look like this.
!circle
center: (73,179)
radius: 7
Alternatively you could forgo writing a new Construct and just have Point follow the bean pattern and use something like this.
!circle
center !!package.goes.here.Point
x: 73
y: 179
radius: 7
anyway Hope this answer is a bit more clear than my last one.
Upvotes: 1
Reputation: 129
Snake Yaml should take care of nesting all by it self. You just need to make sure to add all your AbstractConstructs to the yamlConstructors field inside your Custom Constructor.
Upvotes: 0
Reputation: 5817
I have wrote a quick and dirty customConstructMapping()
to parse your Nested Constructs YAML.
public Map<Object, Object> customConstructMapping(MappingNode mnode) {
Map<Object, Object> values = new HashMap<Object, Object>();
Map<String, Integer> center = new HashMap<String, Integer>();
List<NodeTuple> tuples = mnode.getValue();
for (NodeTuple tuple : tuples) {
ScalarNode knode = (ScalarNode) tuple.getKeyNode();
String key = knode.getValue();
Node vnode = tuple.getValueNode();
if (vnode instanceof MappingNode) {
MappingNode nvnode = (MappingNode) vnode;
if ("!point".equals(nvnode.getTag().getValue())) {
List<NodeTuple> vtuples = nvnode.getValue();
for (NodeTuple vtuple : vtuples) {
ScalarNode vknode = (ScalarNode) vtuple.getKeyNode();
ScalarNode vvnode = (ScalarNode) vtuple.getValueNode();
Integer val = Integer.parseInt(vvnode.getValue());
center.put(vknode.getValue(), val);
}
values.put(key, center);
}
} else if (vnode instanceof ScalarNode) {
Integer val = Integer.parseInt(((ScalarNode) vnode).getValue());
values.put(key, val);
}
}
return values;
}
Upvotes: 0