首页 > Java > java教程 > 正文

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

DDD
发布: 2025-11-03 14:59:01
原创
466人浏览过

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<NoFlyZone> noFlyZones = mapper.readValue(json1, new TypeReference<List<NoFlyZone>>() {});

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

博思AIPPT
博思AIPPT

博思AIPPT来了,海量PPT模板任选,零基础也能快速用AI制作PPT。

博思AIPPT 117
查看详情 博思AIPPT
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<String, JsonNode>参数,从而允许我们检查原始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<String, JsonNode> 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<String, JsonNode>,这允许我们访问原始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<NoFlyZone> noFlyZones1 = mapper.readValue(json1, new TypeReference<List<NoFlyZone>>() {});
        System.out.println("Deserialized JSON 1 (Polymorphic): " + noFlyZones1);

        List<NoFlyZone> noFlyZones2 = mapper.readValue(json2, new TypeReference<List<NoFlyZone>>() {});
        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<String, JsonNode>,您可以在工厂方法内部根据JSON内容进行条件判断,并执行自定义的解析逻辑。
  • @JsonIgnoreProperties(ignoreUnknown = true): 在处理多态或不确定字段的JSON时,这是一个非常有用的注解,可以防止Jackson因遇到Java类中没有对应属性的JSON字段而抛出异常。
  • TypeReference: 当反序列化泛型类型(如List<NoFlyZone>)时,需要使用new TypeReference<List<NoFlyZone>>() {}来提供完整的类型信息,以便Jackson正确处理。
  • 错误处理: 在@JsonCreator工厂方法中,应考虑对缺失关键字段的情况进行错误处理,例如抛出IOException或其他业务异常,以确保数据的完整性和程序的健壮性。
  • 性能考量: Map<String, JsonNode>的方式会先将整个JSON节点解析为JsonNode树,然后进行处理。对于非常大的JSON,这可能会有轻微的性能开销。但在大多数实际应用中,这种开销是可接受的,并且其带来的灵活性优势更为突出。

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

以上就是Jackson反序列化复杂JSON结构:2D数组与多态映射指南的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

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