fastjson使用

fastjson使用

https://github.com/alibaba/fastjson/wiki

1、基本API和配置

准备POJO测试数据

@Before
public void initObjectAndList(){
    //设置user对象的值
    user = new User();
    user.setId(1);
    user.setUsername("小鲁");

    Calendar calendar = Calendar.getInstance();
    calendar.set(1990,11,20);
    Date birthday = calendar.getTime();
    IdCard idCard = new IdCard("1999-05-10","1",birthday);
    user.getIdCards().add(idCard);

    calendar.set(1995,0,1);
    Date birthday2 = calendar.getTime();
    IdCard idCard2 = new IdCard("2000-10-10","0",birthday2);
    user.getIdCards().add(idCard2);

    //设置users集合的值
    users.add(user);
}

@Before
public void initString(){
    userStr = "{\"ids\":1,\"id\":1,\"idCards\":[{\"birthday\":\"1995-01-01\",\"cardNo\":\"2000134102030010\",\"sex\":\"1\"},{\"birthday\":788929283273,\"cardNo\":\"2000134102030020\",\"sex\":\"0\"}],\"username\":\"小鲁\"}";
    usersStr = "[{\"id\":1,\"idCards\":[{\"birthday\":\"1990-01-01\",\"cardNo\":\"2000134102030010\",\"sex\":\"1\"},{\"birthday\":\"2002-11-11\",\"cardNo\":\"2000134102030020\",\"sex\":\"0\"}],\"username\":\"小鲁\"}]";
}

1.1 序列化

  1. 对象/Map -- 字符串

    @Test
    public void toJsonString(){
        String userString = JSON.toJSONString(user);
        System.out.println(userString);
        String usersString = JSON.toJSONString(users);
        System.out.println(usersString);
    }
    
    {
      "id":1,
      "idCards":[
        {
          "birthday":661654769839,
          "cardNo":"2020001",
          "sex":"1"
        },
        {
          "birthday":788921969839,
          "cardNo":"2020002",
          "sex":"0"
        }
      ],
      "username":"小y"
    }
    [
      {
        "id":1,
        "idCards":[
          {
            "birthday":661654769839,
            "cardNo":"2020001",
            "sex":"1"
          },
          {
            "birthday":788921969839,
            "cardNo":"2020002",
            "sex":"0"
          }
        ],
        "username":"小y"
      }
    ]
    
  2. 序列化到OutputStream

    /**
         * 和OutputStream/Writer对接
         * 应用场景:直接和web项目的response对接
         */
    @Test
    public void toJsonOS() throws Exception {
        File file = new File("E:/test.json");
        FileOutputStream fileOutputStream = new FileOutputStream(file);
        JSON.writeJSONString(fileOutputStream,user);
    }
    
  3. BeanToArray

    @Test
    public void bean2Array() throws Exception {
        System.out.println(JSON.toJSONString(user,SerializerFeature.BeanToArray));
    }
    
    [1,[[100.0101,661676334969,"2020001","1"],[200.056,788943534969,"2020002","0"]],"小y"]
    

1.2 反序列化

  1. 字符串转对象/Map/集合

    @Test
    public void parseObjectOrArray(){
        User user = JSON.parseObject(userStr, User.class);
        System.out.println(user);
        List<User> users = JSON.parseArray(usersStr, User.class);
        System.out.println(users);
        List<Map> maps = JSON.parseArray(usersStr, Map.class);
        System.out.println(maps);
    }
    
  2. InputStream转对象/Map/集合

    /**
         * 和inputstream对接
         * 应用场景:web项目中,请求过来的payload数据可以通过该API解析数据
         */
    @Test
    public void testIsToObject() throws IOException {
        InputStream is = new ByteArrayInputStream(userStr.getBytes());
        User user = JSON.parseObject(is, User.class);
        System.out.println(user);
    }
    
  3. 传入的是对象(数组同理),但是对象中的属性值是一个复杂对象

    {"user":{
        "id":1,
        "username":"小A",
        idCards:[
            {cardNo:2000134102030010, "sex":1, "birthday":"1995-01-01"}, 
            {cardNo:2000134102030020, "sex":0, "birthday":"1992-02-02"}
            ]
      }
    }
    
    @Test
    public void testComObject() throws IOException {
        String a = "{\"user\":{\n" +
            "    \"id\":1,\n" +
            "    \"username\":\"小A\",\n" +
            "    idCards:[\n" +
            "        {cardNo:2000134102030010, \"sex\":1, \"birthday\":\"1995-01-01\"}, \n" +
            "        {cardNo:2000134102030020, \"sex\":0, \"birthday\":\"1992-02-02\"}\n" +
            "        ]\n" +
            "\t}\n" +
            "}";
        Map<String,User> user = JSON.parseObject(a, new TypeReference<Map<String, User>>(){});
        System.out.println(user);
    
        User user1 = user.get("user");
        IdCard idCard = user1.getIdCards().get(0);
        System.out.println(idCard.getBirthday());
    }
    

1.3 定制序列化

以最常用的Date类型举例说明。

  1. @JSONField

    @JSONField(format = "yyyy-MM-dd")
    private Date birthday;
    
    @Test
    public void dateFormat1(){
        String string = JSON.toJSONString(user);
        System.out.println(string);
    }
    
  2. 使用SerializerFeature的WriteDateUseDateFormat

    @Test
    public void dateFormat(){
        JSON.DEFFAULT_DATE_FORMAT = "yyyy/MM/dd HH:mm:ss";
        String string = JSON.toJSONString(user,SerializerFeature.WriteDateUseDateFormat);
        System.out.println(string);
    }
    
    {"id":1,"idCards":[{"balance":100.015,"birthday":"1990-11-10","cardNo":"1001"},{"balance":300.0123,"birthday":"2000-10-09","cardNo":"1002"}],"username":"查克拉"}
    
  3. 配置SerializeConfig

    @Test
    public void dateFormat3(){
        SerializeConfig config = new SerializeConfig();
        config.put(Date.class,new SimpleDateFormatSerializer("yyyy/MM/dd HH:mm:ss"));
        String str = JSON.toJSONString(user,config);
        System.out.println(JSON.toJSONString(str);
    }
    
  4. 使用SerializeFilter

    @Test
    public void dateFormat4(){
        // 类似全局配置,@JSONField会失效
        ValueFilter valueFilter = new ValueFilter() {
            public Object process(Object object, String name, Object value) {
                if(value instanceof Date){
                    value = new SimpleDateFormat("yyyy/MM/dd").format(value);
                }
                return value;
            }
        };
        System.out.println(JSON.toJSONString(user,valueFilter));
    }
    

    SerializeFilter下有多个子接口或抽象类

    简单说明:
    PropertyPreFilter根据PropertyName判断是否序列化
    PropertyFilter 在序列化,设定那些字段是否被序列化
    NameFilter 序列化时修改Key的名称。比如属性名为name,可以修改为Name。
    ValueFilter 序列化时修改Value
    BeforeFilter 在序列化对象的所有属性之前执行某些操作

    AfterFilter 在序列化对象的所有属性之后执行某些操作
    他们有执行顺序:PropertyPreFilter --> PropertyFilter --> NameFilter --> ValueFilter --> BeforeFilter --> AfterFilter

  5. 使用JSONField注解的serializeUsing属性

    public class DateSer implements ObjectSerializer {
        public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
            if (object == null) {
                serializer.out.writeNull();
                return;
            }
            Date date = (Date)object;
            String dateStr = new SimpleDateFormat("yyyy-MM/dd HH:mm:ss").format(date);
            serializer.write(dateStr);
        }
    }
    
    @JSONField(serializeUsing = DateSer.class)
    private Date birthday;
    

    注解属性说明:

    public @interface JSONField {
      // 序列化、反序列化的顺序
        int ordinal() default 0;
      // 指定字段的名称
        String name() default "";
      // 指定字段的格式,对日期格式有用 -常用
        String format() default "";
       // 是否序列化 -常用
        boolean serialize() default true;
      // 是否反序列化
        boolean deserialize() default true;
      // 指定该字段使用的SerializerFeature
        SerializerFeature[] serialzeFeatures() default {};
    
        Feature[] parseFeatures() default {};
       // 给属性打上标签, 相当于给属性进行了分组
        String label() default "";
        
        boolean jsonDirect() default false;
        
      // 设置属性的序列化类
        Class<?> serializeUsing() default Void.class;
       // 设置属性的反序列化类
        Class<?> deserializeUsing() default Void.class;
    
        String[] alternateNames() default {};
    
        boolean unwrapped() default false;
    }
    

2、springboot+fastjson

2.1 配置fastjson

  1. 依赖 (我们此处暂不使用fastjson的起步依赖方式)

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.68</version>
    </dependency>
    
  2. 配置类

    springmvc 4.2+ 、fastjson使用最新的

    @Configuration
    public class HttpMessageConfig {
        @Bean
        public HttpMessageConverters fastJsonHttpMessageConverter(){
            FastJsonHttpMessageConverter fastJsonHttpMessageConverter = 
                new FastJsonHttpMessageConverter();
        // fastJsonHttpMessageConverter通过封装FastjsonConfig配置全局
            return new HttpMessageConverters(fastJsonHttpMessageConverter);
        }
    }
    

2.2 FastJsonConfig

public void setCharset(Charset charset);
public void setSerializerFeatures(SerializerFeature... serializerFeatures); 序列化特性
public void setSerializeConfig(SerializeConfig serializeConfig); 序列化配置-个性化
public void setParserConfig(ParserConfig parserConfig); 反序列化配置
public void setSerializeFilters(SerializeFilter... serializeFilters); 序列化过滤器

2.3 SerializerFeature

名称 描述
QuoteFieldNames 输出key时是否使用双引号,默认为true
UseSingleQuotes 使用单引号而不是双引号,默认为false
WriteMapNullValue 是否输出值为null的字段,默认为false
WriteEnumUsingToString Enum输出name()或者original,默认为false
WriteEnumUsingName 用枚举name()输出
UseISO8601DateFormat Date使用ISO8601格式输出,默认为false
WriteNullListAsEmpty List字段如果为null,输出为[],而非null
WriteNullStringAsEmpty 字符类型字段如果为null,输出为”“,而非null
WriteNullNumberAsZero 数值字段如果为null,输出为0,而非null
WriteNullBooleanAsFalse Boolean字段如果为null,输出为false,而非null
SkipTransientField 如果是true,类中的Get方法对应的Field是transient,序列化时将会被忽略。默认为true
SortField 按字段名称排序后输出。默认为false
(过期)WriteTabAsSpecial 把\t做转义输出,默认为false
PrettyFormat 结果是否格式化,默认为false
WriteClassName 序列化时写入类型信息,默认为false。反序列化时需用到
DisableCircularReferenceDetect 消除对同一对象循环引用的问题,默认为false
WriteSlashAsSpecial 对斜杠’/’进行转义
BrowserCompatible 将中文都会序列化为\uXXXX格式,字节数会多一些,但是能兼容IE 6,默认为false
WriteDateUseDateFormat 全局修改日期格式,默认为false。JSON.DEFFAULT_DATE_FORMAT = “yyyy-MM-dd”;JSON.toJSONString(obj, SerializerFeature.WriteDateUseDateFormat);
(过期)DisableCheckSpecialChar 一个对象的字符串属性中如果有特殊字符如双引号,将会在转成json时带有反斜杠转移符。如果不需要转义,可以使用这个属性。默认为false

2.4 SerializeConfig

// API
public boolean put(Type type, ObjectSerializer value)
serializeConfig.propertyNamingStrategy = 
    PropertyNamingStrategy.CamelCase/PascalCase/...;

CamelCase策略,Java对象属性:personId,序列化后属性:persionId
PascalCase策略,Java对象属性:personId,序列化后属性:PersonId
SnakeCase策略,Java对象属性:personId,序列化后属性:person_id
KebabCase策略,Java对象属性:personId,序列化后属性:person-id

3.解决方案

3.1 重复引用

使用 SerializerFeature.DisableCircularReferenceDetect 来去除重复引用

@PostMapping("/users")
public @ResponseBody List<User> users(@RequestBody User user){
    //封装用户信息
    ...
    //封装信用卡信息
    List<IdCard> idCards = new ArrayList<>();
    idCards.add(idCard1);
    idCards.add(idCard2);
    user.setIdCards(idCards);
    user2.setIdCards(idCards);//两个user对象共用一个idCards集合

    List<User> userList = new ArrayList<>();
    userList.add(user);
    userList.add(user2);
    return userList;
}

[
    {
        "createTime": "2020/05/24 17:12:15",
        "id": 4,
        "idCards": [
            {  "balance": 20000.011,
                "birthday": "2020-05-24 17:12:15",
                "cardNo": "2002110" },
            {   "balance": 200.1271,
                "birthday": "2020-05-24 17:12:15",
                "cardNo": "2002120" }
        ],
        "username": ""
    },
    {
        "createTime": "2020/02/02 00:00:00",
        "id": 10,
        "idCards": [
            {  "$ref": "$[0].idCards[0]" },
            {   "$ref": "$[0].idCards[1]" }
        ],
        "username": "lisi"
    }
]
语法 描述
引用根对象
引用自己
引用父对象
引用父对象的父对象
基于路径的引用
fastJsonConfig.setSerializerFeatures(
    //去除重复引用
  SerializerFeature.DisableCircularReferenceDetect
)

如果能直接控制到序列化方法的话,可以

JSON.toJSONString(user,SerializerFeature.DisableCircularReferenceDetect);

3.2 BigDecimal类型设置

  1. public class BigDecimalSerializer implements ObjectSerializer {
        private final String pattern;
        public BigDecimalSerializer(String pattern){
            this.pattern = pattern;
        }
    
        @Override
        public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
            DecimalFormat decimalFormat = new DecimalFormat(pattern);
            SerializeWriter out = serializer.out;
            if (object == null) {
                out.write("0.00");
                return;
            }
    
            BigDecimal decimal = (BigDecimal) object;
            //
            String formatDecimal = decimalFormat.format(decimal);
            out.write(formatDecimal);
        }
    }
    
    serializeConfig.put(BigDecimal.class,new BigDecimalSerializer("#0.00"));
    

    发现并不可行。
    但是如果在IdCard中任意加一个@JSONField注解,并且有format属性,就可用了!

  2. 使用SerializeFilter处理

    FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
    SerializeConfig serializeConfig = new SerializeConfig();
    

    fastJsonConfig.setSerializeConfig(serializeConfig);

    PropertyFilter propertyFilter = new PropertyFilter() {
    @Override
    public boolean apply(Object object, String name, Object value) {
    if(value instanceof BigDecimal){
    return false;
    }
    return true;
    }
    };

    AfterFilter afterFilter = new AfterFilter() {
    @Override
    public void writeAfter(Object object) {
    Field[] fields = object.getClass().getDeclaredFields();
    for (Field field : fields) {
    if (field.getType() == BigDecimal.class) {
    field.setAccessible(true);
    Object value= null;
    try {
    value = (BigDecimal)field.get(object);
    value = ((BigDecimal) value).setScale(2,BigDecimal.ROUND_DOWN);
    } catch (IllegalAccessException e) {
    e.printStackTrace();
    }
    writeKeyValue(field.getName(), value );
    }
    }
    }
    };

    fastJsonConfig.setSerializeFilters(propertyFilter,afterFilter);
    fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);

    
    
  3. 使用SerializeFilter处理

    ValueFilter valueFilter = new ValueFilter() {
        @Override
        public Object process(Object object, String name, Object value) {
            if(value instanceof BigDecimal){
                value = ((BigDecimal)value).setScale(3,BigDecimal.ROUND_DOWN);
            }
            return value;
        }
    };
    fastJsonConfig.setSerializeFilters(valueFilter);
    

3.3 日期类型格式化

SerializeConfig serializeConfig = new SerializeConfig();
serializeConfig.put(Date.class,new SimpleDateFormatSerializer("yyyy-MM-dd HH:mm:ss"));

如果采用下面的全局配置方式,则会导致@JSONField失效

//配置全局日期处理,配置后@JSONField不再生效
fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm"); 

//设置全局ValueFilter,配置后@JSONField不再生效
ValueFilter valueFilter = (Object object, String name, Object value) -> {
    if(value == null){
        value = "";
    }
    if(value instanceof LocalDateTime){
        value = ((LocalDateTime)value).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    }
    if(value instanceof Date){
        value = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date)value);
    }

    return value;
};
fastJsonConfig.setSerializeFilters(valueFilter,...);
posted @ 2023-03-23 15:45  顔宸  阅读(43)  评论(0编辑  收藏  举报