1. 概述
JSON-LD 是一種基於 JSON 的 RDF 格式,用於表示 鏈接數據。它允許將現有的 JSON 對象擴展為具有超媒體能力,換句話説,具備以機器可讀的方式包含鏈接的能力。
在本教程中,我們將探討一些基於 Jackson 的選項,用於直接將 JSON-LD 格式序列化和反序列化為 POJO。我們還將涵蓋 JSON-LD 的基本概念,以便我們理解示例。
2. 基本概念
首次看到 JSON-LD 文檔時,我們會注意到一些成員名稱以 @ 字符開頭。 這些是 JSON-LD 關鍵字,它們的取值有助於我們理解文檔的其餘部分。
為了在 JSON-LD 的世界中進行導航,並理解本教程,我們需要了解四個關鍵字:
- @context 是包含一個鍵值映射的 JSON 對象,該映射包含了用於解釋文檔所需的一切;
- @vocab 是 @context 中的一個可能鍵,它引入了默認詞彙,從而使 @context 對象更短;
- @id 是用於標識鏈接的關鍵字,既可以作為資源屬性來直接鏈接到資源本身,也可以作為 @type 的值來標記任何字段為鏈接;
- @type 是用於標識資源類型(在資源級別或在 @context 中);例如,定義嵌入資源的類型;
3. Java 中序列化
在繼續之前,我們應該回顧一下之前的教程,以刷新我們對 Jackson 的 ObjectMapper、Jackson 註解以及自定義 Jackson 序列化器的記憶。
熟悉 Jackson 之後,我們可能會意識到,可以使用 @id 和 @type 註解,輕鬆地在任何 POJO 中序列化兩個自定義字段。然而,手動編寫 @context 可能會耗費大量時間和容易出錯。
因此,為了避免這種容易出錯的方法,讓我們更詳細地瞭解一下我們可以用來生成 @context 的兩個庫。不幸的是,它們都沒有能夠生成所有 JSON-LD 的功能,稍後我們會更詳細地瞭解它們的不足之處。
4. 使用 Jackson-Jsonld 進行序列化
Jackson-Jsonld 是一個 Jackson 模塊,它允許以方便的方式對 POJO 進行註解,從而生成 JSON-LD 文檔。
4.1. Maven 依賴
首先,將 jackson-jsonld 添加為依賴項到 pom.xml 中:
<dependency>
<groupId>com.io-informatics.oss</groupId>
<artifactId>jackson-jsonld</artifactId>
<version>0.1.1</version>
</dependency>4.2. 示例
然後,讓我們創建一個示例 POJO 併為其進行註釋,以生成 @context:
@JsonldResource
@JsonldNamespace(name = "s", uri = "http://schema.org/")
@JsonldType("s:Person")
@JsonldLink(rel = "s:knows", name = "knows", href = "http://example.com/person/2345")
public class Person {
@JsonldId
private String id;
@JsonldProperty("s:name")
private String name;
// constructor, getters, setters
}讓我們逐步分析我們所做的工作:
- 使用 @JsonldResource 標記POJO為JSON-LD資源的處理對象
- 在 @JsonldNamespace 中定義了我們希望使用的詞彙的簡寫
- 我們在 @JsonldType 中指定的參數將成為資源的 @type
- 我們使用 @JsonldLink 註解添加了指向資源的鏈接。在處理過程中,name 參數將用作字段名稱,並將作為 @context 中的鍵添加到其中。 href 將是字段值,rel 將是 @context 中的映射值
- 我們標記的字段,使用 @JsonldId 註解,將成為資源的 @id
- 我們在 @JsonldProperty 中指定的參數將成為字段名稱在 @context 中的映射值
接下來,讓我們生成JSON-LD文檔。
首先,我們應該在 ObjectMapper 中註冊 JsonldModule。 該模塊包含一個自定義 Serializer,Jackson 將用於標記的 POJO 帶有 @JsonldResource 註解
然後,我們將繼續使用 ObjectMapper 生成JSON-LD文檔:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JsonldModule());
Person person = new Person("http://example.com/person/1234", "Example Name");
String personJsonLd = objectMapper.writeValueAsString(person);因此,personJsonLd 變量現在應包含:
{
"@type": "s:Person",
"@context": {
"s": "http://schema.org/",
"name": "s:name",
"knows": {
"@id": "s:knows",
"@type": "@id"
}
},
"name": "Example Name",
"@id": "http://example.com/person/1234",
"knows": "http://example.com/person/2345"
}4.3. 考慮因素
在為項目選擇該庫之前,我們應該考慮以下事項:
- 使用
<em @vocab</em>關鍵字不可行,因此我們必須使用<em @JsonldNamespace</em>提供字段名稱的簡寫,或者每次都完整地編寫 Internationalized Resource Identifier (IRI)。 - 我們只能在編譯時定義鏈接,因此為了添加運行時鏈接,我們需要使用反射來修改該註解中的參數。
5. 使用 Hydra-Jsonld 進行序列化
Hydra-Jsonld 是 Hydra-Java 庫中的一個模塊,主要用於為 Spring 應用創建便捷的 JSON-LD 響應。它使用 Hydra 詞彙,使 JSON-LD 文檔更具表達力。
然而,Hydra-Jsonld 模塊包含一個 Jackson 序列化器,以及一些註解,我們可以利用它們在 Spring 框架之外生成 JSON-LD 文檔。
5.1. Maven 依賴
首先,將 hydra-jsonld 的依賴添加到 <pom.xml> 中:
<dependency>
<groupId>de.escalon.hypermedia</groupId>
<artifactId>hydra-jsonld</artifactId>
<version>0.4.2</version>
</dependency>5.2. 示例
其次,讓我們為 POJO 添加註釋以生成 <em @context</em>。
Hydra-Jsonld 自動生成默認的 <em @context</em>,無需添加註釋。 <strong>如果對默認值感到滿意,我們只需要添加<em @id` 即可獲得有效的 JSON-LD 文檔。
默認詞彙將是 schema.org 詞彙,<em @type</em> 將是 Java 的 <em class</em> 名稱,以及 POJO 的公共屬性都將包含在生成的 JSON-LD 文檔中。
在這個示例中,讓我們使用自定義值覆蓋這些默認值:
@Vocab("http://example.com/vocab/")
@Expose("person")
public class Person {
private String id;
private String name;
// constructor
@JsonProperty("@id")
public String getId() {
return id;
}
@Expose("fullName")
public String getName() {
return name;
}
}再次,讓我們更仔細地看看涉及的步驟:
- 與 Jackson-Jsonld 示例相比,我們從我們的 POJO 中省略了 knows 字段,因為這與 Hydra-Jsonld 在 Spring Framework 之外的限制有關
- 我們使用 @Vocab 註解設置了首選詞彙
- 通過在類上使用 @Expose 註解,我們設置了不同的資源 @type
- 我們使用 @Expose 註解在屬性上設置其映射到自定義值的 @context
- 為了從屬性生成 @id,我們使用了 Jackson 中的 @JsonProperty 註解
接下來,讓我們配置一個 Jackson 的 Module 實例,以便將其註冊到 ObjectMapper 中。 我們將 JacksonHydraSerializer 作為 BeanSerializerModifier 添加,以便將其應用於所有正在序列化的 POJO :
SimpleModule getJacksonHydraSerializerModule() {
return new SimpleModule() {
@Override
public void setupModule(SetupContext context) {
super.setupModule(context);
context.addBeanSerializerModifier(new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifySerializer(
SerializationConfig config,
BeanDescription beanDesc,
JsonSerializer<?> serializer) {
if (serializer instanceof BeanSerializerBase) {
return new JacksonHydraSerializer((BeanSerializerBase) serializer);
} else {
return serializer;
}
}
});
}
};
}然後,我們將在 ObjectMapper 中註冊 Module,並使用它。 此外,我們還應將 ObjectMapper 設置為僅包含非 null 值,以生成有效的 JSON-LD 文檔:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(getJacksonHydraSerializerModule());
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
Person person = new Person("http://example.com/person/1234", "Example Name");
String personJsonLd = objectMapper.writeValueAsString(person);現在,personJsonLd 變量應包含:
{
"@context": {
"@vocab": "http://example.com/vocab/",
"name": "fullName"
},
"@type": "person",
"name": "Example Name",
"@id": "http://example.com/person/1234"
}5.3. 考慮因素
雖然在 Spring 框架之外使用 Hydra-Jsonld 在技術上是可行的,但它最初的設計是為了與 Spring-HATEOAS 配合使用。因此,無法像 Jackson-Jsonld 那樣生成帶有註解的鏈接。另一方面,對於一些 Spring 相關的類,它們會自動生成鏈接。
在選擇該庫用於項目之前,我們應該考慮以下事項:
- 使用它與 Spring 框架一起將啓用額外的功能
- 如果未使用 Spring 框架,則無法輕鬆生成鏈接
- 我們無法禁用 @vocab 的使用,只能對其進行覆蓋
6. 使用 Jsonld-Java 和 Jackson 進行反序列化
Jsonld-Java 是 JSON-LD 1.0 規範和 API 的 Java 實現,但不幸的是,它不是最新的版本。
對於 1.1 規範版本的實現,請查看 Titanium JSON-LD 庫。
要反序列化一個 JSON-LD 文檔,我們可以使用 JSON-LD API 中的一項特性——“壓縮”(compaction)將其轉換為我們可以使用 ObjectMapper 映射到 POJO 的格式。
6.1. Maven 依賴
首先,添加 jsonld-java 的依賴:
<dependency>
<groupId>com.github.jsonld-java</groupId>
<artifactId>jsonld-java</artifactId>
<version>0.13.0</version>
</dependency>6.2. 示例
讓我們使用以下 JSON-LD 文檔作為輸入:
{
"@context": {
"@vocab": "http://schema.org/",
"knows": {
"@type": "@id"
}
},
"@type": "Person",
"@id": "http://example.com/person/1234",
"name": "Example Name",
"knows": "http://example.com/person/2345"
}為了簡化起見,我們假設文檔的內容存儲在一個名為 inputJsonLd 的 String 變量中。
首先,我們對其進行壓縮並將其轉換回 String:
Object jsonObject = JsonUtils.fromString(inputJsonLd);
Object compact = JsonLdProcessor.compact(jsonObject, new HashMap<>(), new JsonLdOptions());
String compactContent = JsonUtils.toString(compact);<ul>
<li>我們可以使用來自 <em >JsonUtils</em > 的方法解析和寫入 JSON-LD 對象,JsonUtils 是 Jsonld-Java 庫的一部分</li>
<li>在使用 <em >compact</em> 方法時,我們可以將一個空 <em >Map</em> 作為第二個參數。 這樣,壓縮算法將產生一個簡單的 JSON 對象,其中鍵已解析為 IRI 形式</li>
</ul>
<p><strong > <em >compactContent</em > 變量應包含:</strong></p>
{
"@id": "http://example.com/person/1234",
"@type": "http://schema.org/Person",
"http://schema.org/knows": {
"@id": "http://example.com/person/2345"
},
"http://schema.org/name": "Example Name"
}其次,讓我們使用 Jackson 註解定製 POJO,以適應這種文檔結構:
@JsonIgnoreProperties(ignoreUnknown = true)
public class Person {
@JsonProperty("@id")
private String id;
@JsonProperty("http://schema.org/name")
private String name;
@JsonProperty("http://schema.org/knows")
private Link knows;
// constructors, getters, setters
public static class Link {
@JsonProperty("@id")
private String id;
// constructors, getters, setters
}
}最後,讓我們將 JSON-LD 映射到 POJO:
ObjectMapper objectMapper = new ObjectMapper();
Person person = objectMapper.readValue(compactContent, Person.class);7. 結論
在本文中,我們探討了兩種基於 Jackson 的庫,用於將 POJO 序列化為 JSON-LD 文檔,以及一種將 JSON-LD 序列化為 POJO 的方法。
正如我們所強調的,這兩個序列化庫都存在一些不足之處,我們在使用它們之前應該加以考慮。 如果我們需要使用 JSON-LD 的更多功能,這些庫無法提供,我們可以通過使用 RDF 庫,並指定 JSON-LD 輸出格式來創建我們的文檔。