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的属性:

MusicLM
MusicLM

谷歌平台的AI作曲工具,用文字生成音乐

下载
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不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

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

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

419

2023.08.07

json是什么
json是什么

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

535

2023.08.23

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

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

311

2023.10.13

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

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

77

2025.09.10

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

463

2023.08.02

java多态详细介绍
java多态详细介绍

本专题整合了java多态相关内容,阅读专题下面的文章了解更多详细内容。

15

2025.11.27

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

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

298

2023.08.03

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

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

212

2023.09.04

java入门学习合集
java入门学习合集

本专题整合了java入门学习指南、初学者项目实战、入门到精通等等内容,阅读专题下面的文章了解更多详细学习方法。

1

2026.01.29

热门下载

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

精品课程

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

共23课时 | 3万人学习

C# 教程
C# 教程

共94课时 | 7.9万人学习

Java 教程
Java 教程

共578课时 | 53.1万人学习

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

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