[JSON/Java] 经典问题:Json序列化时is开头的属性序列化后“is”丢失 [转]

1 问题描述

  • 项目联调时下游接收方反应某个字段名称与接口定义文档上的定义名称不一样,查看发现该字段为boolean类型的isActiveAlarm,但传给下游接收方的json串里字段变为了activeAlarm,现象如下

  • 查看该实体类的定义,发现类中定义的字段确实为isActiveAlarm,与设计文档上相同,并非定义错误。因此猜测是在传给下游接收方前的JSON序列化时导致is丢失。

image

2 原因分析

  • Java Bean类的属性的类型是boolean类型,那么该属性的读方法的格式可以是isXxx()或者getXxx()

例如,名为stateboolean类型的属性,它的读方法可以是isState()或者是getState()

  • 阿里巴巴规范:
  • 【强制】POJO 类中的任何布尔类型的变量,都不要加 is 前缀,否则部分框架解析会引起序列化错误。
  • 反例:定义为基本数据类型 Boolean isDeleted 的属性,它的方法也是 isDeleted(),框架在反向解析的时候,“误以为”对应的属性名称是 deleted,导致属性获取不到,进而抛出异常。
  • 测试案例

下面看一个测试例子🌰,定义一个实体类TestBean,含有boolean的属性isSuccess 和 int类型的属性isXxx,使用Lombok框架来生成getter、setter和constructor。
测试方法分别使用Gson、Fastjson和Jackson来进行序列化,测试结果如下。

image

@Test
public void GsonBooleanTest() {
    TestBean testBean = new TestBean();
    testBean.setSuccess(true);
    testBean.setIsXxx(123);
 
    //Gson
    String gsonString = new Gson().toJson(testBean);
    System.out.println("Gson: " + gsonString);
 
    //fastjson
    String fastjsonString = JSON.toJSONString(testBean);
    System.out.println("Fastjson: " + fastjsonString);
 
    //Jackson
    String jacksonString = new ObjectMapper().writeValueAsString(testBean);
    System.out.println("Jackson: " + jacksonString);
}
 
@Data
class TestBean {
    private boolean isSuccess;
 
    private int isXxx;
}

可以看到,使用Gson序列化后的json串没有出现is丢失的问题,而jackson和fastjson均出现了is丢失的问题,Gson是根据类中属性进行序列化,所以结果没什么问题。
而Jackson和FastJson的序列化方式是先找到getter方法,再根据JavaBean规范生成对应的属性名,所以不仅isBooTest属性被序列化成booTest且testAtt这个类中根本不存在的属性也在序列化的结果中。

  • 测试案例
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.io.Serializable;

@Data
@ToString
@NoArgsConstructor
class MyDto implements Serializable {
    private Boolean isActiveAlarm;

    public Boolean isActiveAlarm(){//"activeAlarm"
        return isActiveAlarm;
    }

    public Boolean getIsActiveAlarm(){//"isActiveAlarm"
        return isActiveAlarm;
	}  

    public Boolean setIsActiveAlarm(Boolean isActiveAlarm){
        return this.isActiveAlarm = isActiveAlarm;
    }  
}

json序列化 (fastjson)

JSON.toJSONString( myDto )
    {"activeAlarm":false,"isActiveAlarm":false}

JSON.parseObject( "{\"isActiveAlarm\" : false, \"activeAlarm\":true }", MyDto.class)
	isActiveAlarm : false

JSON.parseObject( "{\"activeAlarm\":true }", MyDto.class)
	isActiveAlarm : null

3 解决方案

  • 经过测试发现: 对于jacksonfastjson会出现一下几种情况,而gson在这些情景下都可以正确的序列化 。
  1. is开头的非boolean类型字段,使用getIsXXX方法,序列化后字段名不变
  2. is开头的非boolean类型字段,使用isXXX方法,序列化之后消失
  3. is开头的boolean类型字段,使用getIsXXX方法,序列化之后字段名不变
  4. is开头的boolean类型字段,使用isXXX方法,序列化之后字段名前的is被去除
  • 若要避免该问题,有以下几种方法

方法1:bean的boolean属性设置时不要以is作为小驼峰;
方法2:不要使用@Data生成getter,应该使用快捷键生成,然后手动修改成getIsXxx()的形式;
方法3:使用Gson序列化对象;
方法4:bean的布尔类型属性设置为包装类型Boolean,而不要使用boolean。使用Boolean时@Data生成的getter和setter为getIsXxx(), setIsXxx()。

X 参考文献

posted @ 2025-08-18 13:42  千千寰宇  阅读(76)  评论(0)    收藏  举报