簡體   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