代码之家  ›  专栏  ›  技术社区  ›  The Illusive Man

如果不是所有键都有相同的子键,如何反序列化YAML?

  •  0
  • The Illusive Man  · 技术社区  · 4 年前

    假设这个YAML:

    base:
      foo:
        a: 'a'
        b: 'b'
        c: 'c'
      bar:
        a: 'a'
        b: 'b'
        c: 'c'
      baz:
        a: 'a'
        t: 't'
        z: 'z'
    

    我找到的所有关于反序列化YAML的文档都暗示每个键都持有相同的数据。

    例如,忽略 baz ,我会使用以下数据类:

    data class Base(val base: Data)
    data class Data(val foo: Values, val bar: Values)
    data class Values(val a: String, val b: String, val c :String)
    

    我的文件在 foo,bar,baz ,而且其中许多不共享相同的密钥(甚至不共享相同的数字),那么我如何定义数据类来序列化它呢?

    我尝试了以下方法:

    data class Base(val base: Data)
    data class Data(val d: List<Map<String, Values>>)
    data class Values(val v: List<Map<String, String>>)
    

    但它抱怨,因为它正在寻找与钥匙完全匹配的钥匙。

    1 回复  |  直到 4 年前
        1
  •  1
  •   Karsten Gabriel    4 年前

    我假设你正在使用Jackson对象映射器 support for yaml dataformat ( com.fasterxml.jackson.dataformat:jackson-dataformat-yaml ), 还有 Kotlin module ( com.fasterxml.jackson.module:jackson-module-kotlin ).

    数据类 Values 在您的示例中,不匹配所有对象,因为例如,它不包含 t 和 z 包含在 base.baz 价值

    一种可能性是扩大 价值观 要包含所有可能存在的值,请忽略任何不存在的值:

    data class Values(
        val a: String?,
        val b: String?,
        val c: String?,
        val t: String?,
        val z: String?,
    )
    

    所有值都可以为null,以允许它们不存在。

    如果不需要所有值,还可以添加 @JsonIgnoreProperties(ignoreUnknown = true) 注释,告诉Jackson,如果yaml中有未在数据类中声明的值,这不是问题:

    @JsonIgnoreProperties(ignoreUnknown = true)
    data class Values(...
    

    如果这不够灵活,可以将所有内容反序列化为映射:

    val objectMapper = ObjectMapper(YAMLFactory()).registerKotlinModule()
    val content: Map<String, *>  = objectMapper.readValue(theYaml)
    

    请注意,在本例中,您没有向Jackson提供类型信息,因此没有提供任何非平凡对象(即非 String , Int , ...) 在你的yaml中,它们被反序列化为地图。此外,您自己没有任何类型信息,需要检查每个元素是否是预期类型的实例,并且必须手动强制转换它。 例如,获取元素 base.foo.b 然后你可以写:

    val base = content["base"]
    val foo = (base as Map<String, *>)["foo"]
    val b = (foo as Map<String, *>)["b"]
    

    如果 b 比如说,它属于非平凡类型 Something ,可以使用

    val bWithType: Something = objectMapper.convertValue(b)
    

    但请注意,这意味着 B 再次序列化,然后反序列化,以获得正确的类型。因此,更好的解决方案是更灵活的方法 价值观 上等生。只有在必要时才应使用替代方案。