value of type `java.lang.Long` from Object value (token `JsonToken.START_OBJECT`)

value of type java.lang.Long from Object value (token JsonToken.START_OBJECT)

一、背景与问题

在使用Jackson库进行JSON反序列化时,开发者常遇到以下异常:

Cannot deserialize value of type `java.lang.Long` from Object value (token `JsonToken.START_OBJECT`)

这个错误的核心原因是:Jackson期望将一个JSON对象({})反序列化为Long类型,但实际无法完成类型转换。这通常发生在以下场景中:

  1. JSON字段值是一个嵌套对象(如{"id": {"value": 123}})
  2. Java实体类字段类型为Long,但JSON中对应字段是对象
  3. 使用ObjectMapper未正确配置类型信息

这个错误揭示了Jackson类型推断机制的局限性,也暴露了在复杂数据结构处理时的潜在风险。

二、基本原理

Jackson的反序列化流程遵循以下关键步骤:

  1. Token解析:读取JSON的START_OBJECT标记,进入对象解析模式
  2. 字段匹配:根据@JsonProperty注解或字段名匹配JSON键
  3. 类型推断:根据字段类型和JSON值类型决定反序列化策略
  4. 类型转换:执行具体类型的反序列化逻辑(如Number到Long)

当遇到START_OBJECT时,Jackson会尝试将整个JSON对象作为值类型处理,此时如果字段类型是Long,就会触发类型不匹配错误。这种行为本质上是Jackson的"类型安全"机制在起作用。

三、环境准备

// Maven依赖
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.2</version>
</dependency>

测试用的JSON数据:

{
  "id": {
    "value": 123
  },
  "name": "John Doe"
}

四、核心实现

1. 基础错误示例

public class User {
    @JsonProperty("id")
    private Long id;
    
    @JsonProperty("name")
    private String name;
    
    // 省略getter/setter
}
public class Main {
    public static void main(String[] args) throws Exception {
        String json = "{ \"id\": { \"value\": 123 }, \"name\": \"John Doe\" }";
        
        ObjectMapper mapper = new ObjectMapper();
        User user = mapper.readValue(json, User.class);
        System.out.println(user.getName()); // 会抛出异常
    }
}

错误原因:id字段期望Long类型,但JSON中id字段的值是一个对象({ "value": 123 }),Jackson无法直接转换。


2. 使用@JsonFormat解决方案

public class User {
    @JsonProperty("id")
    @JsonFormat(shape = Shape.OBJECT)
    private Long id;
    
    @JsonProperty("name")
    private String name;
    
    // 省略getter/setter
}
public class Main {
    public static void main(String[] args) throws Exception {
        String json = "{ \"id\": { \"value\": 123 }, \"name\": \"John Doe\" }";
        
        ObjectMapper mapper = new ObjectMapper();
        User user = mapper.readValue(json, User.class);
        System.out.println(user.getName()); // 成功
    }
}

关键点解释:

  • @JsonFormat(shape = Shape.OBJECT) 告诉Jackson该字段期望一个对象
  • Jackson会将JSON对象转换为Long类型,但实际处理逻辑需要额外配置

3. 自定义反序列化器方案

public class CustomLongDeserializer extends JsonDeserializer<Long> {
    @Override
    public Long deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        if (p.getCurrentToken() == JsonToken.START_OBJECT) {
            JsonNode node = p.readTree();
            return node.get("value").asLong();
        }
        return p.getValueAsLong();
    }
}
public class User {
    @JsonProperty("id")
    @JsonDeserialize(using = CustomLongDeserializer.class)
    private Long id;
    
    @JsonProperty("name")
    private String name;
    
    // 省略getter/setter
}
public class Main {
    public static void main(String[] args) throws Exception {
        String json = "{ \"id\": { \"value\": 123 }, \"name\": \"John Doe\" }";
        
        ObjectMapper mapper = new ObjectMapper();
        User user = mapper.readValue(json, User.class);
        System.out.println(user.getName()); // 成功
    }
}

关键点解释:

  • 自定义反序列化器需要继承JsonDeserializer
  • JsonToken.START_OBJECT判断处理嵌套对象
  • 使用JsonNode获取嵌套字段值

五、完整案例

场景描述

某个电商平台的API返回如下JSON:

{
  "product": {
    "id": {
      "value": 1001
    },
    "name": "Laptop",
    "price": 999.99
  }
}

对应的Java实体类需要处理嵌套ID结构:

public class Product {
    @JsonProperty("id")
    @JsonFormat(shape = Shape.OBJECT)
    private Long id;
    
    @JsonProperty("name")
    private String name;
    
    @JsonProperty("price")
    private BigDecimal price;
    
    // 省略getter/setter
}
public class Response {
    @JsonProperty("product")
    private Product product;
    
    // 省略getter/setter
}

完整测试代码:

public class Main {
    public static void main(String[] args) throws Exception {
        String json = "{ \"product\": { \"id\": { \"value\": 1001 }, \"name\": \"Laptop\", \"price\": 999.99 } }";
        
        ObjectMapper mapper = new ObjectMapper();
        Response response = mapper.readValue(json, Response.class);
        System.out.println("Product ID: " + response.getProduct().getId()); // 输出: Product ID: 1001
    }
}

六、源码解析

Jackson的反序列化流程关键代码在AbstractDeserializer类中:

public abstract class AbstractDeserializer implements JsonDeserializer {
    public final void deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        if (p.currentToken() == JsonToken.START_OBJECT) {
            // 处理对象类型
            readObject(p, ctxt);
        } else if (p.currentToken() == JsonToken.START_ARRAY) {
            // 处理数组类型
            readArray(p, ctxt);
        } else {
            // 处理基本类型
            readScalar(p, ctxt);
        }
    }
}

当遇到START_OBJECT时,Jackson会调用readObject方法,此时会根据字段类型进行类型转换。对于Long类型,会尝试将整个对象转换为数值,但由于类型不匹配导致异常。

七、进阶使用

1. 复杂嵌套结构处理

public class NestedId {
    @JsonProperty("value")
    private Long value;
    
    // 省略getter/setter
}
public class Product {
    @JsonProperty("id")
    @JsonFormat(shape = Shape.OBJECT)
    private NestedId id;
    
    // 省略其他字段
}

2. 自动类型转换配置

public class CustomObjectMapper extends ObjectMapper {
    public CustomObjectMapper() {
        enable(DeserializationFeature.USE_JAVA_OBJECT_IN_EMBEDED_OBJECTS);
    }
}

3. 配合Jackson注解使用

@JsonInclude(Include.ALWAYS)
@JsonInclude(JsonInclude.Include.NON_NULL)

八、性能与工程实践

1. 性能优化

  • 使用@JsonFormat(shape = Shape.OBJECT)代替自定义反序列化器(减少开销)
  • 避免在高频使用的类中使用自定义反序列化器
  • 对于复杂结构,可考虑使用JsonNode进行后续处理

2. 异常处理

try {
    User user = mapper.readValue(json, User.class);
} catch (JsonProcessingException e) {
    // 记录日志
    logger.error("JSON反序列化失败", e);
    // 返回默认值或空对象
    return new User();
}

3. 安全考量

  • 对于不可信的JSON数据,建议使用setAcceptUnknownFields(false)禁用未知字段
  • 对于敏感字段,建议使用@JsonIgnore或@JsonProperty控制访问
  • 对于复杂结构,建议使用JsonNode进行类型检查

九、常见问题与踩坑

1. 错误示例:误用Object类型

public class User {
    @JsonProperty("id")
    private Object id;
    
    // 省略getter/setter
}

问题:Object类型可能导致类型混淆,建议明确类型

2. 错误示例:未处理嵌套结构

public class User {
    @JsonProperty("id")
    private Long id;
    
    // 省略getter/setter
}

问题:直接使用Long类型无法处理嵌套对象

3. 错误示例:未配置ObjectMapper

ObjectMapper mapper = new ObjectMapper();
mapper.readValue(json, User.class);

问题:未配置ObjectMapper可能导致无法处理复杂结构

十、最佳实践

  1. 明确类型:对于复杂结构,优先使用JsonFormat或自定义反序列化器
  2. 避免Object类型:除非需要处理动态数据,否则应明确类型
  3. 配置ObjectMapper:对于复杂结构,建议配置ObjectMapper的反序列化策略
  4. 异常处理:对所有反序列化操作添加异常处理逻辑
  5. 安全防护:对不可信数据使用setAcceptUnknownFields(false)
  6. 性能优化:对于高频使用的类,避免使用自定义反序列化器

十一、总结

value of type java.lang.Long from Object value错误揭示了Jackson在处理复杂JSON结构时的类型转换机制。通过理解其工作原理,我们可以采取多种策略解决问题:

  • 使用@JsonFormat指定类型形状
  • 自定义反序列化器处理复杂逻辑
  • 优化ObjectMapper配置
  • 加强异常处理和安全防护

在实际开发中,应根据具体场景选择合适的方案。对于简单结构,使用@JsonFormat即可;对于复杂结构,自定义反序列化器提供了更大的灵活性。同时,需要警惕类型混淆和安全风险,确保系统的健壮性和安全性。

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日