0

0

Jackson反序列化复杂JSON结构:2D数组与多态映射指南

DDD

DDD

发布时间:2025-11-03 14:59:01

|

477人浏览过

|

来源于php中文网

原创

Jackson反序列化复杂JSON结构:2D数组与多态映射指南

本教程深入探讨了如何使用jackson库处理复杂的json反序列化场景。我们将学习如何通过`@jsonformat(shape = jsonformat.shape.array)`注解将2d json数组映射到java对象数组,以及如何利用`@jsoncreator`工厂方法和`mapnode>`动态处理具有不同结构(即多态性)的json数据,从而实现灵活且健壮的反序列化逻辑。

在使用Jackson进行JSON反序列化时,我们通常期望JSON对象的字段能够直接映射到Java对象的属性。然而,当JSON结构变得复杂,例如一个Java对象被表示为JSON数组,或者同一个逻辑实体在不同的JSON输入中具有完全不同的结构时,标准的映射机制可能会遇到挑战。本文将详细介绍如何利用Jackson的强大功能来解决这些问题。

1. 处理2D JSON数组到对象数组的映射

假设我们有一个表示经纬度坐标的Java记录类LngLat,以及一个包含多个LngLat坐标的NoFlyZone记录类。

// LngLat 记录类,表示经纬度
record LngLat(double lng, double lat) {}

// NoFlyZone 记录类,包含一个 LngLat 数组
record NoFlyZone(LngLat[] coordinates) {}

我们可能会遇到以下形式的JSON数据,其中coordinates是一个二维数组,每个内层数组[-3.1, 55.4]代表一个LngLat对象:

String json1 = "[{\"name\":\"Random\"," +
    "\"coordinates\":[[-3.1,55.4],[-3.1,55.9],[-3.7,55.3],[-3.8,55.7],[-3.0,55.8]]}]";

如果直接使用ObjectMapper尝试反序列化,Jackson会抛出MismatchedInputException,因为它期望LngLat是一个JSON对象(例如{"lng":-3.1, "lat":55.4}),但实际遇到的是一个JSON数组。

为了解决这个问题,我们需要告诉Jackson,LngLat对象可以通过JSON数组的形式来表示。这可以通过在LngLat记录类上添加@JsonFormat(shape = JsonFormat.Shape.ARRAY)注解来实现。该注解指示Jackson按照记录中字段的声明顺序,将JSON数组的元素映射到对应的属性。

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.type.TypeReference;

import java.util.List;

// LngLat 记录类,使用 @JsonFormat 注解支持数组形式的 JSON
@JsonFormat(shape = JsonFormat.Shape.ARRAY)
record LngLat(double lng, double lat) {
    // 覆盖 toString() 方法便于输出观察
    @Override
    public String toString() {
        return "LngLat[lng=" + lng + ", lat=" + lat + "]";
    }
}

// NoFlyZone 记录类,coordinates 属性将自动处理 LngLat 数组
@JsonIgnoreProperties("name") // 忽略 JSON 中的 "name" 字段
record NoFlyZone(LngLat[] coordinates) {
    // 覆盖 toString() 方法便于输出观察
    @Override
    public String toString() {
        return "NoFlyZone{coordinates=" + java.util.Arrays.toString(coordinates) + "}";
    }
}

现在,我们可以成功地反序列化包含2D数组的JSON字符串:

public class ArrayDeserializationExample {
    public static void main(String[] args) throws Exception {
        String json1 = "[{\"name\":\"Random\"," +
            "\"coordinates\":[[-3.1,55.4],[-3.1,55.9],[-3.7,55.3],[-3.8,55.7],[-3.0,55.8]]}]";

        ObjectMapper mapper = new ObjectMapper();
        List noFlyZones = mapper.readValue(json1, new TypeReference>() {});

        System.out.println("Deserialized JSON 1: " + noFlyZones);
    }
}

输出示例:

Deserialized JSON 1: [NoFlyZone{coordinates=[LngLat[lng=-3.1, lat=55.4], LngLat[lng=-3.1, lat=55.9], LngLat[lng=-3.7, lat=55.3], LngLat[lng=-3.8, lat=55.7], LngLat[lng=-3.0, lat=55.8]]}]

2. 处理多态JSON结构:动态创建对象

在某些情况下,同一个Java对象可能需要从两种或更多种完全不同的JSON结构中反序列化。例如,除了上述的JSON 1,我们可能还会遇到以下形式的JSON 2,其中经纬度直接作为NoFlyZone的属性:

百度文心百中
百度文心百中

百度大模型语义搜索体验中心

下载
String json2 = "[{\"name\":\"Random\"," + "\"longitude\":-3.1, \"latitude\":55}]";

如果LngLat仍然使用@JsonFormat(shape = JsonFormat.Shape.ARRAY),那么反序列化JSON 2时,Jackson将无法找到LngLat的数组表示,导致新的错误。为了同时支持这两种完全不同的JSON结构,我们需要在更高层次(即NoFlyZone级别)进行自定义反序列化。

我们可以通过在NoFlyZone记录类中引入一个静态工厂方法,并使用@JsonCreator注解来指示Jackson使用此方法创建对象。这个工厂方法可以接收一个Map参数,从而允许我们检查原始JSON的所有字段,并根据其结构动态地构建NoFlyZone实例。

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.core.type.TypeReference;

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

// LngLat 记录类保持不变,仍然使用 @JsonFormat 支持数组形式
@JsonFormat(shape = JsonFormat.Shape.ARRAY)
record LngLat(double lng, double lat) {
    @Override
    public String toString() {
        return "LngLat[lng=" + lng + ", lat=" + lat + "]";
    }
}

// NoFlyZone 记录类,使用 @JsonCreator 处理多态 JSON 结构
@JsonIgnoreProperties(ignoreUnknown = true) // 忽略 JSON 中未映射的字段,如 "name"
public record NoFlyZone(LngLat[] coordinates) {

    @JsonCreator
    public static NoFlyZone getInstance(Map fields) throws IOException {
        LngLat[] longLatArray;

        // 判断 JSON 结构:是否存在 "coordinates" 字段
        boolean hasCoordinatesArray = fields.containsKey("coordinates");

        if (hasCoordinatesArray) {
            // 结构 1: 包含 "coordinates" 数组
            ObjectReader reader = new ObjectMapper().readerFor(LngLat[].class);
            longLatArray = reader.readValue(fields.get("coordinates")); // 反序列化 "coordinates" 节点
        } else {
            // 结构 2: 包含 "longitude" 和 "latitude" 字段
            JsonNode longitudeNode = fields.get("longitude");
            JsonNode latitudeNode = fields.get("latitude");

            // 检查字段是否存在以避免 NullPointerException
            if (longitudeNode == null || latitudeNode == null) {
                throw new IOException("Missing 'longitude' or 'latitude' fields for single LngLat representation.");
            }

            longLatArray = new LngLat[]{
                new LngLat(
                    longitudeNode.asDouble(),
                    latitudeNode.asDouble()
                )
            };
        }
        return new NoFlyZone(longLatArray);
    }

    @Override
    public String toString() {
        return "NoFlyZone{coordinates=" + java.util.Arrays.toString(coordinates) + "}";
    }
}

在上述代码中:

  • @JsonIgnoreProperties(ignoreUnknown = true) 用于忽略JSON中可能存在的,但在Java类中没有对应属性的字段(例如"name"),防止反序列化失败。
  • @JsonCreator 标记了一个静态工厂方法getInstance,Jackson会调用此方法来构造NoFlyZone实例。
  • getInstance方法接收Map,这允许我们访问原始JSON的所有字段及其对应的JsonNode。
  • 通过检查fields.containsKey("coordinates"),我们能判断当前JSON是属于哪种结构。
  • 对于包含coordinates数组的结构,我们使用一个独立的ObjectMapper().readerFor(LngLat[].class)来反序列化coordinates对应的JsonNode。
  • 对于包含longitude和latitude的结构,我们直接从JsonNode中提取双精度值,并创建一个单元素的LngLat数组。

现在,我们可以同时反序列化两种不同结构的JSON字符串:

public class PolymorphicDeserializationExample {
    public static void main(String[] args) throws Exception {
        String json1 = "[{\"name\":\"Random\"," +
            "\"coordinates\":[[-3.1,55.4],[-3.1,55.9],[-3.7,55.3],[-3.8,55.7],[-3.0,55.8]]}]";

        String json2 = "[{\"name\":\"Random\"," + "\"longitude\":-3.1, \"latitude\":55}]";

        ObjectMapper mapper = new ObjectMapper();

        List noFlyZones1 = mapper.readValue(json1, new TypeReference>() {});
        System.out.println("Deserialized JSON 1 (Polymorphic): " + noFlyZones1);

        List noFlyZones2 = mapper.readValue(json2, new TypeReference>() {});
        System.out.println("Deserialized JSON 2 (Polymorphic): " + noFlyZones2);
    }
}

输出示例:

Deserialized JSON 1 (Polymorphic): [NoFlyZone{coordinates=[LngLat[lng=-3.1, lat=55.4], LngLat[lng=-3.1, lat=55.9], LngLat[lng=-3.7, lat=55.3], LngLat[lng=-3.8, lat=55.7], LngLat[lng=-3.0, lat=55.8]]}]
Deserialized JSON 2 (Polymorphic): [NoFlyZone{coordinates=[LngLat[lng=-3.1, lat=55.0]]}]

总结与注意事项

  • @JsonFormat(shape = JsonFormat.Shape.ARRAY): 这是处理将JSON数组直接映射到Java对象属性的简洁方法。它要求JSON数组的元素顺序与Java类中字段的声明顺序一致。适用于固定、单一的数组表示形式。
  • @JsonCreator与工厂方法: 当需要处理更复杂的、具有多态性的JSON结构时,@JsonCreator结合工厂方法提供了最大的灵活性。通过接收Map,您可以在工厂方法内部根据JSON内容进行条件判断,并执行自定义的解析逻辑。
  • @JsonIgnoreProperties(ignoreUnknown = true): 在处理多态或不确定字段的JSON时,这是一个非常有用的注解,可以防止Jackson因遇到Java类中没有对应属性的JSON字段而抛出异常。
  • TypeReference: 当反序列化泛型类型(如List)时,需要使用new TypeReference>() {}来提供完整的类型信息,以便Jackson正确处理。
  • 错误处理: 在@JsonCreator工厂方法中,应考虑对缺失关键字段的情况进行错误处理,例如抛出IOException或其他业务异常,以确保数据的完整性和程序的健壮性。
  • 性能考量: Map的方式会先将整个JSON节点解析为JsonNode树,然后进行处理。对于非常大的JSON,这可能会有轻微的性能开销。但在大多数实际应用中,这种开销是可接受的,并且其带来的灵活性优势更为突出。

通过掌握这些Jackson的高级反序列化技巧,您可以有效地处理各种复杂和多变的JSON数据结构,使您的Java应用程序能够更健壮、更灵活地与外部系统进行数据交互。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

868

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

745

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

741

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

398

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

440

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

447

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

431

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16948

2023.08.03

拼多多赚钱的5种方法 拼多多赚钱的5种方法
拼多多赚钱的5种方法 拼多多赚钱的5种方法

在拼多多上赚钱主要可以通过无货源模式一件代发、精细化运营特色店铺、参与官方高流量活动、利用拼团机制社交裂变,以及成为多多进宝推广员这5种方法实现。核心策略在于通过低成本、高效率的供应链管理与营销,利用平台社交电商红利实现盈利。

98

2026.01.26

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.9万人学习

C# 教程
C# 教程

共94课时 | 7.7万人学习

Java 教程
Java 教程

共578课时 | 51.7万人学习

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

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号