You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何使用Jackson反序列化字段可为字符串或对象的JSON负载

解决Jackson反序列化同一字段两种格式的问题

这个场景我之前也碰到过,要让Jackson能同时处理字符串形式的ID和展开式的对象,自定义反序列化器是最靠谱的方案。下面一步步来实现:

1. 定义实体类

首先我们需要先定义对应的Java实体,先写Category类:

import lombok.Data;

@Data
public class Category {
    private String id;
    private String name;
}

然后是包含category字段的Movie类,注意要给category字段加上@JsonDeserialize注解,指定我们后续要写的自定义反序列化器:

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import lombok.Data;

@Data
public class Movie {
    private String id;
    @JsonDeserialize(using = CategoryDeserializer.class)
    private Category category;
    private String title;
    private String description;
}

2. 实现自定义反序列化器

接下来写核心的CategoryDeserializer,它继承自Jackson的JsonDeserializer<Category>,主要逻辑是判断当前JSON节点是字符串还是对象,分别处理:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

import java.io.IOException;

public class CategoryDeserializer extends JsonDeserializer<Category> {
    @Override
    public Category deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        JsonNode node = p.getCodec().readTree(p);
        Category category = new Category();
        
        if (node.isTextual()) {
            // 处理字符串形式:"/category/12" -> 提取id为"12"
            String categoryStr = node.asText();
            // 这里假设字符串格式固定为"/category/{id}",如果格式有变化可以调整正则或分割逻辑
            String id = categoryStr.replaceAll("^/category/", "");
            category.setId(id);
            // 如果需要name字段,这里可以根据id去查询或者留空,看业务需求
        } else if (node.isObject()) {
            // 处理对象形式:直接反序列化为Category
            category = p.getCodec().treeToValue((ObjectNode) node, Category.class);
        }
        
        return category;
    }
}

3. 测试验证

最后我们可以写个测试代码,验证两种JSON格式都能正确反序列化:

import com.fasterxml.jackson.databind.ObjectMapper;

public class TestDeserialization {
    public static void main(String[] args) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        
        // 测试未展开形式的JSON
        String movieJson1 = "{\"id\":\"777424881071\",\"category\":\"/category/12\",\"title\":\"ACADEMY DINOSAUR\",\"description\":\"A Epic Drama of ...\"}";
        Movie movie1 = mapper.readValue(movieJson1, Movie.class);
        System.out.println("未展开形式解析结果:" + movie1);
        
        // 测试展开形式的JSON
        String movieJson2 = "{\"id\":\"777424881071\",\"category\":{\"id\":\"12\",\"name\":\"Children\"},\"title\":\"ACADEMY DINOSAUR\",\"description\":\"A Epic Drama of ...\"}";
        Movie movie2 = mapper.readValue(movieJson2, Movie.class);
        System.out.println("展开形式解析结果:" + movie2);
    }
}

运行这个测试类,你会看到两种格式的JSON都能正确解析成Movie对象,category字段的id都能正确获取,展开形式的name也能正常读取。

如果你的字符串格式不是固定的/category/{id},只需要调整反序列化器里的字符串解析逻辑就行,比如用正则匹配提取ID部分,或者根据实际的字符串格式做分割。

内容的提问来源于stack exchange,提问作者Jmini

火山引擎 最新活动