反序列化杰克逊中同名但不同类型的属性?

Posted

技术标签:

【中文标题】反序列化杰克逊中同名但不同类型的属性?【英文标题】:Deserializing attributes of same name but different types in Jackson? 【发布时间】:2016-10-15 05:49:30 【问题描述】:

我有一个 REST API,它返回一个 JSON 响应:


    "channel" : "JHBHS"

有时它会返回:


    "channel": 
                    "id": 12321,
                    "name": "Some channel"
               

我有一个类似的 POJO:

public class Event 
    private String channel;
    @JsonProperty("channel")
    private Channel channelObj;


public class Channel 
    private int id;
    private String name;

那么,有没有一种方法(除了编写自己的自定义反序列化器)在Jackson2 中可以帮助我将JSON 中的channel 映射到String 类型,当它是String 时和Channel 类型当它是一个JSON 对象?

或者换句话说,Jackson 有没有办法通过变量的type 而不仅仅是name 进行映射?

【问题讨论】:

我发现了另一个类似的问题***.com/questions/21790727/… 【参考方案1】:

我可以建议你像这样使用JsonNode:

class Event 

    @JsonProperty("channel")
    private JsonNode channelInternal;

    private Channel channel;

    private String channelStr;

    /**
     * Custom getter with channel parsing
     * @return channel
     */
    public Channel getChannel() 
        if (channel == null && channelInternal != null) 
            if (channelInternal.isObject()) 
                int id = channelInternal.get("id").intValue();
                String name = channelInternal.get("name").asText();
                channel = new Channel(id, name);
            
        
        return channel;
    

    /**
     * Custom getter with channel string parsing
     * @return channel string
     */
    public String getChannelStr() 
        if (channelStr == null && channelInternal != null) 
            if (channelInternal.isTextual()) 
                channelStr = channelInternal.asText();
            
        
        return channelStr;
    

或像这样

class Event 

    private Channel channel;

    private String channelStr;

    @JsonSetter("channel")
    public void setChannelInternal(JsonNode channelInternal) 
        if (channelInternal != null) 
            if (channelInternal.isTextual()) 
                channelStr = channelInternal.asText();
             else if (channelInternal.isObject()) 
                int id = channelInternal.get("id").intValue();
                String name = channelInternal.get("name").asText();
                channel = new Channel(id, name);
            
        
    

    public Channel getChannel() 
        return channel;
    

    public String getChannelStr() 
        return channelStr;
    

但我认为使用自定义反序列化器更常见。

这是测试代码

public static void main(String[] args) throws IOException 
    ObjectMapper objectMapper = new ObjectMapper();
    String source1 = "\n" +
            "    \"channel\" : \"JHBHS\"\n" +
            "";
    String source2 = "\n" +
            "    \"channel\": \n" +
            "                    \"id\": 12321,\n" +
            "                    \"name\": \"Some channel\"\n" +
            "               \n" +
            "";

    //Test object parsing
    Event result = objectMapper.readValue(source2, Event.class);
    System.out.println(String.format("%s : %s", result.getChannel().getId(), result.getChannel().getName()));

    //Test string parsing
    result = objectMapper.readValue(source1, Event.class);
    System.out.println(result.getChannelStr());

还有输出:

12321 : Some channel
JHBHS

【讨论】:

非常详细的答案。但是自定义 setter 方法不会像 ***.com/a/27068928/1385441 那样在这里完成工作吗? @Ramswaroop,刚刚用***.com/questions/21790727/…中描述的第二个变体更新了答案

以上是关于反序列化杰克逊中同名但不同类型的属性?的主要内容,如果未能解决你的问题,请参考以下文章

有啥方法可以防止杰克逊中的字段反序列化?

反序列化的改造/杰克逊错误

杰克逊从JSON中的变量类型反序列化

如何反序列化具有相同名称但不同类型的 API 响应

使用杰克逊的 JSON 反序列化:没有找到适合类型的构造函数 - 可以提供默认构造函数或注释构造函数 [重复]

JSON杰克逊序列化反序列化列表列表