如何使用 Snake YAML Java 加载多个 yaml 文件?

How do I load multiple yaml files using Snake YAML Java?

我在一个目录中有几个 yaml 文件。如何将它们全部加载到同一个 YAML 对象(映射)中?

# a.yaml
a: ValueA
c: ValueC

# b.yaml
a: ValueAA
b: ValueB

我要a.yaml,接着b.yaml。结果将是:

{ a: ValueAA, b: ValueB, c: ValueC }

我可以这样做的一种方法是将 a.yaml 和 b.yaml 的内容显式连接成一个字符串,然后加载合并后的字符串。我想知道我是否可以避免这种情况并使用 load() API 按顺序加载 2 个 yaml 文件。

我不知道 SnakeYAML 的细节,但你不能只连接两个文件 a.yamlb.yaml,即使它们在根级别都有映射。 这样做你会在你的映射中得到一个重复的键,并且根据 YAML 1.2(和 1.0)规范你不允许有重复的键,1.1 规范规定你应该在重复键和第一个值上得到警告(你表示你想要第二个)。

所以你必须在 Java 中解决这个问题,你可以通过从它们各自的文件加载文档并将从 a.yaml 加载的数据结构更新为来自 [=11] 的数据结构来解决这个问题=].

您也可以将文件连接成一个包含多个文档的文件,但为此必须用指令结束指示符 (---) 或文档结束指示符 ( ...)。您通常需要使用特殊的 "load-all" 函数来加载这样的多文档文件,从而生成从映射中加载的数据结构列表(或迭代器),然后您可以合并。

如果您以编程方式制作多文档文件,请确保检查文件是否以换行符结尾,否则附加 --- 并且下一个文件不会提供您期望的多文档流.

详见Anthon的回答。作为对想要工作片段的人的 FYI,这就是我所做的。

final Yaml yaml = new Yaml(configConstructor, representer);

try (
    final InputStream defaultYamlStream = new FileInputStream(settingsPath + "/cbench-default.yaml");
    final InputStream customerYamlStream = new FileInputStream(settingsPath + "/" + identifier + ".yaml");
    final InputStream fullYamlStream = new SequenceInputStream(defaultYamlStream, customerYamlStream);
) {
    //try
    parsedConfig = (BenchmarkConfig) yaml.load(fullYamlStream);

} catch (IOException ioe) {
    // ERROR
    System.out.println("Exception parsing the YAML configuration.");
    throw new RuntimeException("Exception parsing the YAML configuration.", ioe);
}

我正在创建一个串联文件流(在我的例子中是序列流)并使用 Anthon 推荐的负载 API,它工作正常。请记下文档结束标记。

这是一个如何使用小递归函数(写在Groovy中)的例子:

@Grapes(
    @Grab(group='org.yaml', module='snakeyaml', version='1.25')
)

import org.yaml.snakeyaml.Yaml

import static groovy.json.JsonOutput.prettyPrint
import static groovy.json.JsonOutput.toJson

def extension = new Yaml().load(("extension.yml" as File).text)
def original = new Yaml().load(("original.yml" as File).text)

println prettyPrint(toJson(original))
println "#############################"

iterateAndOverrite(extension, original)
extension = original

println prettyPrint(toJson(original))

private void iterateAndOverrite(extension, original){
    extension.each {
        if(it.value instanceof Map && original[it.key] != null){
            iterateAndOverrite(extension[it.key], original[it.key])
        } else {
             original[it.key] = extension[it.key]
        }
    }
}

我用它来覆盖和合并两个 yaml 配置,可能对某人有帮助:

 public static void update(Map<String,Object> existingMap,Map<String,Object> newMap){
    newMap.forEach((key, value) -> {
        if(value instanceof LinkedHashMap){
            LinkedHashMap<String,Object> newMapValue = (LinkedHashMap<String,Object>)value;
            LinkedHashMap<String,Object> existingMapValue = (LinkedHashMap<String,Object>)existingMap.getOrDefault(key,new LinkedHashMap<String,Object>());
            update(existingMapValue,newMapValue);
        }
        else{
            existingMap.put(key,value);
        }
    });
}

/**
 * Reads application.yaml file and loads it to config.
 */
public static void init() throws Exception {
    Yaml yaml = new Yaml();

    //loading config file 1
    InputStream inputStream = ConnectionUtil.class
                .getClassLoader()
                .getResourceAsStream("application.yaml");
    config = yaml.load(inputStream);

    //loading config file 2
    String configFile = System.getProperty("config");
    if(configFile!=null){
        File file = new File(configFile);
        if(file.exists()){
            InputStream inputStream2 = new FileInputStream(file);
            Map<String,Object> config2 = yaml.load(inputStream2);
            update(config,config2);
        }
        else
        {
            throw new Exception("invalid config file path"); 
        
        }
    }

    System.out.println(yaml.dump(config));

}