java - 杰克逊链自定义反序列化器
问题描述
如果你有一堂课User
:
class User {
private String firstName;
private String lastName;
private Address address; //object
}
和一个类Address
分别:
class Address {
private String streetName;
private String postCode;
private AddressType addressType; //enum
}
以及两者的自定义杰克逊反序列化器,有没有一种很好的方法来链接它们?例如
class UserDeserialiser extends JsonDeserializer<User> {
public User deserialize(JsonParser jp, DeserializationContext ctxt) {
ObjectNode node = jp.getCodec().readTree(jp);
User user = fetchUser();
user.setFirstName(node.get("firstName").asText());
user.setFirstName(node.get("lastName").asText());
user.setAddress(???); // delegate to AddressDeserialiser here
}
}
同样在地址反序列化器中,是否可以委托默认的枚举来处理枚举(因为它可能具有自定义映射)?
class AddressDeserialiser extends JsonDeserializer<Address> {
public User deserialize(JsonParser jp, DeserializationContext ctxt) {
ObjectNode node = jp.getCodec().readTree(jp);
Address user = fetchAddress();
user.setStreetName(node.get("streetName").asText());
user.setAddressType(???); // delegate to jackson default object mapper?
}
}
解决方案
不久前我有同样的要求,并想出了一个解决方案。我不认为这是一个“好”的解决方案,因为它有点笨拙,但也许这会鼓励某人写出更好的答案。那很好啊。
正如评论中提到的,这通常可以通过使用注释来解决。具体来说,@JacksonDeserialize
注释。
但是,如果(无论出于何种原因 - 并且有很多可能的原因)不需要使用注释,则有必要为 JSON 树的相关子树显式创建解析器。这将获取默认反序列化器或已在对象映射器模块中为相应类型注册的自定义反序列化器。
引用您的示例的相关部分/模式,即包含???
问号的点,是这样的:
JsonNode addressNode = node.get("address");
if (addressNode != null)
{
JsonParser parser = addressNode.traverse();
parser.setCodec(jp.getCodec());
Address address = parser.readValueAs(Address.class);
user.setAddress(address);
}
这是一个展示这种方法的MCVE :
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ObjectNode;
class UserDeserialiser extends JsonDeserializer<User>
{
@Override
public User deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
System.out.println("Deserializing User...");
ObjectNode node = jp.getCodec().readTree(jp);
User user = new User();
user.setFirstName(node.get("firstName").asText());
user.setLastName(node.get("lastName").asText());
JsonNode addressNode = node.get("address");
if (addressNode != null)
{
JsonParser parser = addressNode.traverse();
parser.setCodec(jp.getCodec());
Address address = parser.readValueAs(Address.class);
user.setAddress(address);
}
return user;
}
}
class AddressDeserialiser extends JsonDeserializer<Address>
{
@Override
public Address deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
System.out.println("Deserializing Address...");
ObjectNode node = jp.getCodec().readTree(jp);
Address address = new Address();
address.setStreetName(node.get("streetName").asText());
address.setPostCode(node.get("postCode").asText());
JsonNode addressTypeNode = node.get("addressType");
if (addressTypeNode != null)
{
JsonParser parser = addressTypeNode.traverse();
parser.setCodec(jp.getCodec());
Address.AddressType addressType =
parser.readValueAs(Address.AddressType.class);
address.setAddressType(addressType);
}
return address;
}
}
public class NestedDeserializers
{
public static void main(String[] args) throws IOException
{
User user = new User();
user.setFirstName("A");
user.setLastName("B");
Address address = new Address();
address.setStreetName("C");
address.setPostCode("D");
address.setAddressType(Address.AddressType.X);
user.setAddress(address);
ObjectMapper mapper = createObjectMapper();
String jsonString = mapper.writeValueAsString(user);
System.out.println("JSON string representation:\n" + jsonString);
User readUser = mapper.readValue(jsonString, User.class);
System.out.println("User : " + user);
System.out.println("Read user: " + readUser);
}
private static ObjectMapper createObjectMapper()
{
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
SimpleModule module = new SimpleModule();
module.addDeserializer(User.class, new UserDeserialiser());
module.addDeserializer(Address.class, new AddressDeserialiser());
mapper.registerModule(module);
return mapper;
}
}
//=============================================================================
// Dummy User/Address classes below
class User
{
private String firstName;
private String lastName;
private Address address;
public String getFirstName()
{
return firstName;
}
public void setFirstName(String firstName)
{
this.firstName = firstName;
}
public String getLastName()
{
return lastName;
}
public void setLastName(String lastName)
{
this.lastName = lastName;
}
public Address getAddress()
{
return address;
}
public void setAddress(Address address)
{
this.address = address;
}
@Override
public String toString()
{
return "User [firstName=" + getFirstName() + ", lastName="
+ getLastName() + ", address=" + getAddress() + "]";
}
}
class Address
{
enum AddressType
{
X, Y;
}
private String streetName;
private String postCode;
private AddressType addressType;
public String getStreetName()
{
return streetName;
}
public void setStreetName(String streetName)
{
this.streetName = streetName;
}
public String getPostCode()
{
return postCode;
}
public void setPostCode(String postCode)
{
this.postCode = postCode;
}
public AddressType getAddressType()
{
return addressType;
}
public void setAddressType(AddressType addressType)
{
this.addressType = addressType;
}
@Override
public String toString()
{
return "Address [streetName=" + getStreetName() + ", postCode="
+ getPostCode() + ", addressType=" + getAddressType() + "]";
}
}
推荐阅读
- c# - 实体框架核心(Postgres)多重包含创建幽灵属性
- arrays - 如何在 Excel VBA 函数中返回记录数组?
- python-3.x - Pandas 数据框中的奇怪字符 - 如何标准化为 UTF-8?
- javascript - DOM 事件监听器通过点击事件更改图像源
- r - html_table 丢失的信息
- javascript - GET http://127.0.0.1:3000/build/three.module.js net::ERR_ABORTED 404(未找到)尝试将轨道控制与three.js一起使用时出错
- r - R中回归模型中的正确异方差和自相关
- aws-lambda - DynamoDB 查询返回不完整的数据
- mysql - MemSQL 以 CSV 格式从数组对象中获取键
- sql-server - 如果在同一个表中找到,则连接名字中间名和姓氏