简体   繁体   English

Jackson YAML:支持锚点和引用

[英]Jackson YAML: support for anchors and references

I'm investigating the use of YAML for a somewhat complicated metadata language.我正在研究将 YAML 用于有点复杂的元数据语言。 It would help to make the documents smaller and less complex if we could use YAML's anchors and references .如果我们可以使用YAML 的锚点和引用,将有助于使文档更小、更简单。 I've written some test code that seems to show that Jackson's YAML implementation doesn't support this feature (and/or doesn't surface SnakeYAML's support for this feature).我编写了一些测试代码,似乎表明 Jackson 的 YAML 实现不支持此功能(和/或未显示 SnakeYAML 对此功能的支持)。

Here is my test YAML file:这是我的测试 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

I'm parsing this file like so:我像这样解析这个文件:

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

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

Here is the out from my "examineObject()" method (you can probably guess what it does):这是我的“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"

Clearly something knew enough to omit the anchor value from "set_one.snare" but, in the debugger, I can't find that value anywhere in the JsonNode for this element.很明显,有足够的知识可以从“set_one.snare”中省略锚值,但是在调试器中,我无法在 JsonNode 的任何位置找到该元素的值。 The real problem is that the value of "set_two.snare" is just "ludwig".真正的问题是“set_two.snare”的值只是“ludwig”。 The reference symbol ('*') has been stripped, but the value is that of the reference and not the element it is referring to.引用符号 ('*') 已被删除,但值是引用的值,而不是引用的元素。

I'm using Jackson version 2.8.3 and SnakeYaml version 1.17.我使用的是 Jackson 2.8.3 版和 SnakeYaml 1.17 版。 I am constrained to using Jackson as this is only part of a much bigger project which already uses Jackson for JSON.我只能使用 Jackson,因为这只是一个更大项目的一部分,该项目已经将 Jackson 用于 JSON。

What I would really like is if Jackson could automatically resolve references and make a copy of the referenced value.我真正想要的是 Jackson 是否可以自动解析引用并制作引用值的副本。 In my example this would mean that the value of "set_two.snare" would be "ludwig supralight 6.5x15".在我的示例中,这意味着“set_two.snare”的值将是“ludwig supralight 6.5x15”。

If I can't get my first choice then I would like Jackson to preserve both the anchors and the references so that I could manually post-process the node tree and resolve the references myself.如果我不能得到我的第一选择,那么我希望 Jackson 保留锚点和引用,以便我可以手动对节点树进行后处理并自己解析引用。 For example, when I saw that the value of "set_two.snare" was "*ludwig", I could search the tree for a node with an anchor of "&ludwig" and make a copy of that node.例如,当我看到“set_two.snare”的值为“*ludwig”时,我可以在树中搜索锚点为“&ludwig”的节点,然后复制该节点。

If there is an answer, I have the feeling that it will probably involve the "com.fasterxml.jackson.dataformat.yaml.YAMLParser.Feature" class somehow.如果有答案,我觉得它可能会以某种方式涉及“com.fasterxml.jackson.dataformat.yaml.YAMLParser.Feature”类。 Unfortunately I can't find any documentation on the features (if they exist) that will enable the behavior I am looking for.不幸的是,我找不到任何有关功能(如果存在)的文档,这些文档将启用我正在寻找的行为。

First, Jackson actually does support YAML anchors and references, at least to degree they work with how Jackson supports Object Id references with @JsonIdentityInfo : limitation being that you can not -- for example -- refer to one key/value pair of am Object.首先,杰克逊实际上确实支持 YAML 锚点和引用,至少在某种程度上他们与杰克逊如何使用@JsonIdentityInfo支持对象 Id 引用一起@JsonIdentityInfo :限制是你不能 - 例如 - 引用一个键/值对 am Object .

But identity id/reference handling is only enabled for types and properties specified by annotating then with @JsonIdentityInfo .但是,仅对通过使用@JsonIdentityInfo注释 then 指定的类型和属性启用标识 ID/引用处理。 So you have to annotate either types that may be referenced, or properties (no need to do both).因此,您必须注释可能被引用的类型或属性(无需同时进行)。

One thing that may help here is to consider that Object Id handling by Jackson is very similar for all formats: so although jackson-dataformat-yaml does expose "native" Object (and Type) Ids that YAML has (and JSON does not have), handling at databinding level is identical.这里可能有帮助的一件事是考虑杰克逊处理的对象 ID 对于所有格式都非常相似:因此,尽管jackson-dataformat-yaml确实公开了 YAML 具有(而 JSON 没有)的“本机”对象(和类型)ID ,数据绑定级别的处理是相同的。 So if you can make Object Id/References work with JSON (which adds extra id property), it will work with YAML as well.因此,如果您可以使 Object Id/References 与 JSON(它添加额外的 id 属性)一起使用,它也将与 YAML 一起使用。

There is one extra thing that is related: YAMLParser.Feature.USE_NATIVE_OBJECT_ID which determines how references and ids are expressed when writing YAML -- by default, it uses native anchors, but it can be turned off to use "JSON-like" plain properties.还有一件额外的事情是相关的: YAMLParser.Feature.USE_NATIVE_OBJECT_ID它决定了在编写 YAML 时引用和 id 的表达方式——默认情况下,它使用本机锚点,但可以关闭它以使用“类似 JSON”的普通属性.

I hope this helps.我希望这会有所帮助。 For additional help the best place would be jackson-users mailing list.要获得更多帮助,最好的地方是jackson-users邮件列表。

Since Jackson YAML does not support Anchors and references (issue ), its best to fall-back to SnakeYaml to parse the YAML file & then convert it to Jackson's format to leverage the goodness of Jackson's flexibility.由于 Jackson YAML 不支持 Anchors 和引用(issue ),最好回退到 SnakeYaml 来解析 YAML 文件,然后将其转换为 Jackson 的格式以利用 Jackson 的灵活性优势。 Snake Yaml has support for anchors, unlike Jackson (though Jackson uses snakeyaml in the background to parse Yaml files). 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)

The resulting jsonObj is a JsonNode, which is one of the core data formats in Jackson.生成的 jsonObj 是一个 JsonNode,它是 Jackson 中的核心数据格式之一。 We can use the as & get methods to easily traverse through the YAML file:我们可以使用 as & get 方法轻松遍历 YAML 文件:

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

As YAML format is very close to JSON format there should not be data loss in most cases.由于 YAML 格式非常接近 JSON 格式,因此在大多数情况下不应该丢失数据。 Moreover, Jackson's JsonNode format allows us to use the additional flexibility from Jackson-Json parsers - which is missing in Jackson-YAML parsers.此外,Jackson 的 JsonNode 格式允许我们使用 Jackson-Json 解析器的额外灵活性——这是 Jackson-YAML 解析器所缺少的。

I just tried to make anchors/aliases work in Jackson... and failed.我只是试图让锚点/别名在杰克逊工作......但失败了。 You can see here that alias based id support is not implemented.您可以在此处看到未实现基于别名的 id 支持。 The _currentAnchor instance variable is set and exposed by getObjectId(), but I didn't find a practical way to hook into Jackson to use that method. _currentAnchor 实例变量由 getObjectId() 设置和公开,但我没有找到一种实用的方法来挂钩 Jackson 以使用该方法。 This is not my first fight with Jacksons Object-Id resolution architecture.这不是我第一次与 Jacksons Object-Id 解析架构发生冲突。 Be advised not the spend too much time with it.建议不要花太多时间在上面。

My solution was using the snakeyaml library directly.我的解决方案是直接使用snakeyaml 库。

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

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