1. 概述
JSON-LD 是一种基于 JSON 的 RDF 格式,用于表达 Linked Data。它的核心优势在于:可以在标准 JSON 对象中嵌入超媒体能力,也就是说,能让数据自带机器可读的链接信息,实现资源之间的语义关联。
本文将重点探讨如何使用 Jackson 生态下的工具,直接将 POJO 序列化/反序列化为 JSON-LD 格式。我们也会快速回顾 JSON-LD 的核心概念,以便理解后续示例。
⚠️ 注意:本文不教你 JSON 基础,假定你已熟悉 Jackson 的 ObjectMapper
、注解机制和自定义序列化器。
2. JSON-LD 核心概念
第一次看 JSON-LD 文档时,你会注意到某些字段名以 @
开头,这些是 JSON-LD 的关键字(keywords),它们定义了文档的语义结构。
要理解本文内容,你只需掌握以下四个关键字:
@context
:上下文定义,一个键值映射,告诉解析器如何解释文档中的属性@vocab
:@context
中的一个可选键,用于设置默认词汇表,简化属性命名@id
:资源的唯一标识符(URI),也可用于表示某个字段是链接@type
:资源的类型标识,可出现在资源级别或@context
中
✅ 简单理解:@context
是“字典”,@id
是“地址”,@type
是“身份”,@vocab
是“缩写规则”。
3. Java 中的序列化挑战
熟悉 Jackson 的你可能会想:用 @JsonProperty("@id")
不就能搞定 @id
字段?确实可以。
但问题在于 @context
—— 手写 @context
不仅繁琐,还极易出错,尤其是当字段多、命名空间复杂时。
因此,我们更倾向于使用专门的库来自动化生成 @context
。本文介绍两个基于 Jackson 的方案:jackson-jsonld
和 hydra-jsonld
。⚠️ 但要提前说明:它们都不支持 JSON-LD 全部特性,选择前务必评估其局限。
4. 使用 Jackson-Jsonld 序列化
jackson-jsonld 是一个 Jackson 模块,通过注解方式让 POJO 轻松生成 JSON-LD。
4.1 Maven 依赖
<dependency>
<groupId>com.io-informatics.oss</groupId>
<artifactId>jackson-jsonld</artifactId>
<version>0.1.1</version>
</dependency>
4.2 示例代码
@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
:标记该类为 JSON-LD 资源 - ✅
@JsonldNamespace
:定义命名空间别名,s
代表http://schema.org/
- ✅
@JsonldType
:指定资源类型为s:Person
- ✅
@JsonldLink
:添加静态链接字段knows
,rel
决定其语义,href
是目标地址 - ✅
@JsonldId
:该字段映射为@id
- ✅
@JsonldProperty
:指定字段在@context
中的映射关系
序列化代码:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JsonldModule());
Person person = new Person("http://example.com/person/1234", "Example Name");
String personJsonLd = objectMapper.writeValueAsString(person);
✅ 输出结果:
{
"@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 注意事项
使用前请评估以下限制:
- ❌ 不支持
@vocab
,必须显式定义命名空间或写全 IRI - ❌ 链接(
@JsonldLink
)只能在编译期定义,无法动态添加 - ⚠️ 项目维护不活跃,生产环境需谨慎
5. 使用 Hydra-Jsonld 序列化
Hydra-Jsonld 是 Hydra-Java 的模块,本为 Spring 应用设计,但其核心序列化器也可独立使用。
它基于 Hydra Vocabulary,生成更具语义的 JSON-LD。
5.1 Maven 依赖
<dependency>
<groupId>de.escalon.hypermedia</groupId>
<artifactId>hydra-jsonld</artifactId>
<version>0.4.2</version>
</dependency>
5.2 示例代码
@Vocab("http://example.com/vocab/")
@Expose("person")
public class Person {
private String id;
private String name;
public Person(String id, String name) {
this.id = id;
this.name = name;
}
@JsonProperty("@id")
public String getId() {
return id;
}
@Expose("fullName")
public String getName() {
return name;
}
}
关键点说明:
- ✅
@Vocab
:设置默认词汇表 - ✅
@Expose
:类上使用定义@type
,方法上使用定义属性映射 - ✅
@JsonProperty("@id")
:从 getter 返回@id
值
注册自定义模块:
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 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);
✅ 输出结果:
{
"@context": {
"@vocab": "http://example.com/vocab/",
"name": "fullName"
},
"@type": "person",
"name": "Example Name",
"@id": "http://example.com/person/1234"
}
5.3 注意事项
- ✅ 与 Spring 集成时功能更完整(如自动 HATEOAS 链接)
- ❌ 独立使用时无法通过注解添加自定义链接
- ❌ 无法禁用
@vocab
,只能覆盖 - ⚠️ 设计初衷是配合 Spring-HATEOAS,非通用 JSON-LD 解决方案
6. 使用 Jsonld-Java 反序列化
Jsonld-Java 是 JSON-LD 1.0 的 Java 实现(⚠️ 非最新 1.1)。若需 1.1 支持,可考虑 Titanium JSON-LD。
反序列化的核心思路是:先用 JSON-LD API 将原始文档“压缩”(compact),再用 Jackson 映射到 POJO。
6.1 Maven 依赖
<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"
}
压缩处理:
Object jsonObject = JsonUtils.fromString(inputJsonLd);
Object compact = JsonLdProcessor.compact(jsonObject, new HashMap<>(), new JsonLdOptions());
String compactContent = JsonUtils.toString(compact);
✅ 压缩后输出:
{
"@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"
}
适配的 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
}
}
最终反序列化:
ObjectMapper objectMapper = new ObjectMapper();
Person person = objectMapper.readValue(compactContent, Person.class);
7. 总结
本文介绍了两种 Jackson 集成方案(jackson-jsonld
和 hydra-jsonld
)用于 JSON-LD 序列化,以及一种结合 jsonld-java
的反序列化方法。
✅ 优点:简单粗暴,适合固定结构的 JSON-LD 输出
❌ 缺点:功能有限,灵活性差,维护状态参差
📌 踩坑建议:
- 若只是输出简单 JSON-LD,
jackson-jsonld
更直接 - 若已在用 Spring-HATEOAS,
hydra-jsonld
集成更顺滑 - 若需完整 JSON-LD 1.1 支持,建议直接上 RDF 库(如 RDF4J + JSON-LD 输出)
所有示例代码已上传至 GitHub:https://github.com/eugenp/tutorials/tree/master/json-modules/json-2