0

0

深入理解Jackson处理动态键名JSON的策略

DDD

DDD

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

|

186人浏览过

|

来源于php中文网

原创

深入理解jackson处理动态键名json的策略

本文探讨了如何使用Jackson库处理JSON数据中包含动态生成键名的情况,并将其序列化与反序列化为Java POJO。文章详细介绍了两种核心策略:通过引入包装类将动态键名封装到Map字段中,以及直接将JSON反序列化为Map类型,并提供了相应的代码示例和注意事项,帮助开发者有效应对此类场景。

在处理外部服务返回的JSON数据时,我们有时会遇到顶级属性键名是动态生成的情况。例如,JSON结构可能如下所示,其中 "random1"、"nextRandom500" 等键是不可预测的:

{
    "random1" : {
        "name" : "john",
        "lastName" : "johnson"
    },
    "nextRandom500" : {
        "name" : "jack",
        "lastName" : "jackson"
    },
    "random100500" : {
        "name" : "jack",
        "lastName" : "johnson"
    }   
}

直接将这种结构反序列化到仅包含固定字段的POJO(如 UserResponse 仅有一个 users 字段)会导致 UnrecognizedPropertyException 错误,因为Jackson无法找到与动态键名匹配的POJO属性。为了正确处理这种场景,Jackson提供了两种主要的策略。

1. 策略一:使用包装POJO封装动态键名

如果允许修改JSON的结构,或者可以在客户端对JSON进行预处理,使其包含一个固定的根属性来包裹所有动态键名,那么可以使用一个包含 Map 字段的包装POJO。

1.1 JSON结构调整

将原始JSON调整为如下形式,引入一个固定的根键(例如 "users")来包含所有动态键名及其对应的值:

{
  "users": {
    "random1" : {
      "name" : "john",
      "lastName" : "johnson"
    },
    "nextRandom500" : {
      "name" : "jack",
      "lastName" : "jackson"
    },
    "random100500" : {
      "name" : "jack",
      "lastName" : "johnson"
    }
  }
}

1.2 POJO定义

首先,定义内部的 User POJO,它代表了每个动态键名对应的值结构:

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;

@Data
@SuperBuilder(toBuilder=true)
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class User {
    private String name;
    private String lastName;
}

然后,定义一个包装POJO UserResponse,其中包含一个 Map 字段,其键名为 "users",与调整后的JSON结构中的根键对应:

X Detector
X Detector

最值得信赖的多语言 AI 内容检测器

下载
import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.Map;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;

@Data
@SuperBuilder(toBuilder=true)
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class UserResponse {
    private Map users; // 这里的"users"字段将映射到JSON中的"users"键
}

1.3 反序列化与序列化

使用 ObjectMapper 进行反序列化和序列化操作将变得直接:

import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;

public class JsonProcessingExample {

    public static void main(String[] args) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();

        // 示例JSON (调整后的结构)
        String jsonWithWrapper = "{\n" +
                "  \"users\": {\n" +
                "    \"random1\" : {\n" +
                "      \"name\" : \"john\",\n" +
                "      \"lastName\" : \"johnson\"\n" +
                "    },\n" +
                "    \"nextRandom500\" : {\n" +
                "      \"name\" : \"jack\",\n" +
                "      \"lastName\" : \"jackson\"\n" +
                "    },\n" +
                "    \"random100500\" : {\n" +
                "      \"name\" : \"jack\",\n" +
                "      \"lastName\" : \"johnson\"\n" +
                "    }\n" +
                "  }\n" +
                "}";

        // 反序列化
        UserResponse userResponse = objectMapper.readValue(jsonWithWrapper, UserResponse.class);
        System.out.println("Deserialized UserResponse:");
        userResponse.getUsers().forEach((key, user) ->
                System.out.println("  Key: " + key + ", Name: " + user.getName() + ", Last Name: " + user.getLastName()));

        // 序列化回JSON
        String serializedJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(userResponse);
        System.out.println("\nSerialized JSON from UserResponse:");
        System.out.println(serializedJson);

        // 构建一个UserResponse对象进行序列化
        UserResponse newUserResponse = new UserResponse();
        Map newUsers = new HashMap<>();
        newUsers.put("newRandomKey1", User.builder().name("Alice").lastName("Smith").build());
        newUsers.put("newRandomKey2", User.builder().name("Bob").lastName("White").build());
        newUserResponse.setUsers(newUsers);

        String newSerializedJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(newUserResponse);
        System.out.println("\nSerialized JSON from new UserResponse object:");
        System.out.println(newSerializedJson);
    }
}

2. 策略二:直接反序列化为 Map

如果无法修改JSON的原始结构,或者动态键名本身就是JSON的根,那么可以直接将JSON反序列化为一个 Map 类型。

2.1 POJO定义

在这种策略下,我们只需要定义 User POJO即可,不需要 UserResponse 包装类:

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;

@Data
@SuperBuilder(toBuilder=true)
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class User {
    private String name;
    private String lastName;
}

2.2 反序列化与序列化

由于 Map 是一个泛型类型,Jackson在反序列化时需要 TypeReference 来正确推断类型信息。

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;

public class JsonProcessingDirectMapExample {

    public static void main(String[] args) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();

        // 示例JSON (原始结构)
        String originalJson = "{\n" +
                "    \"random1\" : {\n" +
                "        \"name\" : \"john\",\n" +
                "        \"lastName\" : \"johnson\"\n" +
                "    },\n" +
                "    \"nextRandom500\" : {\n" +
                "        \"name\" : \"jack\",\n" +
                "        \"lastName\" : \"jackson\"\n" +
                "    },\n" +
                "    \"random100500\" : {\n" +
                "        \"name\" : \"jack\",\n" +
                "        \"lastName\" : \"johnson\"\n" +
                "    }   \n" +
                "}";

        // 反序列化为 Map
        Map usersMap = objectMapper.readValue(originalJson,
                new TypeReference>() {});
        System.out.println("Deserialized Map:");
        usersMap.forEach((key, user) ->
                System.out.println("  Key: " + key + ", Name: " + user.getName() + ", Last Name: " + user.getLastName()));

        // 序列化回JSON
        String serializedJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(usersMap);
        System.out.println("\nSerialized JSON from Map:");
        System.out.println(serializedJson);

        // 构建一个Map对象进行序列化
        Map newUsersMap = new HashMap<>();
        newUsersMap.put("anotherRandomKeyA", User.builder().name("Charlie").lastName("Brown").build());
        newUsersMap.put("anotherRandomKeyB", User.builder().name("Diana").lastName("Prince").build());

        String newSerializedJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(newUsersMap);
        System.out.println("\nSerialized JSON from new Map object:");
        System.out.println(newSerializedJson);
    }
}

3. 注意事项与总结

  • 选择策略:
    • 如果JSON结构可以调整,且你希望有一个更结构化的Java对象来代表整个响应,那么使用策略一(包装POJO)更为合适。它提供了更好的类型安全性和封装性
    • 如果JSON结构必须保持原样(动态键名位于根部),并且你只是想获取键值对数据,那么策略二(直接反序列化为 Map是更直接、更简洁的选择。
  • TypeReference 的使用: 在反序列化泛型类型(如 Map 或 List) 时,Jackson需要 TypeReference 来在运行时获取完整的泛型类型信息,以避免类型擦除带来的问题。
  • POJO的设计: 确保内部的 User POJO与动态键名对应的值结构完全匹配。
  • Lombok依赖: 示例代码使用了Lombok的 @Data, @NoArgsConstructor, @SuperBuilder 注解来简化POJO的编写,实际项目中可根据需要选择使用或手动编写getter/setter/构造函数。
  • Jackson配置: objectMapper.writerWithDefaultPrettyPrinter() 用于生成格式化良好的JSON输出,便于阅读。@JsonInclude(JsonInclude.Include.NON_NULL) 注解在序列化时会忽略值为 null 的字段。

通过理解并应用上述两种策略,开发者可以灵活高效地使用Jackson库处理各种包含动态键名的JSON数据,实现与Java POJO之间的无缝序列化和反序列化。

相关专题

更多
java
java

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

832

2023.06.15

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

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

737

2023.07.05

java自学难吗
java自学难吗

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

734

2023.07.31

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

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

397

2023.08.01

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

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

398

2023.08.02

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

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

446

2023.08.02

java有什么用
java有什么用

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

430

2023.08.02

java在线网站
java在线网站

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

16925

2023.08.03

Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

36

2026.01.14

热门下载

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

精品课程

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

共23课时 | 2.5万人学习

C# 教程
C# 教程

共94课时 | 6.7万人学习

Java 教程
Java 教程

共578课时 | 46万人学习

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

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