繁体   English   中英

Jackson YAML:支持锚点和引用

[英]Jackson YAML: support for anchors and references

我正在研究将 YAML 用于有点复杂的元数据语言。 如果我们可以使用YAML 的锚点和引用,将有助于使文档更小、更简单。 我编写了一些测试代码,似乎表明 Jackson 的 YAML 实现不支持此功能(和/或未显示 SnakeYAML 对此功能的支持)。

这是我的测试 YAML 文件:

set_one:
  bass: tama rockstar 22x16
  snare: &ludwig ludwig supralight 6.5x15
  tom1: tama rockstar 12x11
  tom2: tama rockstar 16x16

set_two:
  snare: *ludwig

我像这样解析这个文件:

    ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
    FileInputStream fis = null;

    try
    {
        fis = new FileInputStream(file);
        JsonNode nodeTree = mapper.readTree(fis);
        examineObject(nodeTree, 0);
    }
    ...

这是我的“examineObject()”方法的输出(你可能猜到它是做什么的):

key = "set_one", type = OBJECT
  key = "bass", type = STRING, value = "tama rockstar 22x16"
  key = "snare", type = STRING, value = "ludwig supralight 6.5x15"
  key = "tom1", type = STRING, value = "tama rockstar 12x11"
  key = "tom2", type = STRING, value = "tama rockstar 16x16"
key = "set_two", type = OBJECT
  key = "snare", type = STRING, value = "ludwig"

很明显,有足够的知识可以从“set_one.snare”中省略锚值,但是在调试器中,我无法在 JsonNode 的任何位置找到该元素的值。 真正的问题是“set_two.snare”的值只是“ludwig”。 引用符号 ('*') 已被删除,但值是引用的值,而不是引用的元素。

我使用的是 Jackson 2.8.3 版和 SnakeYaml 1.17 版。 我只能使用 Jackson,因为这只是一个更大项目的一部分,该项目已经将 Jackson 用于 JSON。

我真正想要的是 Jackson 是否可以自动解析引用并制作引用值的副本。 在我的示例中,这意味着“set_two.snare”的值将是“ludwig supralight 6.5x15”。

如果我不能得到我的第一选择,那么我希望 Jackson 保留锚点和引用,以便我可以手动对节点树进行后处理并自己解析引用。 例如,当我看到“set_two.snare”的值为“*ludwig”时,我可以在树中搜索锚点为“&ludwig”的节点,然后复制该节点。

如果有答案,我觉得它可能会以某种方式涉及“com.fasterxml.jackson.dataformat.yaml.YAMLParser.Feature”类。 不幸的是,我找不到任何有关功能(如果存在)的文档,这些文档将启用我正在寻找的行为。

首先,杰克逊实际上确实支持 YAML 锚点和引用,至少在某种程度上他们与杰克逊如何使用@JsonIdentityInfo支持对象 Id 引用一起@JsonIdentityInfo :限制是你不能 - 例如 - 引用一个键/值对 am Object .

但是,仅对通过使用@JsonIdentityInfo注释 then 指定的类型和属性启用标识 ID/引用处理。 因此,您必须注释可能被引用的类型或属性(无需同时进行)。

这里可能有帮助的一件事是考虑杰克逊处理的对象 ID 对于所有格式都非常相似:因此,尽管jackson-dataformat-yaml确实公开了 YAML 具有(而 JSON 没有)的“本机”对象(和类型)ID ,数据绑定级别的处理是相同的。 因此,如果您可以使 Object Id/References 与 JSON(它添加额外的 id 属性)一起使用,它也将与 YAML 一起使用。

还有一件额外的事情是相关的: YAMLParser.Feature.USE_NATIVE_OBJECT_ID它决定了在编写 YAML 时引用和 id 的表达方式——默认情况下,它使用本机锚点,但可以关闭它以使用“类似 JSON”的普通属性.

我希望这会有所帮助。 要获得更多帮助,最好的地方是jackson-users邮件列表。

由于 Jackson YAML 不支持 Anchors 和引用(issue ),最好回退到 SnakeYaml 来解析 YAML 文件,然后将其转换为 Jackson 的格式以利用 Jackson 的灵活性优势。 Snake Yaml 支持锚点,与 Jackson 不同(尽管 Jackson 在后台使用 snakeyaml 来解析 Yaml 文件)。

import java.io.{File, FileInputStream, FileReader}
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.databind.{JsonNode, ObjectMapper}
import org.yaml.snakeyaml.Yaml 

// Parsing the YAML file with SnakeYAML - since Jackson Parser does not support Anchors and references
val ios = new FileInputStream(new File(yamlFilePath))
val yaml = new Yaml()
val mapper = new ObjectMapper().registerModules(DefaultScalaModule)
val yamlObj = yaml.loadAs(ios, classOf[Any])
    
// Converting the YAML to Jackson YAML - since it has more flexibility
val jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(yamlObj) // Formats YAML to a pretty printed JSON string - easy to read
val jsonObj = mapper.readTree(jsonString)

生成的 jsonObj 是一个 JsonNode,它是 Jackson 中的核心数据格式之一。 我们可以使用 as & get 方法轻松遍历 YAML 文件:

jsonObj.at("/parent/first_level_child/second_level_child")
jsonObj.get("key")

由于 YAML 格式非常接近 JSON 格式,因此在大多数情况下不应该丢失数据。 此外,Jackson 的 JsonNode 格式允许我们使用 Jackson-Json 解析器的额外灵活性——这是 Jackson-YAML 解析器所缺少的。

我只是试图让锚点/别名在杰克逊工作......但失败了。 您可以在此处看到未实现基于别名的 id 支持。 _currentAnchor 实例变量由 getObjectId() 设置和公开,但我没有找到一种实用的方法来挂钩 Jackson 以使用该方法。 这不是我第一次与 Jacksons Object-Id 解析架构发生冲突。 建议不要花太多时间在上面。

我的解决方案是直接使用snakeyaml 库。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM