Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
451 views
in Technique[技术] by (71.8m points)

java - How to define a generic list deserializer through annotations with Jackson?

Let's say I have an object which has list properties:

public class Citizen {
    name
    List<Tickets> tickets
    List<Fines> fines
}

I'd like to define a generic custom deserializer for lists through annotations:

public class Citizen {
    ...
    @JsonDeserializer(MyListDeserializer<Tickets>) // <-- generic deserializer
    public void setTickets(List<Tickets> tickets) {
        this.tickets = tickets;
    }

    @JsonDeserializer(MyListDeserializer<Fines>) // <-- how can I do that? 
    public void setFines(List<Fines> fines) {
        this.fines = fines;
    }
}

I'm looking for a way to create a "generic" deserializer — one that would be able to deserialize both types of lists, similar to ContextualDeserializer for mapping JSON to different types of maps with Jackson.

The final purpose is to implement custom deserializing logic in MyListDeserializer to deserialize empty strings "" as empty lists, but I'd like to know about a general approach, not just for empty strings.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

You can specify the deserializer class with which to deserialize the elements of the list with the contentUsing attribute of the @JsonDeserializer annotation.

public class Citizen {
    ...
    @JsonDeserializer(contentUsing=MyListDeserializer.class) 
    public void setTickets(List<Tickets> tickets) {
        this.tickets = tickets;
    }
}

Make your deserializer extend JsonDeserializer<BaseClass> and have a attribute in the BaseClass that stores the actual type of the concrete class.

abstract class BaseTickets {
    String ticketType;
    public String getTicketType()
}

public class MyListDeserializer extends JsonDeserializer<BaseTickets> {

    @Override
    public BaseTickets deserialize(JsonParser jsonParser, DeserializationContext arg1) throws IOException, JsonProcessingException {
        ObjectCodec oc = jsonParser.getCodec();
        JsonNode node = oc.readTree(jsonParser);
        Iterator<JsonNode> elements = node.getElements();
        for (; elements.hasNext();) {
            String type = (String) elements.next().get("ticketType");

            if (type.equals()){
               //create concrete type here
            }
        }
     }

Or if you want a single deserializer for all List types with no common base class, then use the using attribute, have MyListDeserializer extend JsonDeserialize<Object>. For determining the type of list element you would have to write a custom serializer that adds the type information to the list which can then be used in the generic deserializer.

public class Citizen {
    ...
    @JsonDeserializer(using=MyListDeserializer.class)
    @JsonSerializer(using=MyListSerializer.class) 
    public void setTickets(List<Tickets> tickets) {
        this.tickets = tickets;
    }
}

public class MyListSerializer extends JsonSerializer<Object> {

    @Override
    public void serialize(Object list, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
        @SuppressWarnings("rawtypes")
        jgen.writeStartObject();
        String type = getListType(list);
        jgen.writeStringField("listType", type);
        jgen.writeObjectField("list", list)
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...