
在实际开发中,我们经常会遇到需要从web服务接收json数组的情况,该数组可能包含结构相似但又不完全相同的对象。例如,一个数组中既有基类对象(如car),又有其子类对象(如truck),子类会包含一些基类没有的额外属性。当尝试将这样的json数组直接反序列化为list<baseclass>时,jackson默认会将所有对象都映射到基类,导致子类特有的属性无法被识别,从而抛出com.fasterxml.jackson.databind.exc.unrecognizedpropertyexception异常。
例如,考虑以下Java类结构:
// 基类
public class Car {
private String make;
private String model;
private short year;
private String bodyStyle;
private String engineType;
private int horsepower;
// 省略setter和getter
// 为了演示,添加toString方法
@Override
public String toString() {
return "Car{" +
"make='" + make + '\'' +
", model='" + model + '\'' +
", year=" + year +
", bodyStyle='" + bodyStyle + '\'' +
", engineType='" + engineType + '\'' +
", horsepower=" + horsepower +
'}';
}
}
// 子类,继承自Car并添加额外属性
public class Truck extends Car {
private double maxLoad;
private double clearance;
// 省略setter和getter
// 为了演示,添加toString方法
@Override
public String toString() {
return "Truck{" +
"make='" + getMake() + '\'' + // 继承自Car
", model='" + getModel() + '\'' + // 继承自Car
", year=" + getYear() + // 继承自Car
", bodyStyle='" + getBodyStyle() + '\'' + // 继承自Car
", engineType='" + getEngineType() + '\'' + // 继承自Car
", horsepower=" + getHorsepower() + // 继承自Car
", maxLoad=" + maxLoad +
", clearance=" + clearance +
'}';
}
}以及一个包含Car和Truck类型数据的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
}
]直接使用ObjectMapper尝试反序列化为List<Car>会导致异常,因为Car类不认识maxLoad和clearance属性。
为了解决上述问题,Jackson提供了强大的多态反序列化机制。通过在基类上使用@JsonTypeInfo和@JsonSubTypes注解,我们可以指导Jackson在反序列化时根据JSON数据的内容智能地选择正确的子类进行实例化。
@JsonTypeInfo注解用于配置如何处理类型信息。在本场景中,我们采用JsonTypeInfo.Id.DEDUCTION策略,这意味着Jackson会尝试通过检查JSON对象的属性来推断其具体类型。
@JsonSubTypes注解用于注册基类的所有已知子类型。Jackson会利用这些注册信息在推断过程中进行匹配。
为了实现将混合JSON数组反序列化为List<Car>,同时确保Truck对象也能正确实例化,我们需要修改Car类,添加上述Jackson注解:
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
// 使用@JsonTypeInfo和@JsonSubTypes注解实现多态反序列化
@JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION, defaultImpl = Car.class)
@JsonSubTypes({
@JsonSubTypes.Type(value = Truck.class) // 注册Truck作为Car的子类型
})
public class Car {
private String make;
private String model;
private short year;
private String bodyStyle;
private String engineType;
private int horsepower;
// 省略setter和getter
public String getMake() { return make; }
public void setMake(String make) { this.make = make; }
public String getModel() { return model; }
public void setModel(String model) { this.model = model; }
public short getYear() { return year; }
public void setYear(short year) { this.year = year; }
public String getBodyStyle() { return bodyStyle; }
public void setBodyStyle(String bodyStyle) { this.bodyStyle = bodyStyle; }
public String getEngineType() { return engineType; }
public void setEngineType(String engineType) { this.engineType = engineType; }
public int getHorsepower() { return horsepower; }
public void setHorsepower(int horsepower) { this.horsepower = horsepower; }
@Override
public String toString() {
return "Car{" +
"make='" + make + '\'' +
", model='" + model + '\'' +
", year=" + year +
", bodyStyle='" + bodyStyle + '\'' +
", engineType='" + engineType + '\'' +
", horsepower=" + horsepower +
'}';
}
}
// Truck类保持不变,但为了完整性再次列出
public class Truck extends Car {
private double maxLoad;
private double clearance;
// 省略setter和getter
public double getMaxLoad() { return maxLoad; }
public void setMaxLoad(double maxLoad) { this.maxLoad = maxLoad; }
public double getClearance() { return clearance; }
public void setClearance(double clearance) { this.clearance = clearance; }
@Override
public String toString() {
return "Truck{" +
"make='" + getMake() + '\'' +
", model='" + getModel() + '\'' +
", year=" + getYear() +
", bodyStyle='" + getBodyStyle() + '\'' +
", engineType='" + getEngineType() + '\'' +
", horsepower=" + getHorsepower() +
", maxLoad=" + maxLoad +
", clearance=" + clearance +
'}';
}
}现在,使用原始的测试代码进行反序列化:
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class TestDeserialization {
private static final ObjectMapper mapper = new ObjectMapper();
public static void main(String[] args) {
// 假设cars.json文件在classpath下
InputStream src = TestDeserialization.class.getClassLoader().getResourceAsStream("cars.json");
if (src == null) {
System.err.println("Error: cars.json not found in classpath.");
return;
}
try {
// 反序列化为List<Car>
List<Car> vehicles = mapper.readValue(src, new TypeReference<List<Car>>() {});
System.out.println("Deserialization successful. Objects in the list:");
for (Car vehicle : vehicles) {
System.out.println("Type: " + vehicle.getClass().getSimpleName() + ", Details: " + vehicle.toString());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}示例JSON文件 (cars.json):
[
{
"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
}
]运行上述TestDeserialization程序,将不再出现UnrecognizedPropertyException,并且输出会显示列表中包含了Car和Truck两种类型的实例。
预期输出示例:
Deserialization successful. Objects in the list:
Type: Car, Details: Car{make='Ford', model='Focus', year=2018, bodyStyle='hatchback', engineType='T4', horsepower=225}
Type: Car, Details: Car{make='Toyota', model='Prius', year=2021, bodyStyle='hatchback', engineType='T4', horsepower=121}
Type: Car, Details: Car{make='Toyota', model='RAV4', year=2020, bodyStyle='SUV', engineType='V6', horsepower=230}
Type: Truck, Details: Truck{make='Toyota', model='Tacoma', year=2021, bodyStyle='pickup', engineType='V6', horsepower=278, maxLoad=1050.0, clearance=9.4}
Type: Truck, Details: Truck{make='Ford', model='T150', year=2017, bodyStyle='pickup', engineType='null', horsepower=450, maxLoad=2320.0, clearance=8.4}(注意:Ford T150的engineType在JSON中缺失,Jackson会将其设为默认值null,这符合预期行为。)
通过在基类上巧妙地使用@JsonTypeInfo和@JsonSubTypes注解,Jackson能够有效地处理包含混合类型对象的JSON数组。JsonTypeInfo.Id.DEDUCTION结合defaultImpl提供了一种无需修改JSON结构即可实现多态反序列化的强大机制,极大地提高了数据处理的灵活性和健壮性。理解并掌握这些注解对于处理复杂的数据模型至关重要。
以上就是使用Jackson处理包含多种类型对象的JSON数组:多态反序列化指南的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号