
在实际开发中,我们经常会遇到需要从web服务接收json数据,其中包含一个对象数组,而这些对象可能属于不同的类,但它们都继承自一个共同的基类。例如,我们有一个car基类和一个truck子类,truck类除了继承car的属性外,还包含一些特有的属性,如maxload和clearance。我们的目标是将一个包含car和truck实例的json数组反序列化成一个list<car>。
示例JSON数据:
[
{
"make": "Ford",
"model": "Focus",
"year": 2018,
"engineType": "T4",
"bodyStyle": "hatchback",
"horsepower": 225
},
{
"make": "Toyota",
"model": "Tacoma",
"year": 2021,
"engineType": "V6",
"bodyStyle": "pickup",
"horsepower": 278,
"maxLoad": 1050,
"clearance": 9.4
}
]初始Java类结构:
public class Car {
private String make;
private String model;
private short year;
private String bodyStyle;
private String engineType;
private int horsepower;
// Getters and setters omitted for brevity
}
public class Truck extends Car {
private double maxLoad;
private double clearance;
// Getters and setters omitted for brevity
}当我们尝试使用Jackson的ObjectMapper直接将上述JSON反序列化为List<Car>时,会遇到com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException异常。这是因为Jackson在尝试将包含maxLoad和clearance属性的JSON对象映射到Car类时,发现Car类中没有这些属性,从而抛出异常。虽然将目标类型改为List<Truck>可以避免异常,但这会导致所有对象都被尝试反序列化为Truck,并且无法正确处理纯Car类型的对象。
为了解决这个问题,我们需要利用Jackson的多态反序列化(Polymorphic Deserialization)机制。Jackson允许我们在没有显式类型标识符的情况下,通过检查JSON对象的属性来推断其具体类型。这可以通过在基类上使用@JsonTypeInfo和@JsonSubTypes注解来实现。
核心思想是在基类Car上添加Jackson注解,告知它如何识别和处理其子类型。
在Car类上添加@JsonTypeInfo和@JsonSubTypes注解:
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
@JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION, defaultImpl = Car.class)
@JsonSubTypes({
@JsonSubTypes.Type(value = Truck.class)
// 如果有其他子类,也在此处添加,例如:
// @JsonSubTypes.Type(value = Sedan.class),
// @JsonSubTypes.Type(value = SUV.class)
})
public class Car {
private String make;
private String model;
private short year;
private String bodyStyle;
private String engineType;
private int horsepower;
// 构造函数、Getters和Setters
public Car() {} // 必须有无参构造函数
// ... (省略Getters和Setters)
@Override
public String toString() {
return "Car{" +
"make='" + make + '\'' +
", model='" + model + '\'' +
", year=" + year +
", bodyStyle='" + bodyStyle + '\'' +
", engineType='" + engineType + '\'' +
", horsepower=" + horsepower +
'}';
}
}Truck类保持不变,因为它已经继承了Car:
public class Truck extends Car {
private double maxLoad;
private double clearance;
// 构造函数、Getters和Setters
public Truck() {} // 必须有无参构造函数
// ... (省略Getters和Setters)
@Override
public String toString() {
return "Truck{" +
super.toString() + // 调用父类的toString
", maxLoad=" + maxLoad +
", clearance=" + clearance +
'}';
}
}@JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION, defaultImpl = Car.class):
@JsonSubTypes({@JsonSubTypes.Type(value = Truck.class)}):
以下是一个完整的Java示例,演示如何使用上述注解进行多态反序列化:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.core.type.TypeReference;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
// Car.java (如上所示,包含Jackson注解和Getters/Setters/toString)
// Truck.java (如上所示,包含Getters/Setters/toString)
public class PolymorphicDeserializationDemo {
private static final ObjectMapper mapper = new ObjectMapper();
public static void main(String[] args) {
// 模拟从文件或网络读取JSON
String jsonInput = getJsonString(); // 获取JSON字符串
try {
// 使用TypeReference进行反序列化到List<Car>
List<Car> cars = mapper.readValue(jsonInput, new TypeReference<List<Car>>() {});
System.out.println("成功反序列化以下对象:");
for (Car car : cars) {
System.out.println(" 类型: " + car.getClass().getSimpleName() + ", 数据: " + car);
if (car instanceof Truck) {
Truck truck = (Truck) car;
System.out.println(" (卡车特有属性) Max Load: " + truck.getMaxLoad() + ", Clearance: " + truck.getClearance());
}
}
} catch (IOException e) {
System.err.println("反序列化失败: " + e.getMessage());
e.printStackTrace();
}
}
// 模拟获取JSON字符串的方法
private static String getJsonString() {
return "[ " +
" {" +
" \"make\": \"Ford\"," +
" \"model\": \"Focus\"," +
" \"year\": 2018," +
" \"engineType\": \"T4\"," +
" \"bodyStyle\": \"hatchback\"," +
" \"horsepower\": 225" +
" }," +
" {" +
" \"make\": \"Toyota\"," +
" \"model\": \"Prius\"," +
" \"year\": 2021," +
" \"engineType\": \"T4\"," +
" \"bodyStyle\": \"hatchback\"," +
" \"horsepower\": 121 " +
" }," +
" {" +
" \"make\": \"Toyota\"," +
" \"model\": \"RAV4\"," +
" \"year\": 2020," +
" \"engineType\": \"V6\"," +
" \"bodyStyle\": \"SUV\"," +
" \"horsepower\": 230 " +
" }," +
" {" +
" \"make\": \"Toyota\"," +
" \"model\": \"Tacoma\"," +
" \"year\": 2021," +
" \"engineType\": \"V6\"," +
" \"bodyStyle\": \"pickup\"," +
" \"horsepower\": 278," +
" \"maxLoad\": 1050," +
" \"clearance\": 9.4" +
" }," +
" {" +
" \"make\": \"Ford\"," +
" \"model\": \"T150\"," +
" \"year\": 2017," +
" \"horsepower\": 450," +
" \"bodyStyle\": \"pickup\"," +
" \"maxLoad\": 2320," +
" \"clearance\": 8.4" +
" } " +
" ]";
}
}运行上述代码,你将看到输出中正确识别了Car和Truck实例,并将它们都存储在List<Car>中:
成功反序列化以下对象:
类型: Car, 数据: Car{make='Ford', model='Focus', year=2018, bodyStyle='hatchback', engineType='T4', horsepower=225}
类型: Car, 数据: Car{make='Toyota', model='Prius', year=2021, bodyStyle='hatchback', engineType='T4', horsepower=121}
类型: Car, 数据: Car{make='Toyota', model='RAV4', year=2020, bodyStyle='SUV', engineType='V6', horsepower=230}
类型: Truck, 数据: Truck{Car{make='Toyota', model='Tacoma', year=2021, bodyStyle='pickup', engineType='V6', horsepower=278}, maxLoad=1050.0, clearance=9.4}
(卡车特有属性) Max Load: 1050.0, Clearance: 9.4
类型: Truck, 数据: Truck{Car{make='Ford', model='T150', year=2017, bodyStyle='pickup', engineType='null', horsepower=450}, maxLoad=2320.0, clearance=8.4}
(卡车特有属性) Max Load: 2320.0, Clearance: 8.4通过正确配置@JsonTypeInfo和@JsonSubTypes注解,我们可以优雅地处理包含多类型对象的JSON数组,并将其反序列化为统一的基类列表,极大地增强了Jackson在处理复杂数据结构时的灵活性和健壮性。
以上就是Jackson处理包含多类型对象的JSON数组:实现多态反序列化到基类列表的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号