序列化方式二——JSON之Jackson

jackson

1、什么是Jackson

  • Jackson库,作为一款高性能的Java JSON处理工具,以其灵活的解析与生成能力,在Java开发中占据了重要地位。它简化了Java对象与JSON数据之间的转换过程,不仅操作简便,而且性能卓越。

  • Jackson库的核心构建块包括ObjectMapperJsonNodeObjectMapper作为该库的核心类,承担着Java对象与JSON格式数据相互转换的重任,其实例设计为线程安全,便于在多线程环境下共享使用,从而提高了资源利用率和程序效率。

  • JsonNode则是一个抽象基类,用于表示JSON文档中的任何节点。通过其丰富的子类体系,如ObjectNode(对应JSON对象)、ArrayNode(对应JSON数组)以及ValueNode(代表各种基本类型的值),JsonNode提供了一套灵活且强大的API来访问和操作JSON数据。这使得开发者能够轻松读取、修改和遍历JSON结构,满足各种复杂的JSON处理需求。

2、为什么选择jackson?

  1. 性能: Jackson以其出色的性能而闻名。它提供了快速的JSON序列化和反序列化能力,这对于需要处理大量数据或在高并发环境下运行的应用程序来说至关重要。Jackson通过高效的字节码生成和优化的数据结构使用,实现了快速的解析和生成JSON。

  2. 灵活性: Jackson提供了丰富的API和配置选项,允许开发者根据需要进行精细控制。你可以自定义序列化器和反序列化器,以处理复杂的数据结构或特殊的数据类型。此外,Jackson还支持多种数据绑定方式,包括直接绑定到Java对象、树模型绑定和流式API。

  3. 社区支持: Jackson拥有一个活跃的社区,这意味着当你遇到问题时,你可以很容易地找到解决方案或得到帮助。社区还贡献了大量的扩展和插件,进一步增强了Jackson的功能和灵活性。

  4. 扩展性: Jackson的模块化设计使得它易于扩展。你可以通过添加自定义的模块来扩展其功能,而无需修改核心库。这种设计使得Jackson非常适合用于大型项目,因为它可以根据项目的需求进行定制和扩展。

  5. 与其他Java框架的集成: Jackson与许多流行的Java框架和库(如Spring、Hibernate、Grails等)都有良好的集成。这意味着如果你正在使用这些框架,你可以很容易地将Jackson集成到你的项目中,并利用它来处理JSON数据。

  6. 清晰的错误消息: 当数据格式不正确或存在其他问题时,Jackson能够提供清晰的错误消息,帮助开发者快速定位问题所在。这对于调试和排错非常有帮助。

  7. 流式API: Jackson的流式API允许你以流的方式处理JSON数据,这在处理大型JSON文件或需要低内存占用的场景中非常有用。你可以边读取边处理数据,而无需一次性将整个文件加载到内存中。

  8. 文档和教程: Jackson的文档和教程非常全面,覆盖了从基础到高级的各种主题。无论你是初学者还是经验丰富的开发者,都可以找到适合自己的学习资源。

3、jackson常用的API

ObjectMapper

ObjectMapper 是 Jackson 库中的核心类,它负责 Java 对象与 JSON 数据之间的序列化和反序列化操作。通过 ObjectMapper 的实例,可以方便地将 Java 对象转换为 JSON 字符串,以及将 JSON 字符串解析回 Java 对象。其主要方法包括:

  • writeValueAsString(Object): 将 Java 对象序列化为 JSON 格式的字符串。

  • readValue(String, Class<T>): 将 JSON 字符串反序列化为指定类型的 Java 对象。

JsonParser

JsonParser 是用于从 JSON 数据源(如文件、输入流、字符串等)中解析 JSON 数据的低级 API。它提供了逐项(令牌级)读取 JSON 内容的能力。主要方法包括:

  • nextToken(): 读取并返回下一个 JSON 令牌(如开始对象、字段名、值等)。

  • getValueAsString(): 尝试将当前令牌的值作为字符串返回。

  • getValueAsInt(): 尝试将当前令牌的值作为整数返回。

注意:JsonParser 主要用于需要细粒度控制解析过程的场景,如自定义解析逻辑或处理大型 JSON 数据流时。

JsonGenerator

JsonGenerator 是用于构建 JSON 数据的低级 API,它允许开发者将数据写入到不同的数据源(如文件、输出流、字符串缓冲区等)。主要方法包括:

  • writeStartObject(): 写入一个开始对象标记 {

  • writeFieldName(String): 写入一个字段名称,后面通常跟随该字段的值。

  • writeString(String): 写入一个字符串值。

  • writeEndObject(): 写入一个结束对象标记 }

JsonGeneratorJsonParser 相反,它用于生成 JSON 数据,适用于需要构建 JSON 响应或日志等场景。

JsonNode

JsonNode 是 Jackson 库中用于表示 JSON 树模型中的任意节点的抽象基类。它提供了一套丰富的 API 来访问和操作 JSON 数据。主要方法包括:

  • get(String fieldName): 获取指定字段名的子节点。如果字段不存在,则可能返回 null 或特殊的缺失节点。

  • path(String fieldName): 类似于 get,但如果不存在指定字段,则返回一个表示缺失的 JsonNode 实例,而不是 null

  • isObject(): 检查当前节点是否是一个 JSON 对象。

  • isArray(): 检查当前节点是否是一个 JSON 数组。

注解

Jackson 提供了一系列注解来配置序列化和反序列化过程,使得开发者能够更精细地控制这一过程。一些常用的注解包括:

  • @JsonProperty: 用于指定 Java 字段在 JSON 数据中对应的属性名。

  • @JsonIgnore: 标记在字段或方法上,表示在序列化和反序列化过程中忽略该字段。

  • @JsonCreator: 用于指定构造函数或工厂方法作为反序列化的入口点。

  • @JsonSerialize: 用于指定自定义的序列化器来处理特定字段或类的序列化。

  • @JsonDeserialize: 用于指定自定义的反序列化器来处理特定字段或类的反序列化。

4、使用

4.1、引入依赖

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

4.2、基础类型JSON序列化

4.2.1、对象序列化

通过ObjectMapper类的writeValueAsString方法。该方法直接接受一个Java对象作为输入参数,并返回一个包含该对象序列化后数据的JSON格式字符串。

package com.zhz.test.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Student implements Serializable {

    private String name;
    private String sex;
    private int age;
    private Date birthday;
}

/**
 * 对象序列化
 */
@Test
public void testObjectSerialize() throws JsonProcessingException {
    Student student = new Student("张三", "男", 18,new Date());
    ObjectMapper mapper = new ObjectMapper();
    System.out.println(mapper.writeValueAsString(student));
}

4.2.2、集合序列化

Jackson库不仅擅长于将单个Java对象序列化为JSON字符串,它还强大地支持将Java集合(如List、Set、Map等)转换为JSON格式的字符串。通过ObjectMapper类的writeValueAsString方法,开发者可以轻松地将任意Java集合序列化为其JSON表示形式。这种能力在处理复杂数据结构时尤为重要,因为它允许开发者以统一的方式处理单个对象和集合对象,简化了代码并提高了可维护性。

package com.zhz.test.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Student implements Serializable {

    private String name;
    private String sex;
    private int age;
    private Date birthday;
}
@Test
public void testListSerialize() {
    Student student = new Student("张三", "男", 18, new Date());
    ObjectMapper mapper = new ObjectMapper();
    try {
        System.out.println(mapper.writeValueAsString(Lists.newArrayList(student)));
    } catch (JsonProcessingException e) {
        throw new RuntimeException(e);
    }
}

image.png

4.2.3、序列化枚举类型

在Java中,枚举类型(enum)作为一种特殊的类类型,广泛用于表示一组固定的常量值。当需要将这些枚举值作为JSON数据的一部分进行交换时,使用像Jackson这样的JSON处理库进行序列化和反序列化变得尤为重要。

在Java中,枚举(enum)作为表示固定值集合的首选方式,经常与Jackson库结合使用,以实现这些枚举值到JSON字符串的高效序列化和反序列化。这种结合确保了数据交换的灵活性和准确性,特别是在需要与外部系统或API进行交互时。

package com.zhz.test.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Student implements Serializable {

    private String name;
    private Gender sex;
    private int age;
    private Date birthday;
}

package com.zhz.test.entity;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;

public enum Gender {
    MALE("男"),
    FEMALE("女");

    private final String description;


    Gender(String description) {
        this.description = description;
    }

    @JsonValue
    public String getDescription() {
        return description;
    }


    @JsonCreator
    public static Gender fromDescription(String description) {
        for (Gender gender : Gender.values()) {
            if (gender.getDescription().equals(description)) {
                return gender;
            }
        }
        throw new IllegalArgumentException("Unknown gender: " + description);
    }
}
@Test
public void testEnumSerialize() {
    Student student = new Student("张三", Gender.MALE, 18, new Date());
    ObjectMapper mapper = new ObjectMapper();
    try {
        System.out.println(mapper.writeValueAsString(Lists.newArrayList(student)));
    } catch (JsonProcessingException e) {
        throw new RuntimeException(e);
    }
}

image.png

4.2.4、序列化Java时间类型

在Java开发中,处理时间类型的序列化和反序列化是常见需求,特别是在需要将数据以JSON格式传输或存储时。Java 8引入了新的时间日期API(如java.time.LocalDateTime),为处理时间提供了更为强大和灵活的方式。而Jackson库,作为广泛使用的JSON处理库,提供了对Java 8时间类型良好的支持,使得将这些类型序列化为JSON字符串变得简单直接。

采用jackson原生的时间转换器

package com.zhz.test.entity;

import lombok.AllArgsConstructor;
import lombok.Data;

import java.time.LocalDateTime;

@Data
@AllArgsConstructor
public class Event {
    private String name;
    private LocalDateTime eventTime;

}

测试方法

@Test
public void testDateSerialize() throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new JavaTimeModule());

    // 创建Event对象
    Event event = new Event("技术分享会", LocalDateTime.now());

    // 将Event对象序列化为JSON字符串
    String jsonString = mapper.writeValueAsString(event);

    // 输出JSON字符串
    System.out.println(jsonString);
}

结果如下

image.png

我们会发现他的时间并不是我们想要的,我们想要的时间是如下面类型的:2024-09-21 19:01:50,要想达到这种效果,只需要在对应的属性中添加如下配置

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = “yyyy-MM-dd HH:mm:ss”, timezone = “Asia/Shanghai”)

具体的属性配置如下

package com.zhz.test.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;

import java.time.LocalDateTime;

@Data
@AllArgsConstructor
public class Event {
    private String name;
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
    private LocalDateTime eventTime;

}

然后我们再去跑测试用例,即可

image.png

4.3、基础类型JSON反序列化

4.3.1、反序列化单个对象

借助Jackson库的ObjectMapper类,通过调用其readValue方法,可以轻松实现将JSON字符串直接转换为Java对象的过程。此方法需要两个关键参数:一是待转换的JSON字符串,二是目标Java类的Class对象,用于指导反序列化过程。

具体demo如下

package com.zhz.test.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class StudentDeserialize {
    private String name;
    private String  sex;
    private int age;
    private Date birthday;
}

 /**
     * 基础类型反序列化
     */
    @Test
    public void testSingleDeserialize() throws JsonProcessingException {
        String jsonString = "{\"name\":\"张三\",\"sex\":\"男\",\"age\":18,\"birthday\":1726917061043}";
        ObjectMapper mapper = new ObjectMapper();
        try {
            StudentDeserialize students = mapper.readValue(jsonString, StudentDeserialize.class);
            System.out.println(students);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

image.png

4.3.2、反序列化集合对象

通过ObjectMapper类的readValue方法,灵活支持将JSON字符串反序列化为多种Java集合类型,包括但不限于ListSetMap。这一过程自动解析JSON数组或对象结构,并将其映射到对应的Java集合实例中,每个集合元素根据JSON数据的结构和提供的类型信息(泛型参数)进行转换。

测试DEMO

package com.zhz.test.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class StudentDeserialize {
    private String name;
    private String sex;
    private int age;
    private Date birthday;
}


@Test
public void testListDeserialize() {
    String jsonString = "[{\"name\":\"张三\",\"sex\":\"男\",\"age\":18,\"birthday\":1726917061043}]";
    ObjectMapper mapper = new ObjectMapper();
    try {
        System.out.println(mapper.readValue(jsonString, new TypeReference<List<Student>>() {
        }));
    } catch (JsonProcessingException e) {
        throw new RuntimeException(e);
    }
}

image.png

4.3.3、反序列化枚举类型

反序列化枚举类型(enum)是一个相对直接的过程,因为Jackson能够自动识别并处理Java枚举。当你尝试将一个包含枚举值的JSON字符串反序列化为Java对象时,只要该对象中的枚举字段与JSON中的字符串值相匹配(即枚举的name()方法返回的字符串),Jackson就会自动将其转换为相应的枚举实例。

package com.zhz.test.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class StudentDeserialize {
    private String name;
    private Gender  sex;
    private int age;
    private Date birthday;
}

package com.zhz.test.entity;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;

public enum Gender {
    MALE("男"),
    FEMALE("女");

    private final String description;


    Gender(String description) {
        this.description = description;
    }

    @JsonValue
    public String getDescription() {
        return description;
    }


    @JsonCreator
    public static Gender fromDescription(String description) {
        for (Gender gender : Gender.values()) {
            if (gender.getDescription().equals(description)) {
                return gender;
            }
        }
        throw new IllegalArgumentException("Unknown gender: " + description);
    }
}
@Test
public void testEnumDeserialize() {
    String jsonString = "{\"name\":\"张三\",\"sex\":\"男\",\"age\":18,\"birthday\":1726917061043}";
    ObjectMapper mapper = new ObjectMapper();
    try {
        System.out.println( mapper.readValue(jsonString, StudentDeserialize.class));
    } catch (JsonProcessingException e) {
        throw new RuntimeException(e);
    }
}

4.3.4、反序列化Java时间类型

在Jackson库中,反序列化Java时间类型(如java.time.LocalDateTimejava.time.LocalDatejava.time.LocalTime等,这些都是Java 8引入的新时间日期API的一部分)需要额外的配置,因为Jackson的默认配置可能不直接支持这些类型。但是,通过添加jackson-datatype-jsr310模块,你可以轻松地实现这些时间类型的反序列化。

以下是一个反序列化Java时间类型(以java.time.LocalDateTime为例)的示例:

首先,确保你的项目中包含了jackson-databindjackson-datatype-jsr310的依赖。对于Maven项目,pom.xml文件中的依赖项可能如下所示:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.17.2</version>
</dependency>

测试demo

package com.zhz.test.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Event {
    private String name;
    private LocalDateTime eventTime;

}
@Test
public void testDateDeserialize() throws JsonProcessingException {
    String jsonString = "{\"name\":\"技术分享会\",\"eventTime\":\"2024-09-22 00:37:27\"}";
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new JavaTimeModule());
    System.out.println(mapper.readValue(jsonString, Event.class));
}

image.png

4.4、自定义序列化和反序列化

4.4.1、自定义序列化

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

public class User {
    private String name;

    @JsonSerialize(using = CustomEmailSerializer.class)
    private String email;
}
package com.zhz.test.json.jackson;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;

public class CustomEmailSerializer extends JsonSerializer<String> {
    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        if (value != null) {
            gen.writeString("mailto:" + value);
        }
    }
}
    @Test
    public void testObjectDeserialize() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();

        User user = new User("John Doe", "john.doe@example.com");

        String json = mapper.writeValueAsString(user);
        System.out.println(json); 

image.png

4.4.2、自定义反序列化

package com.zhz.test.entity;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.zhz.test.json.jackson.CustomEmailDeserializer;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String name;

    @JsonDeserialize(using = CustomEmailDeserializer.class)
    private String email;
}
package com.zhz.test.json.jackson;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;

public class CustomEmailDeserializer extends JsonDeserializer<String> {
    @Override
    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        String value = p.getText().trim();
        if (value.startsWith("john.")) {
            return value.substring(7);
        }
        return value;
    }
}
@Test
public void testObjectDeserialize() throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    User user = new User("John Doe", "john.doe@example.com");

    String json = mapper.writeValueAsString(user);
    System.out.println(json);
    User deserializedUser = mapper.readValue(json, User.class);
    System.out.println(deserializedUser.getEmail());
}

image.png

5、常用注解用途和使用方法

5.1、@JsonProperty

  • 用于指定Java类的属性在JSON中的字段名。如果不使用此注解,Jackson通常会使用Java字段名(或getter/setter方法的名称去掉get/set和首字母小写后的名称)作为JSON中的字段名。

示例

@JsonProperty(“user_name”)

private String userName;

5.2、@JsonFormat

  • 用于定制日期/时间、数字等类型的格式。你可以指定日期模式、时区等。

示例:

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = “yyyy-MM-dd HH:mm:ss”) private Date creationDate;

5.3、@JsonIgnore

  • 用于忽略Java类的某个属性,使其在序列化到JSON时不会被包含,或在从JSON反序列化到Java对象时不会被设置。

  • 示例:

@JsonIgnore private String password;

5.4、@JsonInclude

  • 用于控制属性的包含逻辑,例如只包含非null的属性、非空集合等。

  • 示例:

@JsonInclude(JsonInclude.Include.NON_NULL)

public class User {...}

5.5、@JsonTypeInfo

  • 用于在JSON中包含类型信息,这对于多态序列化和反序列化非常有用。

  • 示例:

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "classType")

  1. @JsonTypeInfo.As@JsonTypeInfo.Id:

    • 这两个注解通常与@JsonTypeInfo一起使用,以指定类型信息的包含方式和标识符类型。@JsonTypeInfo.As定义了类型信息如何被包含在JSON中(例如,作为属性、外部包装器等),而@JsonTypeInfo.Id定义了类型信息的标识符类型(例如,使用类的完全限定名、最小类名等)。

5.6、 @JsonCreator

  • 用于指定用于反序列化的构造函数或工厂方法。这对于没有无参构造函数的类特别有用。

  • 示例:

@JsonCreator

public MyClass(@JsonProperty("name") String name) {...}

5.7、 @JsonSetter

  • 用于自定义反序列化时设置字段值的方法。如果字段是私有的,并且你不希望使用标准的setter方法(例如,因为字段名在JSON中不同),则此注解很有用。

  • 示例:

@JsonSetter("user_name")

public void setUserName(String userName) {...}

5.8、 @JsonGetter

  • 用于自定义序列化时获取字段值的方法。与@JsonSetter相反,它用于控制序列化过程。

  • 示例:

@JsonGetter("user_name")

public String getUserName() {...}

5.9、 @JsonAnySetter

  • 用于处理JSON中未明确映射到Java类属性的额外字段。这允许你的类在解析JSON时具有更大的灵活性。

  • 示例:

@JsonAnySetter

public void setAdditionalProperty(String name, Object value) {...}

5.10、 @JsonAnyGetter

  • @JsonAnySetter相对应,它用于在序列化时包含额外的属性。这允许你以灵活的方式在JSON中包含未在Java类中明确声明的字段。

  • 示例:

@JsonAnyGetter

public Map<String, Object> getAdditionalProperties() {...}

5.11、@JsonAutoDetect

  • 用于定制Jackson在序列化时自动检测哪些字段、getter/setter方法或创建者(constructors)应该被包括。你可以通过调整可见性(如只包括public字段)或指定哪些具体的getter/setter方法应该被序列化来精细控制这一过程。

  • 示例:

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)

5.12、JsonIgnoreProperties

  • 用于在类级别上忽略一个或多个属性。这对于序列化时排除不需要的属性或在反序列化时忽略JSON中的未知属性特别有用。

  • 示例:

@JsonIgnoreProperties(value = {"internalId", "temporaryData"}, ignoreUnknown = true)

5.13、@JsonIdentityInfo

  • 用于处理循环引用或重复引用的情况。它通过在JSON中包含对象的唯一标识符来避免无限循环或重复数据。

  • 示例:

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")

5.14、@JsonSerialize 和 @JsonDeserialize

  • @JsonSerialize用于指定自定义的序列化器,而@JsonDeserialize用于指定自定义的反序列化器。这使得开发者可以对序列化和反序列化过程进行完全的控制。(样例参考上方demo)

1、@JsonSerialize.typing@JsonDeserialize.typing:

  • 这两个注解允许在序列化和反序列化时指定类型处理的行为。它们通常用于处理多态类型,但与@JsonTypeInfo不同,它们提供了更细粒度的控制。

  • 注意:这些注解可能不是Jackson核心库直接提供的,而是作为扩展或第三方库的一部分存在。

2、@JsonDeserialize.keyUsing@JsonSerialize.keyUsing:

  • 当处理Map类型时,这些注解允许自定义Map键的序列化和反序列化方式。这在键的类型不是简单类型(如String)时特别有用。

  • 示例:

@JsonDeserialize(keyUsing = CustomKeyDeserializer.class)

private Map<CustomKey, Value> map;

3、@JsonDeserialize.contentUsing@JsonSerialize.contentUsing:

  • 类似于keyUsing,这些注解允许自定义Map值或集合元素的序列化和反序列化方式。

  • 示例:

@JsonDeserialize(contentUsing = CustomValueDeserializer.class)

private List<CustomValue> list;

4、@JsonSerialize.using@JsonDeserialize.using:

  • 这两个注解之前已经提到过,但再次强调它们的重要性。它们允许开发者为特定的字段或类指定自定义的序列化和反序列化器。

5.15、@JsonView

  • 用于在序列化时基于视图(View)来包含或排除属性。这允许开发者根据不同的场景或需求来定制输出的JSON结构。

  • 示例:定义视图接口,并在序列化时通过ObjectMapper指定视图。

5.16、@JsonRawValue

  • 用于指示Jackson在序列化时将字段的值作为原始JSON字符串处理,而不是先将其序列化为JSON。这在字段值已经是一个JSON字符串时非常有用。

  • 示例:

@JsonRawValue

private String rawJsonField;

5.17、@JsonAlias

  • 用于在反序列化时指定一个或多个别名给属性。这允许JSON中的字段名与Java类中的字段名不同,但仍然能够正确映射。

  • 示例:

@JsonAlias({"alias1", "alias2"})

private String realFieldName;

5.18、@JsonUnwrapped

  • 用于在序列化时将嵌套对象的属性“展平”到包含它的JSON对象中。这对于简化JSON结构非常有用。

  • 示例:

@JsonUnwrapped

private Address address;

5.19、@JsonManagedReference 和 @JsonBackReference

  • 这两个注解一起使用来处理对象间的双向引用,以避免在序列化时产生循环引用。@JsonManagedReference标记“向前”的引用,而@JsonBackReference标记“向后”的引用。Jackson在序列化时会忽略@JsonBackReference标记的引用。

  • 示例:在父子关系中,父类使用@JsonManagedReference,子类使用@JsonBackReference

5.20、@JsonNaming

  • 用于指定一个命名策略,该策略将应用于类的所有属性。这允许开发者以统一的方式命名JSON字段,例如将所有字段名转换为驼峰式(CamelCase)或下划线分隔式(snake_case)。

  • 示例:

@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)

public class MyClass {...}

5.21、@JsonFilter

  • 用于在序列化或反序列化时应用过滤器。这允许开发者根据某些条件包含或排除属性。

  • 示例:

@JsonFilter("myFilter")

public class MyClass {...}

然后在配置ObjectMapper时设置相应的过滤逻辑。

5.22、JsonRootName

  • 用于在序列化时指定根元素的名称。这在生成具有单个根对象的JSON时非常有用,尤其是在与某些期望特定JSON结构的API交互时。

  • 示例:

@JsonRootName("user")

public class User {...}

6、工具类

package com.star.common.core.utils;

import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.star.common.core.contract.exception.JsonConversionException;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * json工具类(jackson)
 *
 * @author zhouhengzhe
 */
@Slf4j
@UtilityClass
public class JsonKit {

    private static final String DATE_PATTERN = "yyyy-MM-dd";
    private static final String TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
    public static final ObjectMapper DEFAULT_OBJECT_MAPPER = defaultObjectMapper();

    /**
     * 默认的ObjectMapper配置
     * <p>
     * defaultObjectMapper方法创建并配置了一个ObjectMapper实例,用于处理JSON数据的序列化和反序列化。它禁用了未知属性的反序列化失败,设置了日期格式,注册了Java 8时间模块,并设置了时区。
     *
     * @return
     */
    public ObjectMapper defaultObjectMapper() {
        // 创建一个ObjectMapper实例
        ObjectMapper objectMapper = new ObjectMapper();
        // 禁用未知属性的反序列化失败
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        // 设置日期格式
        objectMapper.setDateFormat(new BaseSimpleDateFormat());
        // 创建一个JavaTimeModule实例
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        // 为LocalDate类添加序列化器和反序列化器
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_PATTERN)));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DATE_PATTERN)));
        // 为LocalDateTime类添加序列化器和反序列化器
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(TIME_PATTERN)));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(TIME_PATTERN)));
        // 注册JavaTimeModule到ObjectMapper中
        objectMapper.registerModule(javaTimeModule);
        // 设置时区为上海时区
        objectMapper.setTimeZone(TimeZone.getTimeZone(ZoneId.of("Asia/Shanghai")));
        // 设置序列化时只包含非null的属性
        objectMapper.setSerializationInclusion(Include.NON_NULL);
        // 返回配置好的ObjectMapper实例
        return objectMapper;
    }

    /**
     * Redis专用的ObjectMapper配置
     * <p>
     * redisObjectMapper方法创建了一个专用于Redis的ObjectMapper实例,它启用了所有属性的可见性,激活了默认的类型信息,并配置了一些其他特性。
     * <p>
     * 注意:如果需要在序列化过程中返回异常错误,请不要使用此方法
     *
     * @return 配置好的ObjectMapper实例
     */
    public ObjectMapper redisObjectMapper() {
        // 调用defaultObjectMapper方法获取默认的ObjectMapper实例
        ObjectMapper objectMapper = defaultObjectMapper();
        // 设置所有属性的可见性为任意
        objectMapper.setVisibility(PropertyAccessor.ALL, Visibility.ANY);
        // 激活默认的类型信息,并设置为非final,使用包装数组形式
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, DefaultTyping.NON_FINAL, As.WRAPPER_ARRAY);
        // 关闭在反序列化时遇到未知属性的异常抛出
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 关闭将getter方法作为setter方法使用
        objectMapper.configure(MapperFeature.USE_GETTERS_AS_SETTERS, false);
        // 设置序列化时只包含非空的属性
        objectMapper.setSerializationInclusion(Include.NON_EMPTY);
        // 返回配置好的ObjectMapper实例
        return objectMapper;
    }

    /**
     * 自定义的ObjectMapper配置
     * <p>
     * buildObjectMapper方法允许用户自定义日期和时间格式,并创建一个ObjectMapper实例。
     *
     * @param datePattern 日期格式
     * @param timePattern 时间格式
     * @return ObjectMapper实例
     */

    public ObjectMapper buildObjectMapper(String datePattern, String timePattern) {
        ObjectMapper objectMapper = new ObjectMapper();
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        // 添加LocalDate类型的序列化和反序列化器
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(datePattern)));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(datePattern)));
        // 添加LocalDateTime类型的序列化和反序列化器
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(timePattern)));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(timePattern)));

        // 添加Date类型的自定义序列化和反序列化器,使用ThreadLocal来确保线程安全
        ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat(datePattern));

        // 添加Date类型的自定义序列化和反序列化器
        javaTimeModule.addSerializer(Date.class, new JsonSerializer<>() {
            @Override
            public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                SimpleDateFormat formatter = dateFormatThreadLocal.get();
                String formattedDate = formatter.format(date);
                jsonGenerator.writeString(formattedDate);
            }
        });
        javaTimeModule.addDeserializer(Date.class, new JsonDeserializer<Date>() {
            @Override
            public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
                SimpleDateFormat format = dateFormatThreadLocal.get();
                String date = jsonParser.getText();
                try {
                    return format.parse(date);
                } catch (ParseException e) {
                    // 更好的异常处理,保留原始异常信息
                    throw new RuntimeException(e);
                }
            }
        });
        // 注册JavaTimeModule
        objectMapper.registerModule(javaTimeModule);
        // Long类型返回前端转为String类型,防止过长展示出错
        // 以下代码块已被注释,可根据需要取消注释
        // SimpleModule simpleModule = new SimpleModule();
        // simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        // simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        // objectMapper.registerModule(simpleModule);
        // 允许接受空字符串作为null对象
        objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
        // 关闭反序列化时遇到未知属性的异常抛出
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        return objectMapper;
    }


    /**
     * 将给定的对象转换成JSON格式的字符串
     *
     * @param object 待转换的对象,如果为null则直接返回null
     * @return 转换后的JSON字符串
     * @throws JsonConversionException 如果转换失败则抛出此异常
     */
    public String toJson(Object object) {
        // 如果对象为空,则直接返回null
        if (object == null) {
            return null;
        }
        // 如果对象是String类型的实例,则直接返回该对象(因为字符串本身就是有效的JSON)
        if (object instanceof String) {
            return (String) object;
        }
        try {
            // 尝试将对象转换为JSON格式的字符串
            return DEFAULT_OBJECT_MAPPER.writeValueAsString(object);
        } catch (IOException e) {
            // 如果转换过程中发生异常,则记录错误日志并抛出异常
            log.error("write to json string error:{}", object.getClass().getName(), e);
            throw new JsonConversionException(e);
        }
    }

    /**
     * 将json字符串转换为Map对象
     *
     * @param json 待转换的json字符串
     * @return 转换后的Map对象
     */
    public Map<String, Object> toMap(String json) {
        Map<String, Object> map = Maps.newHashMap();
        try {
            // 解析json字符串为JsonNode对象
            JsonNode rootNode = DEFAULT_OBJECT_MAPPER.readTree(json);
            // 获取JsonNode对象的所有字段名
            Iterator<String> fieldNames = rootNode.fieldNames();
            // 遍历所有字段名
            while (fieldNames.hasNext()) {
                String fieldName = fieldNames.next();
                // 获取字段名对应的JsonNode对象
                JsonNode jsonNode = rootNode.get(fieldName);
                // 如果JsonNode是值节点
                if (jsonNode.isValueNode()) {
                    // 将值节点的值转换为Object类型并添加到map中
                    map.put(fieldName, getNodeValue(jsonNode));
                    // 如果JsonNode是对象节点
                } else if (jsonNode.isObject()) {
                    // 递归调用toMap方法将对象节点转换为Map并添加到map中
                    map.put(fieldName, toMap(jsonNode.toString()));
                    // 如果JsonNode是数组节点
                } else if (jsonNode.isArray()) {
                    // 创建一个列表用于存储数组节点的值
                    List<Object> list = Lists.newArrayList();
                    // 遍历数组节点的每个子节点
                    for (JsonNode childNode : jsonNode) {
                        // 如果子节点是值节点
                        if (childNode.isValueNode()) {
                            // 将子节点的值转换为Object类型并添加到列表中
                            list.add(getNodeValue(childNode));
                            // 如果子节点是对象节点
                        } else if (childNode.isObject()) {
                            // 递归调用toMap方法将对象节点转换为Map并添加到列表中
                            list.add(toMap(childNode.toString()));
                        }
                    }
                    // 将列表添加到map中
                    map.put(fieldName, list);
                }
            }
            return map;
        } catch (JsonProcessingException e) {
            // 解析json字符串出错时打印错误日志
            log.error("parse json to map error:{}", json, e);
            throw new JsonConversionException(e);
        }
    }

    /**
     * 将json字符串转换为Map列表
     *
     * @param json 待转换的json字符串
     * @return 转换后的Map列表,如果转换失败则返回null
     */
    public List<Map<String, Object>> toMapList(String json) {
        try {
            List<Map<String, Object>> list = Lists.newArrayList();
            // 将json字符串解析为JsonNode对象
            JsonNode rootNode = DEFAULT_OBJECT_MAPPER.readTree(json);
            // 遍历JsonNode对象的子节点
            for (JsonNode childNode : rootNode) {
                // 如果子节点是对象节点
                if (childNode.isObject()) {
                    // 将子节点的字符串形式转换为Map对象,并添加到列表中
                    list.add(toMap(childNode.toString()));
                }
            }
            // 返回转换后的Map列表
            return list;
        } catch (JsonProcessingException e) {
            // 解析json字符串转换为Map列表时发生异常,记录错误日志
            log.error("parse json to map error:{}", json, e);
            throw new JsonConversionException(e);
        }
    }

    /**
     * 将对象转换成格式化的JSON字符串
     *
     * @param object 要转换的对象
     * @return 转换后的格式化JSON字符串
     */
    public String toJsonWithDefaultPrettyPrinter(Object object) {
        try {
            // 使用默认的格式化器将对象转换为JSON字符串
            return DEFAULT_OBJECT_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(object);
        } catch (IOException e) {
            // 如果转换过程中发生IO异常,则记录错误日志并返回空字符串
            log.error("write to json string error:{}", object, e);
            throw new JsonConversionException(e);
        }
    }

    /**
     * 将给定的对象转换为指定类型的对象
     *
     * @param object 要转换的对象
     * @param clazz  转换后对象的类型
     * @param <T>    转换后对象的类型
     * @return 转换后的对象
     */
    public <T> T toObject(Object object, Class<T> clazz) {
        if (object == null) {
            return null;
        }
        // 兼容Java 16之前的版本,并避免直接转型可能引发的ClassCastException
        if (!(object instanceof String json)) {
            if (clazz.isInstance(object)) {
                return clazz.cast(object);
            }
            throw new IllegalArgumentException("Object type does not match the specified class.");
        }
        if (json.isEmpty()) {
            return null;
        }
        // 检查DEFAULT_OBJECT_MAPPER是否为null
        if (DEFAULT_OBJECT_MAPPER == null) {
            throw new IllegalStateException("DEFAULT_OBJECT_MAPPER is null.");
        }
        try {
            return DEFAULT_OBJECT_MAPPER.readValue(json, clazz);
        } catch (IOException e) {
            log.error("Parse json string error: {}", json, e);
            // 重新抛出异常,以便调用者能够处理它
            throw new JsonConversionException(e);
        }
    }

    /**
     * 将给定的对象转换为指定类型的对象
     *
     * @param object   待转换的对象
     * @param javaType 转换后对象的类型
     * @param <T>      转换后对象的类型
     * @return 转换后的对象
     * @throws JsonConversionException 如果解析JSON字符串时发生异常
     */
    public <T> T toObject(Object object, JavaType javaType) {
        if (object == null) {
            return null;
        }
        String jsonString = null;
        if (!(object instanceof String)) {
            // 如果object不是String类型且不能直接转换为T,则抛出异常或进行其他处理
            throw new IllegalArgumentException("Object is not a JSON string and cannot be directly cast to type T.");
        }
        jsonString = (String) object;
        if (jsonString.isEmpty()) {
            return null;
        }
        try {
            // 确保DEFAULT_OBJECT_MAPPER已初始化
            if (DEFAULT_OBJECT_MAPPER == null) {
                throw new IllegalStateException("DEFAULT_OBJECT_MAPPER has not been initialized.");
            }
            return DEFAULT_OBJECT_MAPPER.readValue(jsonString, javaType);
        } catch (IOException e) {
            log.error("Parse JSON string error: {}", jsonString, e);
            throw new JsonConversionException(e);
        }
    }

    /**
     * 将对象转换成指定类型的列表
     *
     * @param object   待转换的对象
     * @param beanType 转换后列表中元素的类型
     * @param <T>      转换后列表中元素的类型
     * @return 转换后的列表
     */
    public <T> List<T> toList(Object object, Class<T> beanType) {
        // 如果待转换的对象为空,则返回空列表
        if (object == null) {
            return new ArrayList<>();
        }

        // 判断对象是否为字符串类型
        if (object instanceof String) {
            String jsonString = (String) object;
            // 如果jsonString为空字符串或不是有效的JSON,则返回空列表
            if (jsonString.isEmpty() || !isValidJson(jsonString)) {
                return new ArrayList<>();
            }
            // 构造参数化类型,用于指定列表中元素的类型
            JavaType javaType = DEFAULT_OBJECT_MAPPER.getTypeFactory().constructParametricType(List.class, beanType);
            try {
                // 使用ObjectMapper的readValue方法将jsonString转换为指定类型的列表
                return DEFAULT_OBJECT_MAPPER.readValue(jsonString, javaType);
            } catch (IOException e) {
                // 如果转换过程中发生异常,则记录错误日志并返回空列表
                log.error("Translate to POJO failed. jsonString={}", jsonString, e);
                return new ArrayList<>();
            }
        } else if (object instanceof List) {
            // 如果对象本身就是List类型,则进行类型转换并返回
            return (List<T>) object;
        } else {
            // 如果对象既不是字符串也不是List,则返回空列表或抛出异常(根据业务需求决定)
            log.warn("Unsupported object type for conversion to list: {}", object.getClass().getName());
            // 或者抛出异常,如 throw new IllegalArgumentException("Unsupported object type for conversion to list.");
            throw new IllegalArgumentException("Unsupported object type for conversion to list.");
        }
    }

    /**
     * 用于检查字符串是否为有效的JSON(简单实现,可能需要根据实际需求进行扩展)
     */
    private boolean isValidJson(String json) {
        try {
            DEFAULT_OBJECT_MAPPER.readTree(json);
            return true;
        } catch (IOException e) {
            log.error("Invalid JSON: {}", json, e);
            return false;
        }
    }

    /**
     * 将JSON字符串转换为POJO对象
     *
     * @param object      待转换的JSON字符串
     * @param typeReference 目标POJO对象的类型引用
     * @param <T>           目标POJO对象的类型
     * @return 转换后的POJO对象,若转换失败则返回null
     */
    public <T> T toPojo(Object object, TypeReference<T> typeReference) {
        if (object == null) {
            return null;
        }
        String json = toJson(object);
        if (json.isEmpty()) {
            return null;
        } else {
            try {
                return DEFAULT_OBJECT_MAPPER.readValue(json, typeReference);
            } catch (IOException e) {
                log.error("parse json string error:" + json, e);
                return null;
            }
        }
    }

    /**
     * 判断字符串是否为JSON格式。
     *
     * @param str 待判断的字符串
     * @return 若字符串以"{"开头且以"}"结尾则返回true,否则返回false。
     */
    public boolean isJson(String str) {
        if (str == null) {
            // 处理null值的情况,避免NullPointerException
            return false;
        }
        // 判断字符串是否以"{"开头且以"}"结尾
        return str.startsWith("{") && str.endsWith("}");
    }

    /**
     * 判断给定的字符串是否为JSON数组格式
     *
     * @param str 待判断的字符串
     * @return 如果字符串以 "[" 开头,并以 "]" 结尾,返回true;否则返回false
     */
    public boolean isJsonArray(String str) {
        // 判断字符串是否以 "[" 开头
        // 如果以 "[" 开头,则继续判断字符串是否以 "]" 结尾
        // 如果同时满足两个条件,则返回true,否则返回false
        return str != null && str.startsWith("[") && str.endsWith("]");
    }

    /**
     * 构建集合类型的JavaType对象
     *
     * @param collectionClass 集合类class
     * @param elementClass    元素类class
     * @return 集合类型的JavaType对象
     */
    public JavaType buildCollectionType(Class<? extends Collection> collectionClass, Class<?> elementClass) {
        // 使用DEFAULT_OBJECT_MAPPER的getTypeFactory()方法获取TypeFactory对象
        // 调用TypeFactory对象的constructCollectionType()方法,传入集合类class和元素类class
        // 构建并返回集合类型的JavaType对象
        return DEFAULT_OBJECT_MAPPER.getTypeFactory().constructCollectionType(collectionClass, elementClass);
    }

    /**
     * 构建指定类型的Map的JavaType对象
     *
     * @param mapClass   指定的Map类型
     * @param keyClass   Map中键的类型
     * @param valueClass Map中值的类型
     * @return 返回指定类型的Map的JavaType对象
     */
    public JavaType buildMapType(Class<? extends Map> mapClass, Class<?> keyClass, Class<?> valueClass) {
        // 调用DEFAULT_OBJECT_MAPPER的getTypeFactory()方法获取TypeFactory对象
        // 调用TypeFactory对象的constructMapType()方法构建指定类型的Map的JavaType对象
        // 构造方法的参数分别为mapClass(指定的Map类型)、keyClass(Map中键的类型)和valueClass(Map中值的类型)
        return DEFAULT_OBJECT_MAPPER.getTypeFactory().constructMapType(mapClass, keyClass, valueClass);
    }

    /**
     * 使用给定的JSON字符串更新指定对象。
     *
     * @param jsonString 用于更新对象的JSON字符串
     * @param object     要更新的对象
     * @throws IOException 如果在解析JSON字符串时出现错误,则抛出IOException
     */
    public void update(String jsonString, Object object) {
        try {
            // 使用默认的对象映射器创建一个读取器,用于更新指定的对象
            DEFAULT_OBJECT_MAPPER.readerForUpdating(object).readValue(jsonString);
        } catch (IOException e) {
            // 如果在解析JSON字符串时出现错误,则记录错误日志
            log.error("update json string: {} =>to object: {} ", jsonString, object, e);
            throw new JsonConversionException(e);
        }
    }

    /**
     * 从JsonNode中获取节点值
     *
     * @param childNode 需要获取值的JsonNode对象
     * @return 返回节点值,如果节点是BigDecimal类型,则返回BigDecimal对象;
     * 如果节点是Double类型,则返回Double对象;
     * 如果节点是Float类型,则返回Float对象;
     * 如果节点是Long类型,则返回Long对象;
     * 如果节点是Int类型,则返回Integer对象;
     * 如果节点是Boolean类型,则返回Boolean对象;
     * 如果节点是文本类型,则返回文本字符串;
     * 如果节点是其他类型,则返回null。
     */
    private static Object getNodeValue(JsonNode childNode) {
        // 防止空指针异常
        if (childNode == null) {
            return null;
        }
        // 如果节点是BigDecimal类型
        if (childNode.isBigDecimal()) {
            // 返回BigDecimal对象
            return childNode.decimalValue();
        }
        // 如果节点是Double类型
        if (childNode.isDouble()) {
            // 返回Double对象
            return childNode.asDouble();
        }
        // 如果节点是Float类型
        if (childNode.isFloat()) {
            // 返回Float对象
            return childNode.floatValue();
        }
        // 如果节点是Long类型
        if (childNode.isLong()) {
            // 返回Long对象
            return childNode.asLong();
        }
        // 如果节点是Int类型
        if (childNode.isInt()) {
            // 返回Integer对象
            return childNode.asInt();
        }
        // 如果节点是Boolean类型
        if (childNode.isBoolean()) {
            // 返回Boolean对象
            return childNode.asBoolean();
        }
        // 返回文本字符串
        return childNode.asText();
    }

    private class BaseSimpleDateFormat extends SimpleDateFormat {
        /**
         * 使用默认日期时间格式"yyyy-MM-dd HH:mm:ss.SSS"初始化BaseSimpleDateFormat对象。
         */
        public BaseSimpleDateFormat() {
            super("yyyy-MM-dd HH:mm:ss.SSS");
        }

        /**
         * 使用指定的日期时间格式初始化BaseSimpleDateFormat对象。
         *
         * @param pattern 自定义的日期时间格式字符串
         */
        public BaseSimpleDateFormat(String pattern) {
            super(pattern);
        }

        /**
         * 重写父类的parse方法,用于解析字符串为日期对象。
         *
         * @param source 要解析的日期字符串,格式为yyyy-MM-dd HH:mm:ss或yyyy-MM-dd HH:mm:ss.SSS
         * @return 解析后的日期对象
         * @throws ParseException 如果解析字符串时发生错误,则抛出ParseException异常
         */
        @Override
        public Date parse(String source) throws ParseException {
            // 如果源字符串长度为19,即格式为yyyy-MM-dd HH:mm:ss
            if (source.length() == 19) {
                // 在字符串末尾添加".000",使其符合yyyy-MM-dd HH:mm:ss.SSS的格式
                source = source.concat(".000");
            }
            // 调用父类的parse方法解析修改后的字符串为日期对象,并返回
            return super.parse(source);
        }
    }
}

个人项目

可以学习到的体系

  • 项目完全从0到1开始架构,包含前端,后端,架构,服务器,技术管理相关运维知识!

    • 最佳包名设计,项目分层
  • 破冰CRUD,手撕中间件!

    • 基于MybatisPlus封装属于自己的DDD ORM框架

    • 基于Easyexcel封装属于自己的导入导出组件

    • oss对象存储脚手架(阿里云,minio,腾讯云,七牛云等)

    • 邮件脚手架

    • completefuture脚手架

    • redis脚手架

    • xxl-job脚手架

    • 短信脚手架

    • 常用工具类等

  • 传统MVC代码架构弊端的解决方案

    • DDD+CQRS+ES最难架构
  • 结合实际代码的业务场景

    • 多租户单点登录中心

    • 用户中台

    • 消息中心

    • 配置中心

    • 监控设计

  • 程序员的职业规划,人生规划

    • 打工永远没有出路!

    • 打破程序员的35岁魔咒

    • 技术带给你的优势和竞争力【启发】

    • 万物互联网的淘金之路!

技术以外的赚钱路子

可以一起沟通

具体的文章目录

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

购买链接

DDD用户中台

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhz小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值