首页 > 解决方案 > 杰克逊根据通用类型设置元素名称

问题描述

是否可以使用杰克逊根据泛型的类设置元素的名称?

鉴于以下情况:

public class Room<T> {
    private int roomId;
    private String roomName;
    private T roomDetails;
//constructor getters, setters
}

public class Livingroom {
    private boolean hasCouch;
    private int numOfSeats;
//constructor getters, setters
}

public class Bathroom{
    private boolean hasShower;
    private boolean hasSink;
//constructor getters, setters
}

如果我使用杰克逊序列化房间对象(客厅设置为通用),它看起来像这样:

public void jackson() throws JsonProcessingException {
    Livingroom livingroom = new Livingroom();
    livingroom.setHasCouch(true);
    livingroom.setNumOfSeats(5);

    Room<Livingroom> room = new Room<>();
    room.setRoomDetails(livingroom);
    room.setRoomId(10);
    room.setRoomName("MyRoom");
    XmlMapper xmlMapper = new XmlMapper();
    String xml = xmlMapper.writeValueAsString(room);
    System.out.println(xml);
}

结果是:

<Room>
    <roomId>10</roomId>
    <roomName>MyRoom</roomName>
    <roomDetails>
        <hasCouch>true</hasCouch>
        <numOfSeats>5</numOfSeats>
    </roomDetails>
</Room>

然而,我希望结果是:

<Room>
    <roomId>10</roomId>
    <roomName>MyRoom</roomName>
    <Livingroom>
        <hasCouch>true</hasCouch>
        <numOfSeats>5</numOfSeats>
    </Livingroom>
</Room>

浴室或所有其他可能的房间反之亦然。是否可以让杰克逊在运行时从泛型的实际类派生元素名称?

标签: javaxmlgenericsjackson

解决方案


是的,您可以使用自定义序列化程序根据泛型类型设置元素名称。

请记住,jackson 使用这些get方法来命名序列化 json/xml 的属性,这就是您roomDetails在序列化 xml 上获取元素的原因。

注释喜欢@JsonGetter@JsonProperty期望一个常量值,因此在这种情况下不能使用它们,因为我们只能在运行时确定类型。

解决方案:

Room类注释@JsonSerialize

@JsonSerialize(using = RoomSerializer.class)
public class Room<T> {
    private int roomId;
    private String roomName;
    private T roomDetails;

    public int getRoomId() {
        return roomId;
    }

    public void setRoomId(int roomId) {
        this.roomId = roomId;
    }

    public String getRoomName() {
        return roomName;
    }

    public void setRoomName(String roomName) {
        this.roomName = roomName;
    }

    public T getRoomDetails() {
        return roomDetails;
    }

    public void setRoomDetails(T roomDetails) {
        this.roomDetails = roomDetails;
    }
}

类喜欢Bathroom并且Livingroom保持不变。

RoomSerializer执行:

public class RoomSerializer extends StdSerializer<Room> {

    public RoomSerializer() {
        this(null);
    }

    public RoomSerializer(Class<Room> t) {
        super(t);
    }

    @Override
    public void serialize(Room value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
        jgen.writeStartObject();
        jgen.writeNumberField("roomId", value.getRoomId());
        jgen.writeStringField("roomName", value.getRoomName());

        // Here we determine the field name depending on the type
        String roomDetailsFieldName = value.getRoomDetails().getClass().getSimpleName();
        jgen.writeObjectField(roomDetailsFieldName, value.getRoomDetails());

        jgen.writeEndObject();
    }
}

通过您的示例,您将得到您想要的:

<Room>
    <roomId>10</roomId>
    <roomName>MyRoom</roomName>
    <Livingroom>
        <hasCouch>true</hasCouch>
        <numOfSeats>5</numOfSeats>
    </Livingroom>
</Room>

推荐阅读