使用 SnakeYAML 的嵌套构造

Posted

技术标签:

【中文标题】使用 SnakeYAML 的嵌套构造【英文标题】:Nested Constructs with SnakeYAML 【发布时间】:2013-08-27 02:08:05 【问题描述】:

我正在考虑使用带有 SnakeYAML 的自定义构造,但不确定如何实现嵌套。我使用this example 作为参考。

在链接的例子中,相关的 YAML 和 Construct 是,

- !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;
    

现在,让我们将 YAML 更改为,

- !circle
  center: !point
    x: 73
    y: 129
  radius: 7

我想使用另一个AbstractConstruct 来解析该!point 对象,但在ConstructCircle 上下文中进行。我对Construct/Node 关系的理解非常不稳定,我不知道如何在自定义构造函数中使用自定义构造函数。有什么想法或资源吗?

【问题讨论】:

【参考方案1】:

在使用 SnakeYaml 完成更多项目后,好吧。我想我终于明白你的问题了。嵌套由 SnakeYaml 自动处理。您无需担心这一点。您需要做的就是为 !point 创建另一个 Construct,并将其添加到自定义构造函数类中的地图 yamlConstructors 中。这将在任何地方启用 !point 标签。

点构造可能如下所示:

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");
   

您的 Yaml 文件将如下所示:

!circle
center: !point (73,179)
radius: 7

我认为这个输出看起来好多了。如果在 yaml 中添加 ImplicitResolver:

yaml.addImplicitResolver(new Tag("!point"), Pattern.compile("\\((\\d+),(\\d+\\)"),"(");

那么 yaml 将如下所示。

!circle
center: (73,179)
radius: 7

或者,您可以放弃编写新的 Construct 并让 Point 遵循 bean 模式并使用类似的东西。

!circle
center !!package.goes.here.Point
  x: 73
  y: 179
radius: 7

无论如何希望这个答案比我上一个答案更清楚。

【讨论】:

【参考方案2】:

我写了一个快速而肮脏的customConstructMapping() 来解析您的嵌套构造 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;

【讨论】:

【参考方案3】:

Snake Yaml 应该自己处理所有的嵌套。您只需确保将所有 AbstractConstructs 添加到自定义构造函数内的 yamlConstructors 字段中。

【讨论】:

以上是关于使用 SnakeYAML 的嵌套构造的主要内容,如果未能解决你的问题,请参考以下文章

使用SnakeYAML Java将用户作为嵌套在块序列中的块映射添加到yml文件

包含来自snakeyaml 的YAML 文件

SnakeYAML:解析时如何禁用下划线剥离?

在 OSGi 下使用 SnakeYaml?

Snakeyaml - 如何对流样式进行自定义控制

使用snakeyaml 解析bean 列表