电子书 – Spring Cloud 指南 – NPI EA (分类=Spring Cloud)
announcement - icon

让我们开始使用 Spring Cloud 的微服务架构

>> 加入 Pro 并下载电子书

电子书 – Mockito – NPI EA (标签 = Mockito)
announcement - icon

模拟是单元测试的重要组成部分,Mockito 库使编写 清晰直观的单元测试 变得容易,用于您的 Java 代码。

通过我们的 Mockito 指南 开始模拟,并改进您的应用程序测试

下载电子书

电子书 – Java 并发 – NPI EA (分类=Java 并发)
announcement - icon

在应用程序中处理并发可能是一个棘手的过程,其中包含许多 潜在的陷阱。 扎实的掌握基本知识将有助于最大程度地减少这些问题。

通过我们的 Java 并发 指南开始了解多线程应用程序

>> 下载电子书

电子书 – 响应式 – NPI EA (分类=响应式)
announcement - icon

Spring 5 增加了对使用 Spring WebFlux 模块进行响应式编程的支持,此支持自那时起不断改进。 开始使用 Reactor 项目基础知识和 Spring Boot 中的响应式编程

>> 加入 Pro 并下载电子书

电子书 – Java Streams – NPI EA (分类=Java Streams)
announcement - icon

自从 Java 8 引入以来,Stream API 已成为 Java 开发的基础。 基本操作,例如迭代、过滤、映射元素序列,使用起来看似很简单。

但这些也可能被过度使用并陷入一些常见陷阱。

更好地了解 Stream 的工作方式 以及如何将其与其他语言功能结合使用,请查看我们关于 Java Streams 的指南

>> 加入 Pro 并下载电子书

电子书 – Jackson – NPI EA (分类=Jackson)
announcement - icon

用 Jackson 正确处理 JSON

下载电子书

电子书 – HTTP 客户端 – NPI EA (分类=Http 客户端)
announcement - icon

充分利用 Apache HTTP 客户端

下载电子书

电子书 – Maven – NPI EA (分类 = Maven)
announcement - icon

开始使用 Apache Maven

下载电子书

电子书 – 持久化 – NPI EA (分类=持久化)
announcement - icon

您在努力实现正确的持久化层 Spring 吗?

探索电子书

电子书 – RwS – NPI EA (分类=Spring MVC)
announcement - icon

使用 Spring 构建 REST API 吗?

下载电子书

课程 – LS – NPI EA (分类=Jackson)
announcement - icon

通过 Learn Spring 课程开始学习 Spring 和 Spring Boot

>> 学习 SPRING
课程 – RWSB – NPI EA (分类=REST)
announcement - icon

通过构建一个完整的 REST API,深入了解 Spring Boot 3 和 Spring 6,使用该框架

>> 全新的“REST With Spring Boot”

课程 – LSS – NPI EA (分类=Spring Security)
announcement - icon

是的,Spring Security 可能很复杂,从核心内的更高级功能到框架中深入的 OAuth 支持。

我将安全材料构建为 两个完整的课程 - 核心和 OAuth,以针对这些更复杂的场景进行实践。 我们探索何时以及如何使用每个功能,并 在后台项目中对其进行编码

您可以在这里探索该课程

>> 学习 Spring Security

课程 – LSD – NPI EA (标签=Spring Data JPA)
announcement - icon

Spring Data JPA 是处理 JPA 复杂性的绝佳方式,它具有 Spring Boot 的强大简洁性

通过引导式参考课程开始使用 Spring Data JPA

>> 查看课程

合作伙伴 – Moderne – NPI EA (类别=Spring Boot)
announcement - icon

使用 OpenRewrite 安全且自动地重构 Java 代码。

手动重构大型代码库既缓慢、有风险,又容易拖延。OpenRewrite 应运而生。这个用于大规模、自动化代码转换的开源框架可以帮助团队安全、一致地进行现代化改造。

每个月,OpenRewrite 的创建者和维护者 Moderne 都会举办现场、实践培训课程——一个面向初学者,一个面向经验丰富的用户。您将了解配方的运作方式、如何将其应用于项目,以及如何自信地进行代码现代化改造。

参加下一次课程,带来您的问题,并学习如何自动化通常会占用您 sprint 时间的工作。

合作伙伴 – LambdaTest – NPI EA (类别=测试)
announcement - icon

回归测试是发布流程中的重要步骤,以确保新代码不会破坏现有功能。随着代码库的不断发展,我们希望频繁运行这些测试,以便尽早发现任何问题。

确保这些测试以自动化的方式频繁运行的最佳方法当然是将其包含在 CI/CD 管道中。 这样,每次向仓库提交代码时,回归测试将自动执行。

在本教程中,我们将学习如何使用 Selenium 创建回归测试,然后使用 GitHub Actions 将它们包含在我们的管道中,在 LambdaTest 云网格上运行

>> 如何使用 GitHub Actions 运行 Selenium 回归测试

课程 – LJB – NPI EA (类别 = Core Java)
announcement - icon

通过编码方式构建 Java 的坚实、实用的基础

>> 学习 Java 基础

电子书 – Jackson – NPI (分类=Jackson)
announcement - icon

Jackson 和 JSON 在 Java 中,最终通过实践学习的方式掌握

>> 下载电子书

1. 概述

在本快速教程中,我们将研究使用 Jackson 进行 Java Map 的序列化和反序列化

我们将说明如何将 Map<String, String>Map<Object, String>Map<Object, Object> 序列化和反序列化为 JSON 格式的 String

更多阅读

Jackson – 处理 Map 和空值

如何使用 Jackson 序列化带有 null 键或 null 值的 Map。

如何使用 Jackson 序列化和反序列化枚举

如何使用 Jackson 2 将 Enum 序列化为 JSON 对象。

Jackson XML 序列化和反序列化

本简短教程展示了如何使用 Jackson 库将 Java 对象序列化为 XML 并将它们反序列化回对象。

2. Maven 配置

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.17.2</version>
</dependency>

我们可以在 这里 获取 Jackson 的最新版本。

3. 序列化

序列化将 Java 对象转换为字节流,可以根据需要进行持久化或共享。Java Map 是将一个键 Object 映射到一个值 Object 的集合,通常是最难理解需要序列化的对象。

3.1. Map<String, String> 序列化

对于一个简单的情况,让我们创建一个 Map<String, String> 并将其序列化为 JSON

Map<String, String> map = new HashMap<>();
map.put("key", "value");

ObjectMapper mapper = new ObjectMapper();
String jsonResult = mapper.writerWithDefaultPrettyPrinter()
  .writeValueAsString(map);

ObjectMapper 是 Jackson 的序列化映射器。它允许我们序列化我们的 map,并使用 String 中的 toString() 方法将其写出为格式良好的 JSON String 

{
  "key" : "value"
}

3.2. Map<Object, String> 序列化

经过一些额外的步骤,我们也可以序列化包含自定义 Java 类的 map。让我们创建一个 MyPair 类来表示一对相关的 String 对象。

注意:getter/setter 应该为 public,并且我们使用 @JsonValue 注解 toString() ,以确保 Jackson 在序列化时使用这个自定义的 toString()

public class MyPair {

    private String first;
    private String second;
    
    @Override
    @JsonValue
    public String toString() {
        return first + " and " + second;
    }
 
    // standard getter, setters, equals, hashCode, constructors
}

然后我们将通过扩展 Jackson 的 JsonSerializer 来告诉 Jackson 如何序列化 MyPair

public class MyPairSerializer extends JsonSerializer<MyPair> {

    private ObjectMapper mapper = new ObjectMapper();

    @Override
    public void serialize(MyPair value, 
      JsonGenerator gen,
      SerializerProvider serializers) 
      throws IOException, JsonProcessingException {
 
        StringWriter writer = new StringWriter();
        mapper.writeValue(writer, value);
        gen.writeFieldName(writer.toString());
    }
}

JsonSerializer,顾名思义,使用 MyPairtoString() 方法将 MyPair 序列化为 JSON。 此外,Jackson 提供了许多 Serializer 类 来满足我们的序列化需求。

接下来,我们使用 @JsonSerialize 注解将 MyPairSerializer 应用于我们的 Map<MyPair, String>。 请注意,我们只告诉 Jackson 如何序列化 MyPair ,因为它已经知道如何序列化 String:

@JsonSerialize(keyUsing = MyPairSerializer.class) 
Map<MyPair, String> map;

然后让我们测试我们的 map 序列化

map = new HashMap<>();
MyPair key = new MyPair("Abbott", "Costello");
map.put(key, "Comedy");

String jsonResult = mapper.writerWithDefaultPrettyPrinter()
  .writeValueAsString(map);

序列化的 JSON 输出是

{
  "Abbott and Costello" : "Comedy"
}

3.3. Map<Object, Object> 序列化

最复杂的情况是序列化 Map<Object, Object>,但大部分工作已经完成。 让我们为我们的 map 使用 Jackson 的 MapSerializer ,为 map 的键和值类型使用来自上一节的 MyPairSerializer

@JsonSerialize(keyUsing = MapSerializer.class)
Map<MyPair, MyPair> map;
	
@JsonSerialize(keyUsing = MyPairSerializer.class)
MyPair mapKey;

@JsonSerialize(keyUsing = MyPairSerializer.class)
MyPair mapValue;

然后让我们测试序列化我们的 Map<MyPair, MyPair>

mapKey = new MyPair("Abbott", "Costello");
mapValue = new MyPair("Comedy", "1940s");
map.put(mapKey, mapValue);

String jsonResult = mapper.writerWithDefaultPrettyPrinter()
  .writeValueAsString(map);

使用 MyPairtoString() 方法进行序列化的 JSON 输出是

{
  "Abbott and Costello" : "Comedy and 1940s"
}

3.4. @JsonKey 注解

在创建 Map 时,一个对象可以是键或值。 此外,当对象出现在 Map 中作为键与作为值时,我们可能需要不同的序列化策略。 因此,让我们学习如何使用 @JsonKey 注解来实现这一点。

让我们从定义 Fruit 类开始,该类有两个成员,分别是 varietyname

public class Fruit {
    public String variety;

    @JsonKey
    public String name;

    public Fruit(String variety, String name) {
        this.variety = variety;
        this.name = name;
    }

    @JsonValue
    public String getFullName() {
        return this.variety + " " + this.name;
    }
}

我们必须注意,每当 Fruit 对象作为 Map 的键出现时,我们希望使用它的名称进行序列化。但是,当它作为值出现时,我们希望使用它的名称和品种。

现在,让我们初始化两个 Fruit 类的实例和一个 ObjectMapper 类的实例,用于序列化目的

private static final Fruit FRUIT1 = new Fruit("Alphonso", "Mango");
private static final Fruit FRUIT2 = new Fruit("Black", "Grapes");
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

接下来,我们应该记住,在序列化一个独立实例时,将使用 @JsonValue 属性进行序列化。让我们为这两个对象验证这一点

@Test
public void givenObject_WhenSerialize_ThenUseJsonValueForSerialization() 
  throws JsonProcessingException {
    String serializedValueForFruit1 = OBJECT_MAPPER.writeValueAsString(FRUIT1);
    Assertions.assertEquals("\"Alphonso Mango\"", serializedValueForFruit1);
    String serializedValueForFruit2 = OBJECT_MAPPER.writeValueAsString(FRUIT2);
    Assertions.assertEquals("\"Black Grapes\"", serializedValueForFruit2);
}

进一步地,让我们序列化包含 Fruit 类实例作为键的 selectionByFruit Map

@Test
public void givenMapWithObjectKeys_WhenSerialize_ThenUseJsonKeyForSerialization() 
  throws JsonProcessingException {
    // Given
    Map<Fruit, String> selectionByFruit = new LinkedHashMap<>();
    selectionByFruit.put(FRUIT1, "Hagrid");
    selectionByFruit.put(FRUIT2, "Hercules");
    // When
    String serializedValue = OBJECT_MAPPER.writeValueAsString(selectionByFruit);
    // Then
    Assertions.assertEquals("{\"Mango\":\"Hagrid\",\"Grapes\":\"Hercules\"}", serializedValue);
}

如预期的那样,此序列化使用了 @JsonKey 注解。 此外,我们注意到我们使用了 LinkedHashMap 来固定其访问和序列化期间的键的顺序。

最后,让我们也看看序列化包含 Fruit 类实例作为值的 selectionByPerson Map 的结果

@Test
public void givenMapWithObjectValues_WhenSerialize_ThenUseJsonValueForSerialization() 
  throws JsonProcessingException {
    // Given
    Map<String, Fruit> selectionByPerson = new LinkedHashMap<>();
    selectionByPerson.put("Hagrid", FRUIT1);
    selectionByPerson.put("Hercules", FRUIT2);
    // When
    String serializedValue = OBJECT_MAPPER.writeValueAsString(selectionByPerson);
    // Then
    Assertions.assertEquals("{\"Hagrid\":\"Alphonso Mango\",\"Hercules\":\"Black Grapes\"}", 
      serializedValue);
}

太棒了!我们已经成功地根据对象在 Map 中作为键或值的角色切换了序列化策略。

4. 反序列化

反序列化将字节流转换为我们可以在代码中使用的 Java 对象。 在本节中,我们将 JSON 输入反序列化为不同签名的 Maps

4.1. Map<String, String> 反序列化

对于一个简单的例子,让我们获取一个 JSON 格式的输入字符串并将其转换为 Java 集合 Map<String, String>

String jsonInput = "{\"key\": \"value\"}";
TypeReference<HashMap<String, String>> typeRef 
  = new TypeReference<HashMap<String, String>>() {};
Map<String, String> map = mapper.readValue(jsonInput, typeRef);

我们使用 Jackson 的 ObjectMapper, 就像我们用于序列化一样,使用 readValue() 来处理输入。 此外,请注意我们使用 Jackson 的 TypeReference, 我们将在所有的反序列化示例中使用它来描述我们目标 Map 的类型。 这是我们的 map 的 toString() 表示形式

{key=value}

4.2. Map<Object, String> 反序列化

现在让我们更改我们的输入 JSON 和目标 TypeReference Map<MyPair, String>

String jsonInput = "{\"Abbott and Costello\" : \"Comedy\"}";

TypeReference<HashMap<MyPair, String>> typeRef 
  = new TypeReference<HashMap<MyPair, String>>() {};
Map<MyPair,String> map = mapper.readValue(jsonInput, typeRef);

我们需要为 MyPair 创建一个构造函数,该构造函数接受一个包含两个元素的 String 并将其解析为 MyPair 元素

public MyPair(String both) {
    String[] pairs = both.split("and");
    this.first = pairs[0].trim();
    this.second = pairs[1].trim();
}

我们的 Map<MyPair,String> 对象的 toString()

{Abbott and Costello=Comedy}

当我们反序列化到包含 Map 的 Java 类时,还有另一种选择:我们可以使用 Jackson 的 KeyDeserializer, Jackson 提供的众多 反序列化 类之一。 让我们使用 @JsonCreator@JsonProperty@JsonDeserialize 注解我们的 ClassWithAMap:

public class ClassWithAMap {

  @JsonProperty("map")
  @JsonDeserialize(keyUsing = MyPairDeserializer.class)
  private Map<MyPair, String> map;

  @JsonCreator
  public ClassWithAMap(Map<MyPair, String> map) {
    this.map = map;
  }
 
  // public getters/setters omitted
}

在这里,我们告诉 Jackson 反序列化 ClassWithAMap 中包含的 Map<MyPair, String>,所以我们需要扩展 KeyDeserializer 来描述如何从输入 String 反序列化 map 的键,一个 MyPair 对象

public class MyPairDeserializer extends KeyDeserializer {

  @Override
  public MyPair deserializeKey(
    String key, 
    DeserializationContext ctxt) throws IOException, 
    JsonProcessingException {
      
      return new MyPair(key);
    }
}

然后我们可以使用 readValue 测试反序列化

String jsonInput = "{\"Abbott and Costello\":\"Comedy\"}";

ClassWithAMap classWithMap = mapper.readValue(jsonInput,
  ClassWithAMap.class);

再次,我们 ClassWithAMap 的 map 的 toString() 方法为我们提供了我们期望的输出

{Abbott and Costello=Comedy}

4.3. Map<Object,Object> 反序列化

最后,让我们更改我们的输入 JSON 和目标 TypeReferenceMap<MyPair, MyPair>

String jsonInput = "{\"Abbott and Costello\" : \"Comedy and 1940s\"}";
TypeReference<HashMap<MyPair, MyPair>> typeRef 
  = new TypeReference<HashMap<MyPair, MyPair>>() {};
Map<MyPair,MyPair> map = mapper.readValue(jsonInput, typeRef);

我们的 Map<MyPair, MyPair> 对象的 toString()

{Abbott and Costello=Comedy and 1940s}

5. 结论

在本文中,我们学习了如何将 Java Maps 序列化为 JSON 格式的字符串,以及如何反序列化。

支持本文的代码可在 GitHub 上获取。 一旦你Baeldung Pro 会员 身份登录,就开始学习并在项目上进行编码。
Baeldung Pro – NPI EA (类别 = Baeldung)
announcement - icon

Baeldung Pro 具有完全无广告以及最终具有深色模式,提供干净的学习体验

>> 探索干净的 Baeldung

一旦早期采用者的席位全部用完,价格将上涨并保持在每年 33 美元。

电子书 – HTTP 客户端 – NPI EA (类别=HTTP 客户端)
announcement - icon

Apache HTTP Client 是一个非常强大的库,适用于简单和高级用例,在测试 HTTP 端点时尤其适用。 查看我们的指南,涵盖基本请求和响应处理,以及安全性、Cookie、超时等。

>> 下载电子书

电子书 – Java 并发 – NPI EA (分类=Java 并发)
announcement - icon

在应用程序中处理并发可能是一个棘手的过程,其中包含许多 潜在的陷阱。 扎实的掌握基本知识将有助于最大程度地减少这些问题。

通过我们的 Java 并发 指南开始了解多线程应用程序

>> 下载电子书

电子书 – Java Streams – NPI EA (分类=Java Streams)
announcement - icon

自从 Java 8 引入以来,Stream API 已成为 Java 开发的基础。 基本操作,例如迭代、过滤、映射元素序列,使用起来看似很简单。

但这些也可能被过度使用并陷入一些常见陷阱。

更好地了解 Stream 的工作方式 以及如何将其与其他语言功能结合使用,请查看我们关于 Java Streams 的指南

>> 加入 Pro 并下载电子书

电子书 – 持久化 – NPI EA (分类=持久化)
announcement - icon

您在努力实现正确的持久化层 Spring 吗?

探索电子书

课程 – LS – NPI EA (类别=REST)

announcement - icon

从 Spring Boot 开始,通过 Learn Spring 课程了解核心 Spring。

>> 查看课程

合作伙伴 – Moderne – NPI EA (标签=重构)
announcement - icon

现代 Java 团队行动迅速——但代码库并不总是跟上。 框架会发生变化,依赖关系会漂移,技术债务会累积,直到它开始拖慢交付速度。 OpenRewrite 就是为此而构建的:一个开源重构引擎,可在保持开发人员意图不变的同时自动化重复的代码更改。

由 Moderne 的 OpenRewrite 创建者和维护者领导的每月培训系列,将介绍实际的迁移和现代化模式。 无论您是重构配方的新手,还是准备编写自己的配方,您都将学习以安全且可扩展的方式进行重构的实用方法。

如果您曾经希望重构感觉像编写代码一样自然——并且一样快速——这是一个很好的起点

电子书 Jackson – NPI EA – 3 (类别 = Jackson)
电子书 Jackson – NPI (cat = Jackson)
© .