讲师中心 微信公众号
AI工具推荐 视频效率加速

Jackson反序列化:处理JSON数组到嵌套List的Java对象映射

夏明吖_3739

夏明吖_3739

发布时间:2025-11-27 11:09:00

|

248人浏览过

|

来源于php中文网

原创

Jackson反序列化:处理JSON数组到嵌套List的Java对象映射

本文深入探讨了在使用jackson库将json数组反序列化为包含嵌套列表的java对象时常见的`mismatchedinputexception`问题。文章详细分析了json结构与pojo定义不匹配的原因,并提供了三种解决方案:直接解析为列表、调整json结构,以及通过自定义反序列化器实现灵活映射,旨在帮助开发者高效解决此类序列化挑战。

在Java开发中,我们经常需要将JSON字符串转换为Java对象(POJO)。Jackson是一个流行的JSON处理库,提供了强大的序列化和反序列化功能。然而,当JSON结构与目标POJO的预期结构不完全匹配时,可能会遇到MismatchedInputException等错误。本文将详细解析一个常见场景:将一个直接由对象数组组成的JSON字符串反序列化到一个包含嵌套列表的POJO中,并提供多种解决方案。

理解JSON与POJO结构不匹配的问题

假设我们有一个JSON字符串,它是一个对象数组,每个对象包含name、number和age字段:

[
    {
        "name": "A",
        "number": 1,
        "age": 20
    },
    {
        "name": "B",
        "number": 2,
        "age": 30
    },
    {
        "name": "C",
        "number": 3,
        "age": 40
    }
]

我们期望将其反序列化到一个名为FooResClass的Java对象中,该对象内部包含一个AData类型的列表:

@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class FooResClass extends BaseResModel {
    private List<AData> aDataList;

    @Data
    @JsonIgnoreProperties(ignoreUnknown = true)
    public class AData { // 注意:此处AData为非静态内部类
        @JsonProperty("number")
        private Long number;
        @JsonProperty("name")
        private String name;
    }
}

直接使用ObjectMapper尝试将上述JSON反序列化为FooResClass实例时,会抛出MismatchedInputException。这是因为:

立即学习“Java免费学习笔记(深入)”;

  1. JSON根结构不匹配:提供的JSON是一个数组([开头),而FooResClass是一个Java对象,Jackson期望JSON的根是一个对象({开头),并且其中包含一个名为aDataList的字段来映射到FooResClass的aDataList属性。
  2. 内部类AData的限制:AData作为FooResClass的非静态内部类,其实例化需要FooResClass的一个实例。Jackson在反序列化时无法独立实例化非静态内部类,这也会导致问题。为了让Jackson能够独立创建AData实例,AData必须声明为static内部类。

解决方案一:直接解析JSON数组到列表

如果JSON字符串本身就是一个列表,我们可以直接将其反序列化为Java的List<AData>类型,然后再手动构建FooResClass对象。

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

import java.io.IOException;
import java.util.List;

public class JsonArrayToListExample {

    // 确保AData是静态内部类,以便Jackson可以独立实例化
    @Data
    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class AData {
        @JsonProperty("number")
        private Long number;
        @JsonProperty("name")
        private String name;
    }

    // 假设FooResClass有一个接收List<AData>的构造函数
    @Data
    @JsonIgnoreProperties(ignoreUnknown = true)
    // 假设BaseResModel存在且FooResClass继承它
    // @EqualsAndHashCode(callSuper = true) // 如果需要
    // @AllArgsConstructor // 如果需要有参构造器
    public static class FooResClass { // FooResClass也应是静态类或顶级类
        private List<AData> aDataList;

        public FooResClass(List<AData> aDataList) {
            this.aDataList = aDataList;
        }
    }

    public static void main(String[] args) throws IOException {
        String json = "[\n" +
                "    {\n" +
                "        \"name\": \"A\",\n" +
                "        \"number\": 1,\n" +
                "        \"age\": 20\n" +
                "    },\n" +
                "    {\n" +
                "        \"name\": \"B\",\n" +
                "        \"number\": 2,\n" +
                "        \"age\": 30\n" +
                "    },\n" +
                "    {\n" +
                "        \"name\": \"C\",\n" +
                "        \"number\": 3,\n" +
                "        \"age\": 40\n" +
                "    }\n" +
                "]";

        ObjectMapper mapper = new ObjectMapper();

        // 直接将JSON数组解析为List<AData>
        List<AData> aDataList = mapper.readValue(json, new TypeReference<List<AData>>() {});

        // 使用解析出的列表构建FooResClass实例
        FooResClass fooRes = new FooResClass(aDataList);

        System.out.println("解析成功,FooResClass实例: " + fooRes);
        System.out.println("列表内容: " + fooRes.getADataList());
    }
}

优点:简单直接,适用于JSON结构本身就是目标列表的情况。 缺点:需要手动构建外层对象,如果FooResClass有更多复杂逻辑,这种方式可能不够优雅。

解决方案二:调整JSON结构以匹配POJO

如果我们可以控制JSON的生成方,最直接的解决方案是修改JSON结构,使其与FooResClass的预期完全匹配。这意味着JSON应该是一个对象,其中包含一个名为aDataList的字段,其值是一个数组。

dmap-jsapi-skill
dmap-jsapi-skill

DMap GL地图JSAPI(dmapgl)开发指南。用于编写、审查、调试 DMap 地图 API 代码,涵盖地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi‑gl 或相关地图开发需求时自动触发。

下载

期望的JSON结构应如下所示:

{
   "aDataList":[
      {
         "name":"A",
         "number":1,
         "age":20
      },
      {
         "name":"B",
         "number":2,
         "age":30
      },
      {
         "name":"C",
         "number":3,
         "age":40
      }
   ]
}

在这种情况下,FooResClass的定义保持不变(确保AData是静态内部类),Jackson将能够自动完成反序列化:

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

import java.io.IOException;
import java.util.List;

public class MatchedJsonStructureExample {

    @Data
    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class AData {
        @JsonProperty("number")
        private Long number;
        @JsonProperty("name")
        private String name;
    }

    @Data
    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class FooResClass {
        @JsonProperty("aDataList") // 确保字段名与JSON中的key匹配
        private List<AData> aDataList;
    }

    public static void main(String[] args) throws IOException {
        String json = "{\n" +
                "   \"aDataList\":[\n" +
                "      {\n" +
                "         \"name\":\"A\",\n" +
                "         \"number\":1,\n" +
                "         \"age\":20\n" +
                "      },\n" +
                "      {\n" +
                "         \"name\":\"B\",\n" +
                "         \"number\":2,\n" +
                "         \"age\":30\n" +
                "      },\n" +
                "      {\n" +
                "         \"name\":\"C\",\n" +
                "         \"number\":3,\n" +
                "         \"age\":40\n" +
                "      }\n" +
                "   ]\n" +
                "}";

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

        System.out.println("解析成功,FooResClass实例: " + fooRes);
        System.out.println("列表内容: " + fooRes.getADataList());
    }
}

优点:Jackson自动处理,代码简洁,符合POJO设计理念。 缺点:要求修改JSON源,这在许多情况下可能无法实现。

解决方案三:实现自定义反序列化器 (推荐)

当无法修改JSON结构,又希望直接将JSON数组反序列化为包含嵌套列表的FooResClass实例时,自定义反序列化器是最佳选择。通过实现JsonDeserializer或继承StdDeserializer,我们可以完全控制反序列化过程。

首先,确保FooResClass和AData都是静态内部类,并且FooResClass有一个接收List<AData>的构造函数(或使用Lombok的@AllArgsConstructor)。

import com.fasterxml.jackson.annotation.JsonCreator; // 用于标记构造函数
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import lombok.AllArgsConstructor; // 如果需要有参构造器
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import java.util.List;

// FooResClass 必须是静态的,如果它作为另一个类的内部类存在
// 或者直接作为顶级类
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
@AllArgsConstructor // 为FooResClass提供一个接收List<AData>的构造函数
@JsonDeserialize(using = FooResClassDeserializer.class) // 指定自定义反序列化器
public static class FooResClass { // 确保是静态内部类或顶级类
    private List<AData> aDataList;

    @Data
    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class AData { // 确保是静态内部类
        @JsonProperty("number")
        private Long number;
        @JsonProperty("name")
        private String name;
    }
}

接下来,创建FooResClassDeserializer,它将负责将JSON数组解析为List<AData>,然后用这个列表来构造FooResClass实例。

import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

import java.io.IOException;
import java.util.List;

public class FooResClassDeserializer extends StdDeserializer<FooResClass> {

    public FooResClassDeserializer() {
        this(null);
    }

    public FooResClassDeserializer(Class<FooResClass> vc) {
        super(vc);
    }

    @Override
    public FooResClass deserialize(JsonParser p,
                                   DeserializationContext ctxt) throws IOException, JacksonException {
        ObjectMapper mapper = (ObjectMapper) p.getCodec(); // 获取当前的ObjectMapper实例
        // 将整个JSON输入(它是一个数组)解析为List<FooResClass.AData>
        List<FooResClass.AData> aDataList = mapper.readValue(p, new TypeReference<List<FooResClass.AData>>() {});
        // 使用解析出的列表构建FooResClass实例
        return new FooResClass(aDataList);
    }
}

最后,在使用ObjectMapper进行反序列化时,Jackson会自动查找@JsonDeserialize注解并使用指定的反序列化器。

import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;

public class CustomDeserializerExample {
    public static void main(String[] args) throws IOException {
        String json = "[\n" +
                "    {\n" +
                "        \"name\": \"A\",\n" +
                "        \"number\": 1,\n" +
                "        \"age\": 20\n" +
                "    },\n" +
                "    {\n" +
                "        \"name\": \"B\",\n" +
                "        \"number\": 2,\n" +
                "        \"age\": 30\n" +
                "    },\n" +
                "    {\n" +
                "        \"name\": \"C\",\n" +
                "        \"number\": 3,\n" +
                "        \"age\": 40\n" +
                "    }\n" +
                "]";

        ObjectMapper mapper = new ObjectMapper();
        // Jackson将使用FooResClass上指定的FooResClassDeserializer
        FooResClass fooRes = mapper.readValue(json, FooResClass.class);

        System.out.println("解析成功,FooResClass实例: " + fooRes);
        System.out.println("列表内容: " + fooRes.getADataList());
    }
}

优点:高度灵活,能够处理各种不匹配的JSON结构,无需修改JSON源。将反序列化逻辑封装起来,提高了代码的可维护性。 缺点:增加了代码量和复杂性,需要编写额外的反序列化器类。

注意事项与总结

  1. 静态内部类:无论是哪种解决方案,如果AData是FooResClass的内部类,它都必须声明为static。否则,Jackson无法在没有外部类实例的情况下创建AData的实例。
  2. @JsonIgnoreProperties(ignoreUnknown = true):这个注解非常有用,它告诉Jackson忽略JSON中存在但POJO中没有定义的字段,避免因字段不匹配而抛出异常。
  3. Lombok注解:@Data简化了getter/setter、equals、hashCode和toString方法的生成。@AllArgsConstructor可以方便地为所有字段生成构造函数,这在自定义反序列化器中非常有用。
  4. TypeReference:在反序列化泛型类型(如List<AData>)时,需要使用TypeReference来帮助Jackson获取正确的泛型类型信息。

总而言之,处理JSON数组到包含嵌套列表的Java对象反序列化问题,关键在于理解JSON与POJO之间的结构差异。当JSON结构与POJO预期不符时,我们可以选择直接解析为列表后手动构建,或者在能控制JSON源的情况下调整JSON结构。而当这些方法不可行时,自定义反序列化器提供了一个强大且灵活的解决方案,能够优雅地桥接JSON与POJO之间的差异。选择哪种方法取决于具体的需求、JSON源的可控性以及对代码复杂度的接受程度。

热门AI工具

更多
SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

AionClaw
AionClaw Hot

AionClaw是一款面向办公、创作和编程任务的AI桌面智能体。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

2135

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

3242

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

1096

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

3719

2025.09.10

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

1738

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

2684

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

6064

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

5649

2023.11.24

OpenJDK JVM参数怎么调优
OpenJDK JVM参数怎么调优

Java应用运行缓慢,如何调整JVM参数?本专题介绍OpenJDK JVM性能调优的基本流程,涵盖性能指标确认、运行数据采集、内存配置和垃圾回收分析,帮助你先定位瓶颈,再通过对照测试验证效果,避免直接套用参数而影响应用稳定性。

60

2026.10.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
dev.java 官方:Learn Java
dev.java 官方:Learn Java

共0课时 | 0人学习

Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn