杰克逊YAML:对锚点和参考文献的支持

时间:2016-10-16 19:45:58

标签: jackson yaml

我正在研究将YAML用于某种复杂的元数据语言。如果我们可以使用YAML's anchors and references,这将有助于使文档更小,更简单。我写了一些测试代码,似乎表明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"只是"路德维希"。参考符号(' *')已被剥离,但该值是引用的值,而不是它所引用的元素。

我使用Jackson版本2.8.3和SnakeYaml版本1.17。我被限制使用杰克逊,因为这只是一个更大的项目的一部分,该项目已经使用杰克逊作为JSON。

我真正想要的是杰克逊能否自动解析引用并复制引用的值。在我的例子中,这意味着" set_two.snare"将是"路德维希超级6.5x15"。

如果我无法获得我的第一选择,那么我希望Jackson保留锚点和引用,以便我可以手动后处理节点树并自行解析引用。例如,当我看到" set_two.snare"的值时是" * ludwig",我可以在树上搜索一个锚点为"& ludwig"并制作该节点的副本。

如果有答案,我觉得它可能涉及" com.fasterxml.jackson.dataformat.yaml.YAMLParser.Feature"不知怎的。遗憾的是,我无法找到有关这些功能的文档(如果存在),这些文档将启用我正在寻找的行为。

3 个答案:

答案 0 :(得分:1)

我只是想让Jackson的锚/别名工作...而失败了。 您可以看到here未实现基于别名的ID支持。 _currentAnchor实例变量是由getObjectId()设置和公开的,但是我没有找到一种实用的方法来挂接到Jackson以使用该方法。这不是我第一次使用Jacksons Object-Id解析体系结构。建议不要花太多时间。

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

答案 1 :(得分:0)

首先,杰克逊确实支持YAML锚点和引用,至少在程度上它们与杰克逊如何使用@JsonIdentityInfo支持对象ID引用有关:限制是你不能 - 例如 - 引用一个键/ value对am am。

但是,身份标识/引用处理仅对通过使用@JsonIdentityInfo进行批注指定的类型和属性启用。 因此,您必须注释可能被引用的类型或属性(不需要同时执行这两种操作)。

这里可能有用的一件事是考虑到Jackson的Object Id处理对于所有格式非常相似:所以尽管jackson-dataformat-yaml确实暴露了#34; native" YAML具有的对象(和类型)ID(和JSON没有),在数据绑定级别的处理是相同的。因此,如果您可以使用JSON(添加额外的id属性)使对象ID /引用工作,它也将与YAML一起使用。

还有一件事是相关的:YAMLParser.Feature.USE_NATIVE_OBJECT_ID确定在编写YAML时如何表达引用和id - 默认情况下,它使用本机锚点,但可以关闭它以使用"类JSON"平原属性。

我希望这会有所帮助。如需其他帮助,最好的地方是jackson-users邮件列表。

答案 2 :(得分:0)

由于Jackson YAML不支持锚和引用(issue),因此最好退回SnakeYaml来解析YAML文件,然后将其转换为Jackson格式,以利用Jackson灵活性的优势。与杰克逊不同,Snake Yaml支持锚点(尽管杰克逊在后台使用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解析器中缺少的。