杰克逊连锁定制解串器

时间:2018-08-17 10:26:39

标签: java json jackson deserialization

如果您有课程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?
    }
}

1 个答案:

答案 0 :(得分:2)

前段时间我有相同的要求,并找到了解决方案。我认为这不是一个“不错”的解决方案,因为它有点笨拙,但这也许会鼓励某人写出更好的答案。那太好了。


如评论中所述,通常可以通过使用注释来解决。具体来说,@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() + "]";
    }

}