0

0

Jackson处理带有动态属性键的JSON数据:序列化与反序列化教程

聖光之護

聖光之護

发布时间:2025-09-27 09:30:02

|

611人浏览过

|

来源于php中文网

原创

Jackson处理带有动态属性键的JSON数据:序列化与反序列化教程

本教程详细介绍了如何使用Jackson库处理JSON数据中包含动态或随机生成属性键的场景。我们将探讨当标准POJO映射失效时,如何通过TypeReference直接将JSON反序列化为Map结构,以及在特定JSON结构下如何利用包装POJO进行处理,并提供相应的序列化方法和示例代码,帮助开发者高效、灵活地解析和生成复杂JSON。

引言与问题背景

在与外部服务进行数据交互时,我们经常会遇到json数据结构不完全固定的情况。尤其当json的顶级属性键是动态生成或随机变化的,例如"random1"、"nextrandom500"等,而其对应的值结构却是固定的对象时,使用jackson进行标准pojo(plain old java object)映射会遇到挑战。jackson默认期望json的键与pojo的属性名严格对应,一旦遇到未知属性,便会抛出com.fasterxml.jackson.databind.exc.unrecognizedpropertyexception异常。

例如,以下JSON结构中,random1、nextRandom500、random100500都是动态键:

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

当尝试将上述JSON直接反序列化到一个包含Map属性的UserResponse POJO时,如果JSON本身没有一个固定的包装键(如"users"),Jackson将无法识别"random1"等顶级键,从而导致反序列化失败。

问题分析:为何会抛出UnrecognizedPropertyException?

Jackson在进行对象反序列化时,会尝试将JSON中的每个键映射到目标POJO类中的一个属性。如果JSON中存在POJO类中未定义的顶级键,且没有特殊配置(如@JsonIgnoreProperties(ignoreUnknown = true)),Jackson就会认为这是一个“无法识别的属性”,并抛出UnrecognizedPropertyException。

在上述示例中,如果我们的UserResponse POJO定义如下:

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; // Jackson会寻找名为"users"的键

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

而实际的JSON数据是:

{
    "random1" : { /* ... */ },
    "nextRandom500" : { /* ... */ }
}

Jackson会尝试将"random1"映射到UserResponse的某个属性,但UserResponse中只有一个users属性。由于"random1"与"users"不匹配,Jackson便会抛出异常。

解决方案一:直接映射到Map结构

当JSON的顶级键是动态的,但其值结构是固定的对象时,最直接有效的方法是将整个JSON反序列化为一个Map类型。Jackson的TypeReference提供了一种在运行时保留泛型类型信息的方式,非常适合这种场景。

1. POJO定义

首先,我们需要定义动态键所对应的值对象User:

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;
}

注意:这里不再需要一个外部的UserResponse类来包装这个Map,因为我们将直接把整个JSON看作一个Map。

2. 反序列化

使用ObjectMapper结合TypeReference将JSON字符串反序列化为Map

Magician
Magician

Figma插件,AI生成图标、图片和UX文案

下载
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.io.IOException;
import java.util.Map;

public class JsonDeserializationExample {

    public static void main(String[] args) throws IOException {
        String jsonString = "{\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" +
                "}";

        ObjectMapper objectMapper = new ObjectMapper();
        // 配置以美化输出,非必需
        objectMapper.enable(SerializationFeature.INDENT_OUTPUT);

        // 使用TypeReference进行反序列化
        Map usersMap = objectMapper.readValue(jsonString, new TypeReference>(){});

        System.out.println("反序列化成功,Map内容:");
        usersMap.forEach((key, user) -> 
            System.out.println("  Key: " + key + ", User: " + user.getName() + " " + user.getLastName())
        );
    }
}

解释: new TypeReference>(){}是一个匿名内部类,它允许Jackson在运行时获取到Map的完整泛型类型信息,从而正确地将JSON中的每个顶级键值对映射到Map中的相应条目。

3. 序列化

将Map对象序列化回原始JSON格式同样简单,只需直接调用ObjectMapper的writeValueAsString方法:

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

public class JsonSerializationExample {

    public static void main(String[] args) throws IOException {
        // 创建一个Map对象
        Map usersMap = new HashMap<>();
        usersMap.put("randomA", new User("Alice", "Smith"));
        usersMap.put("randomB", new User("Bob", "Johnson"));

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.enable(SerializationFeature.INDENT_OUTPUT); // 美化输出

        // 序列化Map到JSON字符串
        String serializedJson = objectMapper.writeValueAsString(usersMap);

        System.out.println("序列化后的JSON:");
        System.out.println(serializedJson);
    }
}

解决方案二:使用包装POJO(适用于特定JSON结构)

如果JSON的结构并非直接由动态键构成,而是包含一个固定的顶级键,该键的值是一个由动态键组成的Map,那么使用一个包装POJO是更合适的选择。

例如,如果你的JSON数据实际上是这样的:

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

在这种情况下,"users"是一个固定的顶级键,其值是一个Map

1. POJO定义

此时,你最初定义的UserResponse POJO是完全适用的:

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; // Jackson会寻找名为"users"的键

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

2. 反序列化与序列化

直接使用ObjectMapper对UserResponse.class进行反序列化和序列化:

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

public class WrappedPojoExample {

    public static void main(String[] args) throws IOException {
        String wrappedJsonString = "{\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" +
                "}";

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.enable(SerializationFeature.INDENT_OUTPUT);

        // 反序列化
        UserResponse userResponse = objectMapper.readValue(wrappedJsonString, UserResponse.class);
        System.out.println("反序列化成功,UserResponse内容:");
        userResponse.getUsers().forEach((key, user) -> 
            System.out.println("  Key: " + key + ", User: " + user.getName() + " " + user.getLastName())
        );

        // 序列化
        UserResponse newResponse = new UserResponse();
        Map newUsers = new HashMap<>();
        newUsers.put("newRandomX", new User("Charlie", "Brown"));
        newResponse.setUsers(newUsers);

        String serializedWrappedJson = objectMapper.writeValueAsString(newResponse);
        System.out.println("\n序列化后的JSON:");
        System.out.println(serializedWrappedJson);
    }
}

总结与注意事项

  1. 理解JSON结构是关键: 在选择反序列化策略之前,务必清晰地了解你所处理的JSON数据的确切结构。
    • 如果整个JSON的顶级键都是动态的,且其值是结构化对象,请使用解决方案一(直接映射到Map)
    • 如果JSON包含一个固定的顶级键,该键的值是一个动态键的Map,请使用解决方案二(使用包装POJO)
  2. TypeReference的用途: TypeReference是Jackson处理泛型类型(如List、Map)时获取完整类型信息的强大工具,尤其在编译时无法确定具体泛型参数时非常有用。
  3. Lombok简化POJO: 示例中使用了Lombok注解(@Data, @SuperBuilder, @NoArgsConstructor)来简化POJO的编写,减少了样板代码。在实际项目中,可以根据需要选择使用。
  4. @JsonInclude(JsonInclude.Include.NON_NULL): 这个Jackson注解在序列化时会忽略值为null的属性,使得生成的JSON更加简洁。
  5. 异常处理: 在实际应用中,objectMapper.readValue()和objectMapper.writeValueAsString()方法可能会抛出JsonProcessingException(或其子类IOException),因此需要进行适当的异常捕获和处理。
  6. 依赖管理: 确保项目中已正确引入Jackson库的依赖,例如jackson-databind和jackson-annotations。
    
        com.fasterxml.jackson.core
        jackson-databind
        2.13.4 
    
    
        com.fasterxml.jackson.core
        jackson-annotations
        2.13.4 
    
    
    
        org.projectlombok
        lombok
        1.18.24 
        provided
    

通过掌握这些技巧,开发者可以灵活应对各种复杂的JSON数据结构,高效地进行序列化和反序列化操作。

热门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

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

236

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

458

2024.03.01

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

236

2023.09.22

clawdbot ai使用教程 保姆级clawdbot部署安装手册
clawdbot ai使用教程 保姆级clawdbot部署安装手册

Clawdbot是一个“有灵魂”的AI助手,可以帮用户清空收件箱、发送电子邮件、管理日历、办理航班值机等等,并且可以接入用户常用的任何聊天APP,所有的操作均可通过WhatsApp、Telegram等平台完成,用户只需通过对话,就能操控设备自动执行各类任务。

19

2026.01.29

热门下载

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

精品课程

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

共23课时 | 3万人学习

C# 教程
C# 教程

共94课时 | 7.9万人学习

Java 教程
Java 教程

共578课时 | 52.9万人学习

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

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