1. 概述
在本教程中,我们将介绍如何使用 Jackson 和 Gson 将不同格式的 JSON 数据映射到同一个 Java 字段上。这在对接多个第三方 API 时特别有用,尤其是当它们返回结构相似但字段命名不一致的数据时。
2. Maven 依赖
为了使用 Jackson 和 Gson 库,我们需要在 pom.xml
中添加如下依赖:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
<scope>test</scope>
</dependency>
⚠️ 注意:这里我们将依赖范围设置为
test
,因为示例代码主要用于单元测试场景。
3. 示例 JSON 数据
假设我们要从不同的天气服务接口获取数据,并统一处理。这两个服务返回的 JSON 格式略有不同:
{
"location": "London",
"temp": 15,
"weather": "Cloudy"
}
和:
{
"place": "Lisbon",
"temperature": 35,
"outlook": "Sunny"
}
我们的目标是将这两种格式都反序列化为同一个 Java 类 —— Weather
:
public class Weather {
private String location;
private int temp;
private String outlook;
}
接下来我们看看如何分别使用 Jackson 和 Gson 实现这一需求。
4. 使用 Jackson 实现映射
✅ 核心思路:通过 @JsonProperty
和 @JsonAlias
注解实现多字段映射。
@JsonProperty
:用于指定主字段名(用于序列化和反序列化)@JsonAlias
:用于指定别名字段(仅用于反序列化)
示例代码如下:
@JsonProperty("location")
@JsonAlias("place")
private String location;
@JsonProperty("temp")
@JsonAlias("temperature")
private int temp;
@JsonProperty("outlook")
@JsonAlias("weather")
private String outlook;
然后我们使用 Jackson 的 ObjectMapper
来测试反序列化逻辑:
@Test
public void givenTwoJsonFormats_whenDeserialized_thenWeatherObjectsCreated() throws Exception {
ObjectMapper mapper = new ObjectMapper();
Weather weather = mapper.readValue("{\n"
+ " \"location\": \"London\",\n"
+ " \"temp\": 15,\n"
+ " \"weather\": \"Cloudy\"\n"
+ "}", Weather.class);
assertEquals("London", weather.getLocation());
assertEquals("Cloudy", weather.getOutlook());
assertEquals(15, weather.getTemp());
weather = mapper.readValue("{\n"
+ " \"place\": \"Lisbon\",\n"
+ " \"temperature\": 35,\n"
+ " \"outlook\": \"Sunny\"\n"
+ "}", Weather.class);
assertEquals("Lisbon", weather.getLocation());
assertEquals("Sunny", weather.getOutlook());
assertEquals(35, weather.getTemp());
}
✅ 踩坑提示:如果你只用了
@JsonAlias
而没有@JsonProperty
,序列化时会找不到对应字段哦!
5. 使用 Gson 实现映射
✅ 核心思路:使用 @SerializedName
注解中的 value
和 alternate
参数。
value
:默认字段名alternate
:可选字段别名(支持多个)
示例代码如下:
@SerializedName(value="location", alternate="place")
private String location;
@SerializedName(value="temp", alternate="temperature")
private int temp;
@SerializedName(value="outlook", alternate="weather")
private String outlook;
测试代码如下:
@Test
public void givenTwoJsonFormats_whenDeserialized_thenWeatherObjectsCreated() throws Exception {
Gson gson = new GsonBuilder().create();
Weather weather = gson.fromJson("{\n"
+ " \"location\": \"London\",\n"
+ " \"temp\": 15,\n"
+ " \"weather\": \"Cloudy\"\n"
+ "}", Weather.class);
assertEquals("London", weather.getLocation());
assertEquals("Cloudy", weather.getOutlook());
assertEquals(15, weather.getTemp());
weather = gson.fromJson("{\n"
+ " \"place\": \"Lisbon\",\n"
+ " \"temperature\": 35,\n"
+ " \"outlook\": \"Sunny\"\n"
+ "}", Weather.class);
assertEquals("Lisbon", weather.getLocation());
assertEquals("Sunny", weather.getOutlook());
assertEquals(35, weather.getTemp());
}
✅ 小贴士:Gson 的
alternate
支持传入数组,比如alternate={"place", "address"}
,适合字段别名较多的场景。
6. 总结
在这篇文章中我们展示了两种主流 JSON 解析库的多字段映射方案:
库 | 主字段注解 | 别名字段注解 |
---|---|---|
Jackson | @JsonProperty |
@JsonAlias |
Gson | @SerializedName |
alternate 属性 |
无论你是对接多个第三方接口,还是处理历史遗留的多种数据格式,这两种方式都能让你优雅地统一数据模型,避免写一堆适配器类。
📌 完整代码可参考 GitHub 仓库: