序列化方式二——JSON之fastjson

news2024/11/15 13:38:49

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

 @Test
    public void testFastjson2() {
        Student student = new Student("小明", "男", 18, new Date());
        //序列化成JSON格式的字符串
        String jsonStr = JSON.toJSONString(student, new SimplePropertyPreFilter() {
            @Override
            public 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方法。

  @Data
    public static class CustomObject {
        private String name;
        private int value;

        // 构造方法、getter和setter省略
    }

    public static class CustomSerializer implements ObjectSerializer {
        @Override
        public 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;}

        @Override
        public 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;}

        @Override
        public 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

    @Test
    public 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.coloradmin.cn/o/2152758.html

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

相关文章

Linux top命令详解与重点内容说明

文章目录 重点说明基本信息进程(任务)信息cpu占用信息%Cpu(s)内存信息交换内存信息每列含义说明交互命令多窗口模式颜色配置命令参数 重点说明 top命令非常强大&#xff0c;也非常复杂&#xff0c;很难面面俱到&#xff0c;也没有必要&#xff0c;这篇文章的目的是介绍重点&am…

Apache Doris 实践

Apache Doris 实践 官方使用指南&#xff1a;https://doris.incubator.apache.org/zh-CN/docs/install/source-install/compilation-with-docker/ 手动安装 下载二进制安装包https://apache-doris-releases.oss-accelerate.aliyuncs.com/apache-doris-2.1.5-bin-x64.tar.gz …

企业搭建VR虚拟展厅,如何选择搭建平台?

选择虚拟展厅搭建平台时&#xff0c;需要综合考虑多个因素以确保平台能够满足您的具体需求并提供高质量的展示效果。以下是一些关键的选择标准&#xff1a; 1. 技术实力与创新能力 技术平台选择&#xff1a;确保平台支持虚拟现实&#xff08;VR&#xff09;、增强现实&#xf…

http免费升级https教程

将HTTP免费升级至HTTPS的详细教程涉及获取SSL证书、配置服务器和测试HTTPS连接等关键步骤。 以下是具体介绍&#xff1a; 获取SSL证书&#xff1a;选择提供免费SSL证书的服务&#xff0c;如JoySSL。在证书提供商的网站上申请免费SSL证书注册时填写注册码230920&#xff0c;获取…

如何优化前端页面的 AJAX 请求性能并避免冲突

个人名片 🎓作者简介:java领域优质创作者 🌐个人主页:码农阿豪 📞工作室:新空间代码工作室(提供各种软件服务) 💌个人邮箱:[2435024119@qq.com] 📱个人微信:15279484656 🌐个人导航网站:www.forff.top 💡座右铭:总有人要赢。为什么不能是我呢? 专栏导…

MBR60100PT-ASEMI适配变频器专用MBR60100PT

编辑&#xff1a;ll MBR60100PT-ASEMI适配变频器专用MBR60100PT 型号&#xff1a;MBR60100PT 品牌&#xff1a;ASEMI 封装&#xff1a;TO-247 安装方式&#xff1a;插件 批号&#xff1a;最新 最大平均正向电流&#xff08;IF&#xff09;&#xff1a;60A 最大循环峰值…

Redis基础数据结构之 Sorted Set 有序集合 源码解读

目录标题 Sorted Set 是什么?Sorted Set 数据结构跳表&#xff08;skiplist&#xff09;跳表节点的结构定义跳表的定义跳表节点查询层数设置 Sorted Set 基本操作 Sorted Set 是什么? 有序集合&#xff08;Sorted Set&#xff09;是 Redis 中一种重要的数据类型&#xff0c;…

Wpf使用NLog将日志输出到LogViewer

1 LogViewer LogViewer是通过UDP传输的高性能实时log查看器。 具有一下特性&#xff1a; 通过UDP读取日志通过文件导入日志导出日志到一个文件中排序、过滤&#xff08;日志树&#xff0c;日志等级&#xff09;和查找突出显示搜索文本从UPD接收日志时忽略IP地址列表多接收器支…

灾备技术演进之路 | 虚拟化无代理备份只能挂载验证和容灾吗?只能无代理恢复吗?且看科力锐升级方案

灾备技术演进之路系列 虚拟化备份技术演进 摆脱束缚&#xff0c;加速前行 无代理备份仅能挂载/恢复验证吗&#xff1f; ——科力锐极简验证演练无代理备份来了 无代理备份无法应对平台级故障吗&#xff1f; ——科力锐应急接管无代理备份来了 无代理备份仅能同平台挂载吗&a…

Java笔试面试题AI答之单元测试JUnit(7)

文章目录 37. 请列举一些JUnit扩展 &#xff1f;1. 参数化测试2. 条件测试执行3. 临时目录4. 时间测试5. 重复测试6. 前置/后置条件7. Mockito8. Spring Test9. JUnit Vintage10. Testcontainers11. 自定义注解和扩展12. 测试监听器&#xff08;TestListener 和 RunListener&am…

python中ocr图片文字识别样例(二)

一、说明 本次解决图片相关出现中文乱码问题&#xff0c;属于上篇文章的优化&#xff0c;前提条件依赖上篇文章的包&#xff0c;当然ocr的具体应用场景很多&#xff0c;根据自身需求进行调整 二、具体实现 2.1 代码实现&#xff1a; # -*- coding: utf-8 -*- import easyoc…

CertiK因发现Apple Vision Pro眼动追踪技术漏洞,第6次获苹果认可

​2024年9月20日&#xff0c;头部Web3.0安全机构CertiK自豪地宣布&#xff0c;CertiK的工程师因发现Apple Vision Pro MR&#xff08;混合现实&#xff09;头显设备中的关键漏洞而获得Apple公司认可&#xff0c;这已经是Apple公司第六次公开发布对CertiK的致谢&#xff0c;Cert…

day21JS-npm中的部分插件使用方法

1. 静态资源目录 静态资源目录就是访问服务器的某些路劲时候&#xff0c;服务器可以吐出一个写好的指定页面。 实现思路&#xff1a; 1、先判断要找的路径是否是文件&#xff0c;如果是文件&#xff0c;就加载发给对方。 2、如果是文件夹&#xff0c;找到这个文件夹所在路径中…

828华为云征文|华为云Flexus云服务器X实例之openEuler系统下部署Joplin笔记工具

828华为云征文&#xff5c;华为云Flexus云服务器X实例之openEuler系统下部署joplin笔记工具 前言一、Flexus云服务器X实例介绍1.1 Flexus云服务器X实例简介1.2 Flexus云服务器X实例特点1.3 Flexus云服务器X实例使用场景 二、Joplin介绍2.1 Joplin简介2.2 Joplin主要特点 三、本…

倒排索引(反向索引)

倒排索引&#xff08;Inverted Index&#xff09;是搜索引擎和数据库管理系统中常用的一种数据结构&#xff0c;用于快速检索文档集合中的文档。在全文搜索场景中&#xff0c;倒排索引是一种非常高效的手段&#xff0c;因为它能够快速定位到包含特定关键词的所有文档。 1、基本…

Java面试——集合篇

1.Java中常用的容器有哪些&#xff1f; 容器主要包括 Collection 和 Map 两种&#xff0c;Collection 存储着对象的集合&#xff0c;而 Map 存储着键值对(两个对象)的映射表。 如图&#xff1a; 面试官追问&#xff1a;说说集合有哪些类及他们各自的区别和特点&#xff1f; S…

@Mapper 和 @MapperScan

今天在学习项目的时候&#xff0c;遇到一个小问题&#xff0c;先记录下来。 Mapper 和 MapperScan 的效果是一样的 Mapper 加到对应的接口上MapperScan 则通常在一个单独的配置类中&#xff0c;在这个类中使用MapperScan声明要扫描的 mapper 接口。也就是说&#xff0c;这两个…

进程分析工具Process Explorer使用

进程分析工具Process Explorer使用 Process Explorer让使用者能了解看不到的在后台执行的处理程序&#xff0c;能显示目前已经载入哪些模块&#xff0c;分别是正在被哪些程序使用着&#xff0c;还可显示这些程序所调用的DLL进程&#xff0c;以及他们所打开的句柄。Process Expl…

MySQL-DDL/DML(数据定义/操作语言)

数据定义语言(DDL-Data Definition Language) 代表关键字:create ,drop,alter 数据操作语言(DML-Data Manipulation Language) 代表关键字&#xff1a;insert,delete,update 1、表的操作 1.1 创建表 create table 表名(字段1,字段2,字段3.....字段n) [charset字符集][type表类…

【JavaScript】LeetCode:46-50

文章目录 46 翻转二叉树47 对称二叉树48 二叉树的直径49 二叉树的层序遍历50 将有序数组转换为二叉搜索树 46 翻转二叉树 递归前序遍历 / 后序遍历&#xff0c;这里给出前序遍历的代码。遍历节点&#xff0c;交换左右子树。 /*** Definition for a binary tree node.* functio…