支持继承的YAML或JSON库

时间:2017-03-21 20:19:09

标签: json jackson yaml

我们正在建立一项服务。它必须从文件中读取配置。我们目前正在使用YAML和Jackson对YAML进行反序列化。我们的YAML文件需要继承/扩展另一个YAML文件。例如:

extends: base.yaml

appName: my-awesome-app

...

因此部分配置存储在base.yaml中。有没有图书馆支持这个?如果允许从多个文件继承,则奖励积分。我们可以改为使用JSON而不是YAML。

2 个答案:

答案 0 :(得分:1)

JSON和YAML都无法包含文件。无论你做什么都将是一个预处理步骤,你将把base.yaml和你的实际文件放在一起。

这样做的粗略方法是:

#include base.yaml
appName: my-awesome-app

让这成为你的档案。加载后,您首先阅读第一行,如果它以#include开头,则将其替换为所包含文件的内容。你需要递归地做这件事。这基本上是C预处理器对C文件的处理和包含。

缺点是:

  • 即使两个文件都是有效的YAML,结果也可能不是。
  • 如果任一文件包含指令结束或文档结束标记(---...),您将在一个文件中最终得到两个单独的文档。
  • 您无法替换文件中base.yaml的任何值。

所以另一种方法是实际操作YAML结构。为此,您需要YAML解析器的API(在您的情况下为SnakeYAML)并使用它解析您的文件。您应该使用compose API:

private Node preprocess(final Reader myInput) {
    final Yaml yaml = new Yaml();
    final Node node = yaml.compose(myInput);
    processIncludes(node);
    return node;
}

private void processIncludes(final Node node) {
    if (node instanceof MappingNode) {
        final List<NodeTuple> values = ((MappingNode) node).getValue();
        for (final NodeTuple tuple: values) {
            if ("!include".equals(tuple.getKeyNode().getTag().getValue())) {
                final String includedFilePath =
                        ((ScalarNode) tuple.getValueNode()).getValue();
                final Node content = preprocess(new FileReader(includedFilePath));
                // now merge the content in your preferred way into the values list.
                // that will change the content of the node.
            }
        }
    }
}

public String executePreprocessor(final Reader source) {
    final Node node = preprocess(source);
    final StringWriter writer = new StringWriter();
    final DumperOptions dOptions = new DumperOptions()
    Serializer ser = new Serializer(new Emitter(writer, dOptions),
                                    new Resolver(), dOptions, null);
    ser.open();
    ser.serialize(node);
    ser.close();
    return writer.toString();
}

此代码将解析包含如下:

!include : base.yaml
appName: my-awesome-app

我使用了私有标记!include,因此不存在与任何普通映射关键字的名称冲突。注意!include背后的空间。我没有给代码合并包含的文件,因为我不知道你想如何处理重复的映射键。但实施起来应该不难。注意错误,我还没有测试过这段代码。

结果字符串可以作为杰克逊的输入。

答案 1 :(得分:0)

可能出于相同的愿望,我创建了此工具:jq-front

您可以按照以下语法并与yq命令结合使用。

extends: [ base.yaml ]

appName: my-awesome-app

...
$ yq -j . your.yaml | jq-front | yq -y .

请注意,由于该工具支持多重继承,因此您需要将文件名放在数组中进行扩展。

您可能不喜欢的要点

  • 有点慢。 (但是对于配置信息,因为可以一次将其转换为扩展文件,而且永远不会再成为原始文件,所以可能没问题)
  • 由于该工具依赖于*的{​​{1}}运算符,因此数组内的对象无法按预期方式运行。