精确编辑yaml

Edit yaml in a precise way

我想在 java 中编辑一个 yaml,(我正在编辑 BungeeCord 配置文件以创建一个系统,该系统启动具有由用户端口定义的 bungeecord 实例)但以一种精确的方式,我需要在 yaml 文件中准确地写这个:

listeners:
- query_port: 25577
  motd: '&1Another Bungee server'
  tab_list: GLOBAL_PING
  query_enabled: false
  proxy_protocol: false
  ping_passthrough: false
  priorities:
  - lobby
  bind_local_address: true
  host: 0.0.0.0:25577
  max_players: 1
  tab_size: 60
  force_default_server: false

我做了一些非常相似的事情,但是有一个竖线阻止 BungeeCord 读取文件:

public class ProxyYaml {
    HashMap<String, Object> entries = new HashMap<String, Object>();
    
    public ProxyYaml() {
        entries.put("query_port", 25577);
        entries.put("motd", "Hey Guys");
        entries.put("tab_list", "GLOBAL_PING");
        entries.put("query_enabled", false);
        entries.put("proxy_protocol", false);
        entries.put("ping_passthrough", false);
        entries.put("priorities", Arrays.asList("lobby"));
        entries.put("bind_local_address", true);
        entries.put("host", "0.0.0.0:25577");
        entries.put("max_players", 1);
        entries.put("tab_size", 60);
        entries.put("force_default_server", false);
    }
    
    public ArrayList<String> getProperties() {
        ArrayList<String> finalString = new ArrayList<String>();
        for(String entry : entries.keySet()) {
            finalString.add(entry + ": " + entries.get(entry).toString());
        }
        return finalString;
    }
}

(我正在使用 SimpleYaml api 但如果需要我可以更改 api )

                    File propsProxyFile = new File(path + "config.yml");
                    YamlFile propsProxyYaml = new YamlFile(propsProxyFile);
                    try {
                        propsProxyYaml.load(propsProxyFile);
                        propsProxyYaml.set("listeners", Arrays.asList(new ProxyYaml().getProperties()));
                        propsProxyYaml.save(propsProxyFile);
                    } catch (IOException | InvalidConfigurationException e) {
                        System.out.println(MainNetwork.logo + "Can't load proxy properties file");
                        return;
                    }

有代码输出(带竖线):

listeners:
- |
  query_port: 25577
  motd: '&1Another Bungee server'
  tab_list: GLOBAL_PING
  query_enabled: false
  proxy_protocol: false
  ping_passthrough: false
  priorities:
  - lobby
  bind_local_address: true
  host: 0.0.0.0:25577
  max_players: 1
  tab_size: 60
  force_default_server: false

请问我该怎么办?

竖线字符 (|) 启动 YAML 块标量。这意味着以下所有行都是文字字符串,不受进一步 YAML 解析的影响。

您的代码中发生了很多奇怪的事情,让我们回顾一下:


    public ArrayList<String> getProperties() {
        ArrayList<String> finalString = new ArrayList<String>();
        for(String entry : entries.keySet()) {
            finalString.add(entry + ": " + entries.get(entry).toString());
        }
        return finalString;
    }

您在此处手动将映射转换为字符串列表。你为什么这样做?您希望最终的 YAML 文件包含键值对作为映射,因此您不应将它们转换为字符串列表。

让我们用一个简单的例子来讨论这里发生的事情: 假设我们有这个 java 地图:

Map<String, String> value = Map.of("foo", "bar");

如果我们直接将其序列化为 YAML,我们将得到

foo: bar

但如果我们通过您的方法进行管道传输,我们将得到

- "foo: bar"

即一个标量值的序列——因为我们手动将映射条目转换为字符串!这不是你想要的。


    propsProxyYaml.set("listeners", Arrays.asList(new ProxyYaml().getProperties()));

您在 getProperties() 的 return 值上调用 Arrays.asList,其类型为 ArrayList<String>,因此 asList 将 return 的值输入 List<ArrayList<String>>,其中有一个条目是您构建的列表。目前还不清楚为什么要调用 Arrays.asList 除非你想要一个列表列表。根据所需的 YAML 输出,这不是您想要的。


现在让我们讨论一下 set 方法的作用。我不太了解 SimpleYAML,坦率地说,它的文档很糟糕,因为它基本上只包含自动生成的 API 文档。该方法的第一个参数名为path,这意味着它不是一个简单的映射键。

显然 发生的是 List<ArrayList<String>> 值被转换为具有一个标量值的 YAML 序列,并且该标量值包含您生成的所有字符串值,由换行符分隔。没有适当的文档,就不可能说这是预期的行为还是错误。无论如何,这没有任何意义。


现在,实际的 YAML 在其根部包含一个具有一个条目的映射,其键是标量 listeners。它的值是一个包含另一个映射的列表。这意味着,您实际想要序列化的类型是

Map<String, List<Map<String, Object>>>

我建议您简单地构建一个这种类型的值并使用 SnakeYAML API,它确实 proper documentation 关于其序列化系统的工作原理。无论如何,SimpleYAML 在幕后使用 SnakeYAML,似乎没有理由使用记录不详的 API 而不是记录良好的行为,而不是使用记录良好的行为。

您还可以创建自定义 Java class 而不是使用 Map。然后键将变成 class 字段。