序列化方式二——JSON之fastjson

fastjson&fastjson2(版本:1.2.83_noneautotype)

扩展点

  • Fastjson通过其丰富的扩展点显著增强了用户定制序列化和反序列化行为的灵活性,完美契合了实际开发中的多样化需求。在SpringBoot与SpringCloud的集成环境中,开发者能够利用SerializeFilter和ParserProcess精细地构建复杂的序列化与反序列化逻辑,以应对复杂的数据处理场景。同时,通过调整SerializerFeature和ParserFeature,用户可以轻松控制输出格式与特性,如日期格式、空字段处理等,确保数据交换的准确性和效率。

  • 此外,SerializeConfig与ParserConfig作为全局配置工具,进一步简化了配置过程,允许开发者在全局范围内统一设置序列化和反序列化的规则,这不仅减少了代码冗余,还极大地提升了系统的可维护性和可扩展性。这些设计使得Fastjson成为处理JSON数据时的强大工具,有效促进了开发效率与系统性能的提升。

序列化扩展点
SerializeFilter
  • SerializeFilter接口及其子接口(如SimplePropertyPreFilter、PropertyPreFilter等)为开发者提供了在序列化过程中精细控制输出内容的能力。通过实现这些接口,用户可以灵活地过滤掉不需要的字段、修改字段值、调整字段的序列化行为等,从而满足特定场景下的数据输出需求。
使用方法:
  1. 实现SerializeFilter接口或其子接口: 根据需求,选择实现SerializeFilter接口或其提供的子接口(如SimplePropertyPreFilter用于简单的属性预过滤,PropertyPreFilter提供基于属性名称和值的过滤逻辑等)。在实现的类中,通过覆盖相关方法来自定义序列化逻辑。

  2. 创建SerializeFilter实例: 实例化步骤1中创建的自定义SerializeFilter类,准备将其应用于序列化过程。

  3. 在序列化时传入自定义的SerializeFilter实例: 当调用JSON.toJSONString()等序列化方法时,利用这些方法的重载版本,将自定义的SerializeFilter实例作为参数传入。这样,在序列化过程中就会应用到你定义的过滤和修改逻辑,从而按需输出JSON字符串。

示例简化:
package com.zhz.test.entity;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Student implements Serializable {private String name;private String sex;private int age;
}
 @Testpublic void testFastjson2() {Student student = new Student("小明", "男", 18, new Date());//序列化成JSON格式的字符串String jsonStr = JSON.toJSONString(student, new SimplePropertyPreFilter() {@Overridepublic boolean apply(JSONSerializer serializer, Object source, String name) {// 定制序列化逻辑// 返回true表示保留该字段,false表示过滤return !"name".equals(name);}});System.out.println(jsonStr);//反序列化成对象Student student1 = JSON.parseObject(jsonStr, Student.class);System.out.println(student1);}

image.png

我们可以发现name属性已过滤。

SerializerFeature(日期格式化请看此)
  • SerializerFeature 枚举提供了丰富的选项,用于在序列化过程中精细控制输出的格式和特性。通过这些特性,开发者可以定制JSON字符串的外观,比如格式化输出、忽略空值字段、输出日期格式等,以满足不同的数据交换和展示需求。
使用方法:
  • 在调用序列化方法(如JSON.toJSONString)时,可以通过其重载版本传入一个或多个SerializerFeature枚举值作为参数,以此来指定所需的序列化特性。这些枚举值可以通过逻辑运算符(如|)组合使用,以同时启用多个特性。
解析SerializerFeature的属性:
  1. QuoteFieldNames:

    • 字段名使用双引号括起来,符合 JSON 标准。
  2. UseSingleQuotes:

    • 使用单引号而不是双引号来括起字段名和字符串值。这不是 JSON 标准,但在某些情况下可能更节省空间或符合特定需求。
  3. WriteMapNullValue:

    • 写入值为 null 的字段。默认情况下,值为 null 的字段不会被序列化。
  4. WriteEnumUsingToString:

    • 枚举类型使用 toString() 方法的返回值进行序列化,而不是使用枚举的名字。
  5. WriteEnumUsingName:

    • 枚举类型使用其名字(即 name() 方法的返回值)进行序列化。
  6. UseISO8601DateFormat:

    • 使用 ISO8601 标准的日期格式来序列化日期对象。
  7. WriteNullListAsEmpty:

    • null 值的 List 字段序列化为空数组 []
  8. WriteNullStringAsEmpty:

    • null 值的字符串字段序列化为空字符串 ""
  9. WriteNullNumberAsZero:

    • null 值的数字字段序列化为 0
  10. WriteNullBooleanAsFalse:

    • null 值的布尔字段序列化为 false
  11. SkipTransientField:

    • 跳过 transient 修饰的字段,不进行序列化。
  12. SortField:

    • 对字段进行排序后再序列化。
  13. WriteTabAsSpecial(已弃用):

    • 特殊处理 Tab 字符,通常不建议使用。
  14. PrettyFormat:

    • 格式化输出,使 JSON 字符串更易于阅读,包含缩进和换行。
  15. WriteClassName:

    • 写入类名信息,便于反序列化时恢复对象的实际类型。
  16. DisableCircularReferenceDetect:

    • 禁用循环引用检测,避免因为循环引用导致的无限递归。
  17. WriteSlashAsSpecial:

    • 对斜杠 / 进行特殊处理,通常用于确保生成的 JSON 可以作为 URL 的一部分而不被破坏。
  18. BrowserCompatible:

    • 浏览器兼容模式,处理一些与浏览器相关的特殊字符。
  19. WriteDateUseDateFormat:

    • 使用自定义的日期格式来序列化日期对象。
  20. NotWriteRootClassName:

    • 不写入根对象的类名,即使启用了 WriteClassName
  21. DisableCheckSpecialChar(已弃用):

    • 禁用特殊字符检查,通常不建议使用以避免潜在的安全问题。
  22. BeanToArray:

    • 将 Java Bean 序列化为数组形式,而不是默认的键值对形式。
  23. WriteNonStringKeyAsString:

    • 将非字符串类型的 Key 也序列化为字符串。
  24. NotWriteDefaultValue:

    • 不写入默认值,即如果字段的值等于其类型的默认值(如数字为 0,布尔为 false),则不进行序列化。
  25. BrowserSecure:

    • 浏览器安全模式,防止 XSS 攻击等安全问题。
  26. IgnoreNonFieldGetter:

    • 忽略非字段的 getter 方法,即只序列化 Java Bean 的字段,不序列化通过 getter 方法暴露的属性(如果该 getter 不是对应某个字段的)。
  27. WriteNonStringValueAsString:

    • 将非字符串类型的值也强制序列化为字符串。
  28. IgnoreErrorGetter:

    • 在序列化过程中,如果 getter 方法抛出异常,则忽略该异常并继续序列化其他字段。
  29. WriteBigDecimalAsPlain:

    • BigDecimal 类型的值序列化为无科学计数法的普通数字字符串。
  30. MapSortField:

    • Map 类型的字段进行排序后再序列化。
测试样例
package com.zhz.test.entity;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Student implements Serializable {private String name;private String sex;private int age;
}
@Test
public void testFastjson3() {Student student = new Student("小明", null, 18);//序列化成JSON格式的字符串String jsonStr = JSON.toJSONString(student, SerializerFeature.WriteNullStringAsEmpty);System.out.println(jsonStr);//反序列化成对象Student student1 = JSON.parseObject(jsonStr, Student.class);System.out.println(student1);
}

image.png

SerializeConfig
用途
  • 全局配置序列化:提供统一的配置入口,管理序列化过程中的各种行为。

  • 自定义序列化器:为特定类型定义自己的序列化逻辑。

  • 日期格式设置:指定日期对象在序列化时应该遵循的格式。

使用方法
  1. 创建 SerializeConfig 实例:首先,需要创建一个 SerializeConfig 对象。

  2. 配置选项:通过 SerializeConfig 提供的各种方法,配置所需的序列化选项,如添加自定义序列化器、设置日期格式等。

  3. 应用配置:在调用 JSON.toJSONString 或其他序列化方法时,通过传入已配置的 SerializeConfig 实例,来应用这些全局配置。

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 testFastjson4() {// 创建 SerializeConfig 实例SerializeConfig serializeConfig = new SerializeConfig();// 配置日期格式(可选)serializeConfig.put(Date.class, new SimpleDateFormatSerializer("yyyy-MM-dd HH:mm:ss"));// 创建对象实例Student student = new Student("小明", null, 18,new Date());// 序列化成 JSON 格式的字符串,并应用 SerializeConfig 配置String jsonStr = JSON.toJSONString(student, serializeConfig, SerializerFeature.WriteNullStringAsEmpty);System.out.println(jsonStr);// 反序列化成对象(注意:反序列化时不需要 SerializeConfig)Student student1 = JSON.parseObject(jsonStr, Student.class);System.out.println(student1);// 假设有一个 Student 类,包含姓名、地址、年龄和日期字段(略)
}

image.png

自定义序列化器(Serializer)

允许用户自定义序列化器,用于控制特定类型的序列化过程。用户需要实现ObjectSerializer 接口,并重写write方法。

  @Datapublic static class CustomObject {private String name;private int value;// 构造方法、getter和setter省略}public static class CustomSerializer implements ObjectSerializer {@Overridepublic void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {CustomObject customObj = (CustomObject) object;serializer.write("custom_" + customObj.getName()); // 自定义序列化逻辑}}public static void main(String[] args) {CustomObject customObject = new CustomObject();customObject.setName("test");customObject.setValue(100);SerializeConfig config = new SerializeConfig();config.put(CustomObject.class, new CustomSerializer());String jsonString = JSON.toJSONString(customObject, config);System.out.println(jsonString); // 输出自定义序列化后的结果}

image.png

反序列化扩展点
ParseProcess

用途: 在反序列化过程中,ParseProcess 接口允许用户执行额外的逻辑操作,比如对反序列化后的对象进行修改、验证或添加额外的处理步骤。

使用方法
  1. 实现 ParseProcess 接口: 创建一个类,实现 ParseProcess 接口,并重写其中的方法。在这个方法里,你可以定义反序列化完成后需要执行的额外逻辑。

  2. 应用 ParseProcess 实例: 在调用 JSON.parseObject 或其他反序列化方法时,通过其重载版本传入你实现的 ParseProcess 实例。这样,在反序列化过程完成后,会自动调用你的自定义逻辑。

  3. ParseProcess有两个实现类,分别为:ExtraProcessorExtraTypeProvider,两者作用不一样,下面是两个实现类的demo。

使用ExtraProcessor 处理多余字段
  public class VO {private int id;private Map<String, Object> attributes = new HashMap<String, Object>();public int getId() { return id; }public void setId(int id) { this.id = id;}public Map<String, Object> getAttributes() { return attributes;}@Overridepublic String toString() {return "VO{" +"id=" + id +", attributes=" + attributes +'}';}public static void main(String[] args) {ExtraProcessor processor = new ExtraProcessor() {public void processExtra(Object object, String key, Object value) {System.out.println("---------------object = " + object);System.out.println("---------------key = " + key);System.out.println("---------------value = " + value);System.out.println();VO vo = (VO) object;vo.setId(789);// 修改一下id值vo.getAttributes().put(key, value);}};// 这里name和phone是多余的,在VO里没有VO vo = JSON.parseObject("{\"id\":123,\"name\":\"abc\",\"phone\":\"2134213\"}", VO.class, processor);System.out.println("vo.getId() = " + vo.getId());System.out.println("vo.getAttributes().get(\"name\") = " + vo.getAttributes().get("name"));System.out.println("vo.getAttributes().get(\"phone\") = " + vo.getAttributes().get("phone"));}}

image.png

使用ExtraTypeProvider 为多余的字段提供类型
        public static class VO {private int id;private Map<String, Object> attributes = new HashMap<String, Object>();public int getId() { return id; }public void setId(int id) { this.id = id;}public Map<String, Object> getAttributes() { return attributes;}@Overridepublic String toString() {return "VO{" +"id=" + id +", attributes=" + attributes +'}';}public static void main(String[] args) {// 这里name和phone是多余的,在VO里没有VO vo = JSON.parseObject("{\"id\":123,\"name\":\"abc\",\"phone\":\"2134213\"}", VO.class, new MyExtraProcessor());System.out.println("vo.getId() = " + vo.getId());System.out.println("vo.getAttributes().get(\"name\") = " + vo.getAttributes().get("name"));System.out.println("vo.getAttributes().get(\"phone\") = " + vo.getAttributes().get("phone"));}}public static class MyExtraProcessor implements ExtraProcessor, ExtraTypeProvider {public void processExtra(Object object, String key, Object value) {VO vo = (VO) object;vo.getAttributes().put(key, value);}public Type getExtraType(Object object, String key) {if ("phone".equals(key)) {return int.class;}return null;}}

image.png

ParserFeature
用途
  • Feature 枚举在 Fastjson 中用于控制反序列化的行为和特性。通过指定不同的 Feature 枚举值,用户可以定制反序列化过程中的多种行为,如是否忽略未知的字段、是否允许单引号包围的字符串等。
使用方法:
  • 在进行反序列化操作时,用户可以通过 JSON.parseObject 等方法的重载版本,传入 Feature 枚举值来控制反序列化的特性。这些枚举值作为额外的参数,指导 Fastjson 如何解析和转换 JSON 字符串为 Java 对象。
解析Feature枚举的属性:
  1. AutoCloseSource:

    • 自动关闭JSON源输入流。在完成反序列化后,会尝试关闭输入流,如InputStream
  2. AllowComment:

    • 允许JSON字符串中包含注释。标准的JSON是不支持注释的,开启此特性后,可以解析包含///* */注释的JSON。
  3. AllowUnQuotedFieldNames:

    • 允许字段名不使用双引号包围。标准的JSON要求字段名必须使用双引号,但开启此特性后,可以解析不使用双引号的字段名。
  4. AllowSingleQuotes:

    • 允许使用单引号包围字符串值。标准的JSON要求字符串值使用双引号,但开启此特性后,也支持单引号。
  5. InternFieldNames:

    • 对字段名进行字符串驻留(intern)。这有助于减少内存占用,当有很多相同字段名的JSON对象时。
  6. AllowISO8601DateFormat:

    • 允许使用ISO8601格式的日期字符串。例如,2023-04-01T12:00:00Z
  7. AllowArbitraryCommas:

    • 允许JSON对象中存在多余的逗号。例如,{"a":1,,"b":2}中的逗号。
  8. UseBigDecimal:

    • 使用BigDecimal来解析浮点数,而不是double。这可以提供更精确的数值表示。
  9. IgnoreNotMatch:

    • 忽略不匹配的字段。当JSON中的字段与Java对象的字段不匹配时,不会抛出异常。
  10. SortFeidFastMatch (可能是SortFieldFastMatch的拼写错误):

    • 对字段进行排序以快速匹配。这可能有助于优化某些情况下的反序列化性能。
  11. DisableASM:

    • 禁用ASM(Java字节码操作和分析框架)优化。在某些情况下,为了避免与ASM相关的问题,可以禁用它。
  12. DisableCircularReferenceDetect:

    • 禁用循环引用检测。当JSON中存在循环引用时,不会抛出异常。
  13. InitStringFieldAsEmpty:

    • 将字符串字段初始化为空字符串,而不是null
  14. SupportArrayToBean:

    • 支持将JSON数组反序列化为Java Bean。通常,JSON数组会转换为Java的Listarray,但开启此特性后,可以将其转换为具有特定字段的Java对象。
  15. OrderedField:

    • 保持字段的顺序。在反序列化时,Java对象的字段将按照JSON中出现的顺序进行设置。
  16. DisableSpecialKeyDetect:

    • 禁用特殊键检测。例如,不处理$ref等用于处理循环引用的特殊键。
  17. UseObjectArray:

    • 使用Object[]来接收反序列化的数组,而不是具体类型的数组。
  18. SupportNonPublicField:

    • 支持反序列化到非公共字段(例如,私有字段)。
  19. IgnoreAutoType:

    • 忽略自动类型识别。这可能与安全性相关,以防止利用类型识别进行攻击。
  20. DisableFieldSmartMatch:

    • 禁用字段智能匹配。这可能影响字段名与Java对象属性名的匹配逻辑。
  21. SupportAutoType:

    • 支持自动类型识别。允许在反序列化时自动确定对象的类型。
  22. NonStringKeyAsString:

    • 将非字符串类型的键也作为字符串处理。
  23. CustomMapDeserializer:

    • 使用自定义的Map反序列化器。允许用户定义如何反序列化JSON对象为Map类型。
  24. ErrorOnEnumNotMatch:

    • 当枚举值不匹配时抛出错误。如果JSON中的枚举值与Java枚举类中的值不匹配,将抛出异常。
  25. SafeMode:

    • 安全模式。在此模式下,Fastjson会采取更严格的安全措施来防止潜在的安全风险,如自动类型识别的限制等。
package com.zhz.test.serialization.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 testFastjson5() {// 创建对象实例Student student = new Student("小明", null, 18, new Date());// 序列化成 JSON 格式的字符串,并应用 SerializeConfig 配置String jsonStr = JSON.toJSONString(student, SerializerFeature.WriteNullStringAsEmpty);System.out.println(jsonStr);// 反序列化成对象(注意:反序列化时不需要 SerializeConfig)Student student1 = JSON.parseObject(jsonStr, Student.class, Feature.ErrorOnEnumNotMatch);System.out.println(student1);
}
自定义反序列化器(Deserializer)

ObjectDeserializer 专门用于处理将 JSON 数据反序列化为 Java 对象的过程。不过,需要注意的是,对于大多数开发者而言,直接使用 ObjectDeserializer 进行自定义反序列化可能不是最常见的做法,因为 Fastjson 提供了更高级别的抽象(如 @JSONField 注解、Feature 枚举等)来简化这一过程。

然而,在需要高度自定义反序列化行为时,了解并可能实现 ObjectDeserializer 是很有用的。下面是对 ObjectDeserializer 的一些基本解析:

1. 接口定义

ObjectDeserializer 是一个接口,它定义了一系列方法来处理 JSON 数据的反序列化过程。这些方法包括但不限于:

  • <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName):这是最主要的反序列化方法,用于将 JSON 数据转换为 Java 对象。

  • int getFastMatchToken():返回一个整数值,用于快速匹配 JSON 数据的类型,以提高反序列化性能。

2. 使用场景
  • 当你需要处理复杂的反序列化逻辑,而 Fastjson 提供的默认行为或注解方式无法满足需求时。

  • 当你需要优化反序列化性能,通过自定义 ObjectDeserializer 来实现更高效的解析逻辑时。

  • 当你需要处理自定义类型或第三方库中的类型,并且这些类型没有提供标准的 Fastjson 支持时。

3. 实现自定义 **ObjectDeserializer**

实现自定义的 ObjectDeserializer 通常涉及以下几个步骤:

  1. 定义类:创建一个类,实现 ObjectDeserializer 接口。

  2. 实现方法:根据需求实现接口中的方法,特别是 deserialze 方法。

  3. 注册反序列化器:将自定义的反序列化器注册到 Fastjson 的 ParserConfig 中,以便在反序列化过程中使用。

4、实现实例

**下面我们要实现一个需求:**实现自适应的各种日期解析

日期工具类

package com.zhz.test.serialization.json.fastjson;import com.google.common.collect.Sets;
import com.google.common.primitives.Ints;import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;/*** 日期工具类<br>** @author zhouhengzhe*/
public class DateSupport {/*** pattern for received date processing.*/private static final String[] PATTERNS = new String[]{"yyyy-MM-dd'T'HH:mm:ss.SSSZ",         /** ISO8601时间格式 */"yyyy-MM-dd HH:mm:ss",                  /** 用于SQL语句的时间戳格式转换格式 */"yyyy-MM-dd",                                  /** 日期转换格式 */"yyyy-MM-d HH:mm:ss","yyyy-MM-dd HH:mm:ss Z","yyyy-MM-dd HH:mm:ss z","HH:mm:ss","EEE MMM dd HH:mm:ss z yyyy",    /** Tue Feb 27 10:43:27 CST 2024 */"EEE, dd MMM yyyy HH:mm:ss '-'S '('z')'","EEE, dd MMM yyyy HH:mm:ss '+'S '('z')'","EEE, dd MMM yyyy HH:mm:ss '-'S","EEE, dd MMM yyyy HH:mm:ss '+'S","EEE, dd MMM yyyy HH:mm:ss z","EEE, dd MMM yyyy HH:mm:ss Z","EEE, dd MMM yyyy HH:mm:ss","EEE, d MMM yyyy HH:mm:ss '-'S '('z')'","EEE, d MMM yyyy HH:mm:ss '+'S '('z')'","EEE, d MMM yyyy HH:mm:ss '-'S","EEE, d MMM yyyy HH:mm:ss '+'S","EEE, d MMM yyyy HH:mm:ss z","EEE, d MMM yyyy HH:mm:ss Z","EEE, d MMM yyyy HH:mm:ss","EEE, dd MMM yy HH:mm:ss '-'S '('z')'","EEE, dd MMM yy HH:mm:ss '+'S '('z')'","EEE, dd MMM yy HH:mm:ss '-'S","EEE, dd MMM yy HH:mm:ss '+'S","EEE, dd MMM yy HH:mm:ss z","EEE, dd MMM yy HH:mm:ss Z","EEE, dd MMM yy HH:mm:ss","EEE, d MMM yy HH:mm:ss '-'S '('z')'","EEE, d MMM yy HH:mm:ss '+'S '('z')'","EEE, d MMM yy HH:mm:ss '-'S","EEE, d MMM yy HH:mm:ss '+'S","EEE, d MMM yy HH:mm:ss z","EEE, d MMM yy HH:mm:ss Z","EEE, d MMM yy HH:mm:ss","dd MMM yyyy HH:mm:ss '-'S","dd MMM yyyy HH:mm:ss '+'S","dd MMM yyyy HH:mm:ss '-'S '('z')'","dd MMM yyyy HH:mm:ss '+'S '('z')'","dd MMM yyyy HH:mm:ss z","dd MMM yyyy HH:mm:ss Z","dd MMM yyyy HH:mm:ss","dd MMM yyy HH:mm:ss '-'S","dd MMM yyy HH:mm:ss '+'S","dd MMM yyy HH:mm:ss '-'S '('z')'","dd MMM yyy HH:mm:ss '+'S '('z')'","dd MMM yyy HH:mm:ss z","dd MMM yyy HH:mm:ss Z","dd MMM yyy HH:mm:ss","yyyy.MM.dd HH:mm:ss z","yyyy.MM.dd HH:mm:ss Z","yyyy.MM.d HH:mm:ss z","yyyy.MM.d HH:mm:ss Z","yyyy.MM.dd HH:mm:ss","yyyy.MM.d HH:mm:ss","yy.MM.dd HH:mm:ss z","yy.MM.dd HH:mm:ss Z","yy.MM.d HH:mm:ss z","yy.MM.d HH:mm:ss Z","yy.MM.dd HH:mm:ss","yy.MM.d HH:mm:ss","yyyy MM dd HH:mm:ss","yyyy MM d HH:mm:ss","yyyy MM dd HH:mm:ss z","yyyy MM dd HH:mm:ss Z","yyyy MM d HH:mm:ss z","yyyy MM d HH:mm:ss Z","yy MM dd HH:mm:ss","yy MM d HH:mm:ss","yy MM dd HH:mm:ss z","yy MM dd HH:mm:ss Z","yy MM d HH:mm:ss z","yy MM d HH:mm:ss Z","yy-MM-dd HH:mm:ss z","yy-MM-dd HH:mm:ss Z","yy-MM-d HH:mm:ss z","yy-MM-d HH:mm:ss Z","yy-MM-dd HH:mm:ss","yy-MM-d HH:mm:ss","dd MMM yyyy","d MMM yyyy","dd.MMM.yyyy","d.MMM.yyyy","dd-MMM-yyyy","d-MMM-yyyy","dd MM yyyy","d MM yyyy","dd.MM.yyyy","d.MM.yyyy","dd-MM-yyyy","d-MM-yyyy","yyyy MM dd","yyyy MM d","yyyy.MM.dd","yyyy.MM.d","yyyy-MM-d","dd MMM yy","d MMM yy","dd.MMM.yy","d.MMM.yy","dd-MMM-yy","d-MMM-yy","dd MM yy","d MM yy","dd.MM.yy","d.MM.yy","dd-MM-yy","d-MM-yy","yy MMM dd","yy MMM d","yy.MMM.d","yy-MMM-dd","yy-MMM-d","yy.MMM.dd",// ex: Wed 19, Feb 2003"EEE dd, MMM yyyy",// ex: Wed 19, Feb 03"EEE dd, MMM yy"};/*** ISO8601 date time pattern*/static final String ISO8601_FORMATTER_STR = PATTERNS[0];/*** 用于SQL语句的时间戳格式转换格式*/public static final String TIMESTAMP_FORMATTER_STR = PATTERNS[1];/*** 日期转换格式*/static final String DATE_FORMATTER_STR = PATTERNS[2];/*** get a date from a date string representation in one of the registered formats** @param strDate       the date as string.* @param pattern       [out] if not null, return pattern string or null if (null or empty) or correct pattern was not found* @param excludeIndexs excluded pattern index* @return Date object ,otherwise null If (null or empty) or correct pattern was not found*/public static java.util.Date getDateFromString(String strDate, AtomicReference<String> pattern, int... excludeIndexs) {java.util.Date dReceivedDate = null;if (strDate == null || strDate.trim().equals("null")) {return dReceivedDate;} else {strDate = strDate.trim();}Set<Integer> exidx =Sets.newHashSet(Ints.asList(null == excludeIndexs ? new int[0] : excludeIndexs));SimpleDateFormat pSimpleDateFormat = new SimpleDateFormat("", Locale.ENGLISH);if (!strDate.isEmpty()) {for (int i = 0; i < PATTERNS.length; i++) {if (exidx.contains(i)) {continue;}try {pSimpleDateFormat.applyPattern(PATTERNS[i]);dReceivedDate = pSimpleDateFormat.parse(strDate);if (dReceivedDate == null) {continue;}if (null != pattern) {pattern.set(PATTERNS[i]);}return dReceivedDate;} catch (ParseException pe) {; // ignore this format try the next one}}}return dReceivedDate;}/*** get a date from a date string representation in one of the registered formats** @param strDate the date as string.* @return Date object ,otherwise null If (null or empty) or correct pattern was not found*/public static java.util.Date getDateFromString(String strDate) {return getDateFromString(strDate, null);}/*** get a date from a date string representation in one of the registered formats** @param dateStr     the date as string.* @param targetClass* @return Date object ,otherwise null If (null or empty) or correct pattern was not found*/public static <D extends Date> D parseDateString(String dateStr, Class<D> targetClass) {if (null != dateStr && null != targetClass) {Date date = null;try {date = new SimpleDateFormat(ISO8601_FORMATTER_STR).parse(dateStr);} catch (ParseException e3) {try {date = java.sql.Timestamp.valueOf(dateStr);} catch (IllegalArgumentException e) {try {date = java.sql.Date.valueOf(dateStr);} catch (IllegalArgumentException e1) {try {date = java.sql.Time.valueOf(dateStr);} catch (IllegalArgumentException e2) {date = getDateFromString(dateStr);}}}}return castToDate(date, targetClass);}return null;}/*** get a date from a date string representation in one of the registered formats** @param input         the date as string.* @param targetClass   Date or Calendar or subclass required* @param excludeIndexs excluded pattern index* @return Date object ,otherwise null If (null or empty) or correct pattern was not found*/public static <D> D parseAsDate(String input, Class<D> targetClass, int... excludeIndexs) {if (null != input && null != targetClass) {Date date = getDateFromString(input, null, excludeIndexs);return castToDate(date, targetClass);}return null;}/*** convert {@link Date} to ISO8601 date time format string** @param date* @return ISO8601 date time format string or null if date is null*/public static String toISO8601String(Date date) {return null == date ? null : new SimpleDateFormat(ISO8601_FORMATTER_STR).format(date);}/*** format {@link Datec} to  string** @param date* @param format date time format string,use ISO8601 format if null* @return ISO8601 date time format string or null if date is null*/public static String formatDate(Date date, String format) {return null == date ? null : new SimpleDateFormat(null == format ? ISO8601_FORMATTER_STR : format).format(date);}/*** Verify that the string represantes the date with one of the registered formats** @param strDate the date as string.* @return boolean "true" if the string represantes the date in one of the registed formats.*/public static boolean isDate(String strDate) {return null != getDateFromString(strDate);}/*** Verify that the string represantes the date with one of the registered formats** @param strDate the date as string.* @return boolean "true" if the string represantes the date in one of the registed formats.* @since 3.25.0*/public static String patternOf(String strDate) {AtomicReference<String> p = new AtomicReference<>();getDateFromString(strDate, p);return p.get();}/*** 将对象转为指定的日期类型** @param <F>         原类型  String,Number,java.util.Date or Calendar or subclass* @param <T>         目标类型  java.util.Date or Calendar or subclass* @param from* @param targetClass*/@SuppressWarnings("unchecked")public static <F, T> T castToDate(F from, Class<T> targetClass) {if (null != from && null != targetClass) {if (targetClass.isInstance(from)) {return targetClass.cast(from);} else if (from instanceof Date) {Date date = (Date) from;if (Date.class.isAssignableFrom(targetClass)) {try {return targetClass.getConstructor(long.class).newInstance(date.getTime());} catch (Exception e) {throw new IllegalArgumentException("UNSUPPORTED Date type:" + targetClass.getName(), e);}} else if (targetClass == Calendar.class) {Calendar calendar = Calendar.getInstance();calendar.setTime(date);return (T) calendar;} else if (Calendar.class.isAssignableFrom(targetClass)) {try {Calendar calendar = (Calendar) targetClass.newInstance();calendar.setTime(date);return (T) calendar;} catch (Exception e) {throw new IllegalArgumentException("UNSUPPORTED Date type:" + targetClass.getName(), e);}} else {throw new IllegalArgumentException("UNSUPPORTED Date type:" + targetClass.getName());}} else if (from instanceof Calendar) {return castToDate(((Calendar) from).getTime(), targetClass);} else if (from instanceof Number) {return castToDate((new Date(((Number) from).longValue())), targetClass);} else if (from instanceof String) {return castToDate(getDateFromString((String) from, null), targetClass);} else {throw new IllegalArgumentException("UNSUPPORTED Date type:" + from.getClass().getName());}}return null;}
}

反序列化实现

package com.zhz.test.serialization.json.fastjson;import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.JSONLexer;
import com.alibaba.fastjson.parser.JSONScanner;
import com.alibaba.fastjson.parser.JSONToken;
import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;import java.lang.reflect.Type;
import java.util.Calendar;
import java.util.Date;/*** 日期类型FASTJSON反序列化实现,* 相比默认的反序列化器 {@link com.alibaba.fastjson.serializer.DateCodec}  支持更多日期格式* @author zhouhengzhe*/
public class FastjsonDateDeserializer implements ObjectDeserializer {@Override@SuppressWarnings({ "unchecked", "rawtypes" })public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {JSONLexer lexer = parser.lexer;if(!(type instanceof Class) || !(Date.class.isAssignableFrom((Class)type) || Calendar.class.isAssignableFrom((Class)type))) {throw new IllegalArgumentException("type required Date or Calendar");}switch(lexer.token()) {case JSONToken.LITERAL_INT:{long millis = lexer.longValue();lexer.nextToken(JSONToken.COMMA);try {return (T) DateSupport.castToDate(millis,(Class)type);} catch (Exception e) {throw new JSONException(e.getMessage(),e);}}case JSONToken.LITERAL_STRING:{String strVal = lexer.stringVal();lexer.nextToken(JSONToken.COMMA);try(JSONScanner iso8601Lexer = new JSONScanner(strVal)){if (iso8601Lexer.scanISO8601DateIfMatch(false)) {Calendar calendar = iso8601Lexer.getCalendar();return (T) DateSupport.castToDate(calendar,(Class)type);}else {Object parsed = DateSupport.parseAsDate(strVal, (Class)type,0,1,2,3,4);if(parsed != null) {return (T) parsed;}throw new JSONException("FAIL parse date: "+strVal);}}}case JSONToken.NULL:lexer.nextToken();return null;default:throw new JSONException("parse error");}}public int getFastMatchToken() {return JSONToken.LITERAL_INT;}
}

测试DEMO

    @Testpublic void test6FastjsonDateDeserializer() {try {String input="\"Tuesday February 27 10:43:27 CST 2024\"";
//        String input="\"2024-02-27 15:01:31+0800\"";
//        String input="\"2024-02-27\"";
//        ParserConfig.global.putDeserializer(Date.class, new FastjsonDateDeserializer());ParserConfig.global.putDeserializer(java.sql.Date.class, new FastjsonDateDeserializer());Date parsed = JSON.parseObject(input,java.sql.Date.class);System.out.println(parsed);} catch (Throwable e) {e.printStackTrace();}}

结果

image.png

5. 注意事项
  • 在实现自定义 ObjectDeserializer 时,要特别注意处理各种可能的 JSON 数据格式和异常情况。

  • 确保你的反序列化器是线程安全的,特别是在多线程环境下使用时。

  • 考虑性能优化,避免在反序列化过程中进行不必要的计算或资源消耗。

package com.zhz.test.entity;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Student implements Serializable {private String name;private String sex;private int age;
}

常用API

//把JSON文本解析为JSONObject或者JSONArray
public static final Object parse(String text); //把JSON文本解析成JSONObject
public static final JSONObject parseObject(String text)//把JSON文本解析为JavaBean
public static final <T> T parseObject(String text, Class<T> clazz);  //把JSON文本解析成JSONArray。只有字符串是JSON数组的时候才能转,不然会报错。
public static final JSONArray parseArray(String text);  //把JSON文本解析成JavaBean集合
public static final <T> List<T> parseArray(String text, Class<T> clazz);  //将JavaBean序列化为JSON文本 
public static final String toJSONString(Object object); //将JavaBean序列化为带格式的JSON文本
public static final String toJSONString(Object object, boolean prettyFormat);  //将JavaBean转换为JSONObject或者JSONArray。
public static final Object toJSON(Object javaObject); 

普通版DEMO

引入依赖
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83_noneautotype</version>
</dependency>
测试demo
/*** fastjson测试*/
@Test
public void testFastjson(){Student student = new Student("小明", '男', 18);//序列化成JSON格式的字符串String jsonStr = JSON.toJSONString(student);System.out.println(jsonStr);//反序列化成对象Student student1 = JSON.parseObject(jsonStr, Student.class);System.out.println(student1);
}
结果

image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/145832.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

五、 共射极放大电路

共射极放大电路 1、三极管的结构、三极管各极电流关系、特性曲线、放大条件。   2、元器件的作用、电路的用途、电压放大倍数、输入和输出的信号电压相位关系、交流和直流等效电路图。 3、静态工作点的计算、电压放大倍数的计算。

【PCL实现点云分割】ROS深度相机实践指南(上):PCL库初识和ROS-PCL数据类型转换

前言 本教程使用PCL对ROS深度相机捕获到的画面进行操场上锥桶的分割 本人相关的RGBD深度相机原理及其使用教程&#xff1a; [csdn 博客] 上手一个RGBD深度相机&#xff1a;从原理到实践–ROS noeticAstra S&#xff08;上&#xff09;&#xff1a;解读深度测距原理和内外参推导…

金属3D打印经济效益高吗?

在我国制造业迈向产业升级的重要阶段&#xff0c;3D打印技术如同一股强劲的新风&#xff0c;特别是在航空航天、汽车、生物医疗等领域&#xff0c;已成为复杂构件制造的“明星”技术。那么&#xff0c;对于众多生产厂家而言&#xff0c;金属3D打印的经济账到底怎么算&#xff1…

rabbitmq 短信验证码

1.生成的验证码用redis存 减少数据库压力 2.通知运营商发送短信的事情交给rabbitmq的队列去做&#xff0c;无论成功或者是失败&#xff0c;用户那边都不知道。没有收到验证码&#xff08;监听失败&#xff09;用户只会觉得是运营商的问题&#xff0c;而不会怀疑是我们的系统有问…

深入掌握 Go 单元测试:从基础到进阶的完整指南

你好&#xff0c;我是陈明勇&#xff0c;一名热爱技术、乐于分享的开发者&#xff0c;同时也是开源爱好者。 成功的路上并不拥挤&#xff0c;有没有兴趣结个伴&#xff1f; 个人网站&#xff1a;https://chenmingyong.cn 文章持续更新&#xff0c;如果本文能让您有所收获&#…

罗马数字详解

一. 罗马数字の背景 1. 罗马数字的诞生与进化 罗马数字起源于古罗马帝国&#xff0c;拥有一个漫长而复杂的历史&#xff0c;始于公元前 8 世纪至 9 世纪&#xff0c;与古罗马帝国在帕兰丁山&#xff08;Palantine Hill&#xff09;周围建立的时间大致相同。不过&#xff0c;罗…

铲屎官进!宠物空气净化器真的有用吗?哪款去浮毛效果好

国庆小长假就要来了&#xff0c;别人都在苦恼抢票问题&#xff0c;而我在想会不会被我妈赶出家门... 毕业后我就留在了广州上班&#xff0c;独自一人租房难免会感觉孤独&#xff0c;就养了一只小猫和我作伴。这次放假这么久&#xff0c;我不放心留它一个人在家&#xff0c;也没…

SpringBoot 项目如何使用 pageHelper 做分页处理 (含两种依赖方式)

分页是常见大型项目都需要的一个功能&#xff0c;PageHelper是一个非常流行的MyBatis分页插件&#xff0c;它支持多数据库分页&#xff0c;无需修改SQL语句即可实现分页功能。 本文在最后展示了两种依赖验证的结果。 文章目录 一、第一种依赖方式二、第二种依赖方式三、创建数…

Virtuoso服务在centos中自动停止的原因分析及解决方案

目录 前言1. 问题背景2. 原因分析2.1 终端关闭导致信号12.2 nohup命令的局限性 3. 解决方案3.1 使用 screen 命令保持会话3.2 使用 tmux 作为替代方案3.3 使用系统服务&#xff08;systemd&#xff09; 4. 其他注意事项4.1 网络配置4.2 日志监控 结语 前言 在使用Virtuoso作为…

Transformer 的可视化解释

Transformer 的可视化解释&#xff1a;了解 LLM Transformer 模型如何与交互式可视化配合使用 部署 Nodejs version > 20.0 git clone https://github.com/poloclub/transformer-explainer.git cd transformer-explainer npm install npm run dev# fix: cnpm install --pl…

AD9854 为什么输出波形幅度受限??

&#x1f3c6;本文收录于《全栈Bug调优(实战版)》专栏&#xff0c;主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&am…

lambda 自调用递归

从前序与中序遍历序列构造二叉树 官方解析实在是记不住&#xff0c;翻别人的题解发现了一个有意思的写法 class Solution { public:TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {auto dfs [](auto&& dfs, auto&&…

Pandas和matplotlib实现同期天气温度对比

目录 1、下载近两年的天气Excel数据 2、pandas加载Excel 3、将时间作为索引 4、按日计算最值、均值 5、选取近两年同期温度数据 6、同期温度曲线对比,共享y轴 1、下载近两年的天气Excel数据 一个免费的天气数据下载网址:METAR北京(机场)历史天气 (rp5.ru) 选择”北京天…

centos 7.9安装k8s

前言 Kubernetes单词来自于希腊语&#xff0c;含义是领航员&#xff0c;生产环境级别的容器编排技术&#xff0c;可实现容器的自动部署扩容以及管理。Kubernetes也称为K8S&#xff0c;其中8代表中间8个字符&#xff0c;是Google在2014年的开源的一个容器编排引擎技术&#xff…

一文读懂SpringCLoud

一、前言 只有光头才能变强 认识我的朋友可能都知道我这阵子去实习啦&#xff0c;去的公司说是用SpringCloud(但我觉得使用的力度并不大啊~~)… 所以&#xff0c;这篇主要来讲讲SpringCloud的一些基础的知识。(我就是现学现卖了&#xff0c;主要当做我学习SpringCloud的笔记吧&…

【JPCS出版】第二届应用统计、建模与先进算法国际学术会议(ASMA2024,9月27日-29)

第二届应用统计、建模与先进算法国际学术会议 2024 2nd International Conference on Applied Statistics, Modeling and Advanced Algorithms&#xff08;ASMA2024&#xff09; 会议官方 会议官网&#xff1a;www.icasma.org 2024 2nd International Conference on Applied …

Moveit2与gazebo联合仿真:添加摄像头传感器

1.代码更新修改 1.1 添加物理关节 如图&#xff0c;在原有机械臂的基础上添加camera_link和base_camera_joint作为传感器的几何属性 对应的xml代码如下 <link name"${prefix}camera_link"><collision><geometry><box size"0.01 0.1 0.05&…

【Python】练习:控制语句(二)第4关

第4关&#xff1a;控制结构综合实训 第一题第二题&#xff08;※&#xff09;第三题&#xff08;※&#xff09;第四题&#xff08;※&#xff09;第五题&#xff08;※&#xff09;第六题&#xff08;※&#xff09; 第一题 #第一题def rankHurricane(velocity):#请在下面编写…

毫米波雷达预警功能 —— 盲区检测(BSD)预警

文档声明&#xff1a; 以下资料均属于本人在学习过程中产出的学习笔记&#xff0c;如果错误或者遗漏之处&#xff0c;请多多指正。并且该文档在后期会随着学习的深入不断补充完善。感谢各位的参考查看。 笔记资料仅供学习交流使用&#xff0c;转载请标明出处&#xff0c;谢谢配…

MySQL高阶1875-将工资相同的雇员分组

目录 题目 准备数据 分析数据 题目 编写一个解决方案来获取每一个被分配到组中的雇员的 team_id 。 返回的结果表按照 team_id 升序排列。如果相同&#xff0c;则按照 employee_id 升序排列。 这家公司想要将 工资相同 的雇员划分到同一个组中。每个组需要满足如下要求&a…