Jackson详解

3,478 阅读20分钟

一、背景

在实际开发中,我们经常会遇到对接口格式为json的入参和返参进行处理,比如返回的数据里对日期格式进行处理,数据中为null的不返回 等等,而咱们常用的序列化、反序列化为Jackson,所以此次咱们对Jackson的属性配置做些讲解:实际上Jackson提供了通过注解的方式对实例进行规则处理,也可以通过对 com.fasterxml.jackson.databindobjectMapper(该类序列化和反序列化处理) 的属性进行配置来处理,下面主要按这两块介绍:文章来源 点击跳转(感谢作者的分享)

二、Jackson的配置:

1、Jackson自动检测机制:

jackson默认的字段属性发现规则如下:

所有被public修饰的字段->所有被public修饰的getter->所有被public修饰的setter

若类中的一个private属性,且没有设置public的getter和setter方法,则对该类对象进行序列化时,默认不对这个private属性进行序列化。

若此时任然需要对该private属性进行序列化,可以通过设置自动检测功能来实现:

配置实现:

// 自动检测所有类的全部属性
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY) ;

//自动检测所有类的public getter方法
objectMapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.ANY) ;

//自动检测所有类的public setter方法
objectMapper.setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.ANY); 

@JsonAutoDetect(作用在类上)注解实现:

  • fieldVisibility:字段的可见级别

  • ANY:任何级别的字段都可以自动识别

  • NONE:所有字段都不可以自动识别

  • NON_PRIVATE:非private修饰的字段可以自动识别

  • PROTECTED_AND_PUBLIC:被protected和public修饰的字段可以被自动识别

  • PUBLIC_ONLY:只有被public修饰的字段才可以被自动识别

  • DEFAULT:同PUBLIC_ONLY

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)

2、Jackson序列化与反序列化的配置属性:

2.1、Jackson序列化的配置属性:

//这个特性,决定了解析器是否将自动关闭那些不属于parser自己的输入源。
// 如果禁止,则调用应用不得不分别去关闭那些被用来创建parser的基础输入流InputStream和reader;
//默认是true
objectMapper.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, true);
//是否允许解析使用Java/C++ 样式的注释(包括'/'+'*''//' 变量)
objectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);

//设置为true时,属性名称不带双引号
objectMapper.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, false);
//反序列化是是否允许属性名称不带双引号
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);

//是否允许单引号来包住属性名称和字符串值
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);

//是否允许JSON字符串包含非引号控制字符(值小于32ASCII字符,包含制表符和换行符)
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);

//是否允许JSON整数以多个0开始
objectMapper.configure(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS, true);

//null的属性不序列化
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

//按字母顺序排序属性,默认false
objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY,true);

//是否以类名作为根元素,可以通过@JsonRootName来自定义根元素名称,默认false
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE,true);

举例:
@JsonRootName("myPojo")
public static class TestPOJO{
        private String name;

        public String getName() {
                return name;
        }

        public void setName(String name) {
                this.name = name;
        }
}
 该类在序列化成json后类似如下:{"myPojo":{"name":"aaaa"}}
//是否缩放排列输出,默认false
objectMapper.configure(SerializationFeature.INDENT_OUTPUT,false);

举例:

如果一个类中有ab、c、d四个可检测到的属性,那么序列化后的json输出类似下面:

{
  "a" : "aaa",
  "b" : "bbb",
  "c" : "ccc",
  "d" : "ddd"
}
//序列化Date日期时以timestamps输出,默认true
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,true);

比如如果一个类中有private Date date;这种日期属性,序列化后为:{"date" : 1413800730456},若不为true,则为{"date" : "2014-10-20T10:26:06.604+0000"}
//序列化枚举是否以toString()来输出,默认false,即默认以name()来输出
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);

//序列化枚举是否以ordinal()来输出,默认false
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_INDEX,false);
举例:
@Test
public void enumTest() throws Exception {
        TestPOJO testPOJO = new TestPOJO();
        testPOJO.setName("myName");
        testPOJO.setMyEnum(TestEnum.ENUM01);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING,false);
        objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_INDEX,false);
        String jsonStr1 = objectMapper.writeValueAsString(testPOJO);
        Assert.assertEquals("{\"myEnum\":\"ENUM01\",\"name\":\"myName\"}",jsonStr1);

        ObjectMapper objectMapper2 = new ObjectMapper();
        objectMapper2.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING,true);
        String jsonStr2 = objectMapper2.writeValueAsString(testPOJO);
        Assert.assertEquals("{\"myEnum\":\"enum_01\",\"name\":\"myName\"}",jsonStr2);

        ObjectMapper objectMapper3 = new ObjectMapper();
        objectMapper3.configure(SerializationFeature.WRITE_ENUMS_USING_INDEX,true);
        String jsonStr3 = objectMapper3.writeValueAsString(testPOJO);
        Assert.assertEquals("{\"myEnum\":0,\"name\":\"myName\"}",jsonStr3);
}
public static class TestPOJO{
        TestPOJO(){}
        private TestEnum myEnum;
        private String name;

        //getters、setters省略
}

public static enum TestEnum{
        ENUM01("enum_01"),ENUM02("enum_01"),ENUM03("enum_01");

        private String title;

        TestEnum(String title) {
                this.title = title;
        }

        @Override
        public String toString() {
                return title;
        }
}


//序列化单元素数组时不以数组来输出,默认false
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING,true);
//序列化Map时对key进行排序操作,默认false
objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS,true);

举例:
@Test
public void orderMapBykey() throws Exception {
        TestPOJO testPOJO = new TestPOJO();
        testPOJO.setName("myName");
        Map<String,Integer> counts = new HashMap<>();
        counts.put("a",1);
        counts.put("d",4);
        counts.put("c",3);
        counts.put("b",2);
        counts.put("e",5);
        testPOJO.setCounts(counts);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS,false);
        String jsonStr1 = objectMapper.writeValueAsString(testPOJO);
        Assert.assertEquals("{\"name\":\"myName\",\"counts\":{\"d\":4,\"e\":5,\"b\":2,\"c\":3,\"a\":1}}",jsonStr1);

        ObjectMapper objectMapper2 = new ObjectMapper();
        objectMapper2.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS,true);
        String jsonStr2 = objectMapper2.writeValueAsString(testPOJO);
        Assert.assertEquals("{\"name\":\"myName\",\"counts\":{\"a\":1,\"b\":2,\"c\":3,\"d\":4,\"e\":5}}",jsonStr2);
}

public static class TestPOJO{
        private String name;
        private Map<String,Integer> counts;

        //getters、setters省略
}


//序列化char[]时以json数组输出,默认false
objectMapper.configure(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS,true);
//序列化BigDecimal时是输出原始数字还是科学计数,默认false,即以toPlainString()科学计数方式来输出
objectMapper.configure(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN,true);

举例:
    @Test
    public void bigDecimalAsPlain() throws Exception {
            TestPOJO testPOJO = new TestPOJO();
            testPOJO.setName("myName");
            testPOJO.setCount(new BigDecimal("1e20"));

            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.configure(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN,false);
            String jsonStr1 = objectMapper.writeValueAsString(testPOJO);
            Assert.assertEquals("{\"name\":\"myName\",\"count\":1E+20}",jsonStr1);

            ObjectMapper objectMapper2 = new ObjectMapper();
            objectMapper2.configure(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN,true);
            String jsonStr2 = objectMapper2.writeValueAsString(testPOJO);
            Assert.assertEquals("{\"name\":\"myName\",\"count\":100000000000000000000}",jsonStr2);
    }

2.2、Jackson反序列化的配置属性:

//当遇到未知属性(没有映射到属性,没有任何setter或者任何可以处理它的handler,是否应该抛出JsonMappingException异常
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

//该特性决定对于json浮点数,是否使用BigDecimal来序列化。如果不允许,则使用Double序列化。 默认为false
objectMapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, false);

//该特性决定对于json整形(非浮点),是否使用BigInteger来序列化。如果不允许,则根据数值大小来确定 是使用Integer或者Long
objectMapper.configure(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS, false);

//该特性决定JSON ARRAY是映射为Object[]还是List<Object>。如果开启,都为Object[],false时,则使用List  默认为false
objectMapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, false);

//是否使用Enum.toString()的值对json string进行反序列化。这个的设置和WRITE_ENUMS_USING_TO_STRING需要一致。
objectMapper.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true);

3、Jackson常用的注解:

3.1、@JsonIgnore

该注解作用在字段或方法上,用来完全忽略被注解的字段和方法对应的属性,即便这个字段或方法可以被自动检测到或者还有其他的注解,当@JsonIgnore不管注解在getters上还是setters上都会忽略对应的属性.

public class TestVo {

    private int id;
    @JsonIgnore
    private String name;
    private int age;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }
    @JsonIgnore
    public void setAge(int age) {
        this.age = age;
    }
}


@Test
    public void getJsonIgnoreUse() throws IOException {
        TestVo testVo = new TestVo();
        testVo.setId(111);
        testVo.setName("小明");
        testVo.setAge(22);

        ObjectMapper objectMapper = new ObjectMapper();
        String jsonStr = objectMapper.writeValueAsString(testVo);
        Assert.assertEquals("{\"id\":111}",jsonStr);

        String jsonStr2 = "{\"id\":111,\"name\":\"小明\",\"age\":22}";
        TestVo testVo1 = objectMapper.readValue(jsonStr2, TestVo.class);
        Assert.assertEquals(111,testVo1.getId());
        Assert.assertNull(testVo1.getName());
        Assert.assertEquals(0,testVo1.getAge());
    }

3.2、@JsonProperty

该注解作用在字段或方法上,用来对属性的序列化/反序列化,可以用来避免遗漏属性,同时提供对属性名称重命名,比如在很多场景下Java对象的属性是按照规范的驼峰书写,但是实际展示的却是类似C-style或C++/Microsolft style

public class TestJsonPropertyVo {
    //注意这里必须得有该注解,因为没有提供对应的getId和setId函数,而是其他的getter和setter,防止遗漏该属性
    @JsonProperty
    private int id;
    @JsonProperty("first_name")
    private String firstName;

    public int testJsonProperty() {
        return id;
    }

    public void testJsonProperty(int id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

}

@Test
    public void testJsonPropertyUse() throws IOException {
        TestJsonPropertyVo testJsonPropertyVo = new TestJsonPropertyVo();
        testJsonPropertyVo.testJsonProperty(111);
        testJsonPropertyVo.setFirstName("小明");

        ObjectMapper objectMapper = new ObjectMapper();
        String jsonStr = objectMapper.writeValueAsString(testJsonPropertyVo);
        Assert.assertEquals("{\"id\":111,\"first_name\":\"小明\"}",jsonStr);

        String jsonStr2 = "{\"id\":111,\"first_name\":\"小明\"}";
        TestJsonPropertyVo testPOJO2 = objectMapper.readValue(jsonStr2, TestJsonPropertyVo.class);
        Assert.assertEquals(111, testPOJO2.testJsonProperty());
        Assert.assertEquals("小明", testPOJO2.getFirstName());
    }

3.3、@JsonIgnoreProperties

该注解作用在类上,用来说明有些属性在序列化/反序列化时需要忽略掉,可以将它看做是@JsonIgnore的批量操作,但它的功能比@JsonIgnore要强,比如一个类是代理类,我们无法将@JsonIgnore标记在属性或方法上,此时便可用@JsonIgnoreProperties标注在类声明上,它还有一个重要的功能是作用在反序列化时解析字段时过滤一些未知的属性,否则通常情况下解析到我们定义的类不认识的属性便会抛出异常。

可以注明是想要忽略的属性列表如@JsonIgnoreProperties({"name","age","title"})

也可以注明过滤掉未知的属性如@JsonIgnoreProperties(ignoreUnknown=true)

@JsonIgnoreProperties({"name","age","title"})
public class TestJsonIgnorePropertiesVo {
    private int id;
    private String name;
    private int age;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
@Test
    public void testJsonIgnorePropertiesUse() throws IOException {
        TestJsonIgnorePropertiesVo testJsonIgnorePropertiesVo = new TestJsonIgnorePropertiesVo();
        testJsonIgnorePropertiesVo.setId(111);
        testJsonIgnorePropertiesVo.setName("小明");
        testJsonIgnorePropertiesVo.setAge(22);
        ObjectMapper objectMapper = new ObjectMapper();
        String jsonStr = objectMapper.writeValueAsString(testJsonIgnorePropertiesVo);
        Assert.assertEquals("{\"id\":111}",jsonStr);//name和age被忽略掉了

        String jsonStr2 = "{\"id\":111,\"name\":\"myName\",\"age\":22,\"title\":\"myTitle\"}";
        TestJsonIgnorePropertiesVo testJsonIgnorePropertiesVo1 = objectMapper.readValue(jsonStr2, TestJsonIgnorePropertiesVo.class);
        Assert.assertEquals(111, testJsonIgnorePropertiesVo1.getId());
        Assert.assertNull(testJsonIgnorePropertiesVo1.getName());
        Assert.assertEquals(0,testJsonIgnorePropertiesVo1.getAge());
        String jsonStr3 = "{\"id\":111,\"name\":\"小明\",\"count\":33}";//这里有个未知的count属性,反序列化会报错
        objectMapper.readValue(jsonStr3, TestJsonIgnorePropertiesVo.class);
    }

如果将上面的@JsonIgnoreProperties({"name","age","title"}) 更换为@JsonIgnoreProperties(ignoreUnknown=true)那么测试用例中在反序列化未知的count属性时便不会抛出异常了.

3.4、@JsonUnwrapped

该注解作用在属性字段或方法上,用来将子JSON对象的属性添加到封闭的JSON对象.

public class TestJsonUnwrappedVo1 {
    private int id;
    @JsonUnwrapped
    private TestJsonUnwrappedVo2 name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public TestJsonUnwrappedVo2 getName() {
        return name;
    }

    public void setName(TestJsonUnwrappedVo2 name) {
        this.name = name;
    }
}

public class TestJsonUnwrappedVo2 {

    private String firstName;
    private String secondName;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getSecondName() {
        return secondName;
    }

    public void setSecondName(String secondName) {
        this.secondName = secondName;
    }
}

@Test
    public void testJsonUnwrappedUse() throws Exception {
        TestJsonUnwrappedVo1 testJsonUnwrappedVo1 = new TestJsonUnwrappedVo1();
        testJsonUnwrappedVo1.setId(111);
        TestJsonUnwrappedVo2 testJsonUnwrappedVo2 = new TestJsonUnwrappedVo2();
        testJsonUnwrappedVo2.setFirstName("张");
        testJsonUnwrappedVo2.setSecondName("三");
        testJsonUnwrappedVo1.setName(testJsonUnwrappedVo2);
        ObjectMapper objectMapper = new ObjectMapper();
        String jsonStr = objectMapper.writeValueAsString(testJsonUnwrappedVo1);
        //如果没有@JsonUnwrapped,序列化后将为{"id":111,"name":{"firstName":"张","secondName":"三"}}
        //因为在name属性上加了@JsonUnwrapped,所以name的子属性firstName和secondName将不会包含在name中。
        Assert.assertEquals("{\"id\":111,\"firstName\":\"张\",\"secondName\":\"三\"}",jsonStr);
        String jsonStr2 = "{\"id\":111,\"firstName\":\"张\",\"secondName\":\"三\"}";
        TestJsonUnwrappedVo1 testPOJO2 = objectMapper.readValue(jsonStr2,TestJsonUnwrappedVo1.class);
        Assert.assertEquals(111,testPOJO2.getId());
        Assert.assertEquals("张",testPOJO2.getName().getFirstName());
        Assert.assertEquals("三",testPOJO2.getName().getSecondName());
    }

在2.0+版本中@JsonUnwrapped添加了prefix和suffix属性,用来对字段添加前后缀,这在有关属性分组上比较有用,在上面的测试用例中,如果我们将TestPOJO的name属性上的@JsonUnwrapped添加前后缀配置,即@JsonUnwrapped(prefix = "name_",suffix = "_test")那么TestPOJO序列化后将为{"id":111,"name_firstName_test":"张","name_secondName_test":"三"},反序列化时也要加上前后缀才会被解析为POJO.

public class TestJsonUnwrappedVo1 {

    private int id;
    @JsonUnwrapped(prefix = "name_",suffix = "_test")
    private TestJsonUnwrappedVo2 name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public TestJsonUnwrappedVo2 getName() {
        return name;
    }

    public void setName(TestJsonUnwrappedVo2 name) {
        this.name = name;
    }
}

public class TestJsonUnwrappedVo2 {

    private String firstName;
    private String secondName;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getSecondName() {
        return secondName;
    }

    public void setSecondName(String secondName) {
        this.secondName = secondName;
    }
}
@Test
    public void testJsonUnwrappedUse() throws Exception {
        TestJsonUnwrappedVo1 testJsonUnwrappedVo1 = new TestJsonUnwrappedVo1();
        testJsonUnwrappedVo1.setId(111);
        TestJsonUnwrappedVo2 testJsonUnwrappedVo2 = new TestJsonUnwrappedVo2();
        testJsonUnwrappedVo2.setFirstName("张");
        testJsonUnwrappedVo2.setSecondName("三");
        testJsonUnwrappedVo1.setName(testJsonUnwrappedVo2);
        ObjectMapper objectMapper = new ObjectMapper();
        String jsonStr = objectMapper.writeValueAsString(testJsonUnwrappedVo1);
        //如果没有@JsonUnwrapped,序列化后将为{"id":111,"name":{"firstName":"张","secondName":"三"}}
        //因为在name属性上加了@JsonUnwrapped,所以name的子属性firstName和secondName将不会包含在name中。
        Assert.assertEquals("{\"id\":111,\"name_firstName_test\":\"张\",\"name_secondName_test\":\"三\"}",jsonStr);
        String jsonStr2 = "{\"id\":111,\"name_firstName_test\":\"张\",\"name_secondName_test\":\"三\"}";
        TestJsonUnwrappedVo1 testPOJO2 = objectMapper.readValue(jsonStr2,TestJsonUnwrappedVo1.class);
        Assert.assertEquals(111,testPOJO2.getId());
        Assert.assertEquals("张",testPOJO2.getName().getFirstName());
        Assert.assertEquals("三",testPOJO2.getName().getSecondName());
    }

3.5、@JsonIdentityInfo

2.0+版本新注解,作用于类或属性上,被用来在序列化/反序列化时为该对象或字段添加一个对象识别码,通常是用来解决循环嵌套的问题,比如数据库中的多对多关系,通过配置属性generator来确定识别码生成的方式,有简单的,配置属性property来确定识别码的名称,识别码名称没有限制。 对象识别码可以是虚拟的,即存在在JSON中,但不是POJO的一部分,这种情况下我们可以如此使用注解 @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class,property = "@id")

对象识别码也可以是真实存在的,即以对象的属性为识别码,通常这种情况下我们一般以id属性为识别码,可以这么使用注解 @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id")

@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class,property = "@id")
public class TestJsonIdentityInfoVo1 {

    private String name;
    private TestJsonIdentityInfoVo2[] children;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public TestJsonIdentityInfoVo2[] getChildren() {
        return children;
    }

    public void setChildren(TestJsonIdentityInfoVo2[] children) {
        this.children = children;
    }
}
public class TestJsonIdentityInfoVo2 {

    private String name;
    private TestJsonIdentityInfoVo1 parent;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public TestJsonIdentityInfoVo1 getParent() {
        return parent;
    }

    public void setParent(TestJsonIdentityInfoVo1 parent) {
        this.parent = parent;
    }
}
@Test
    public void testJsonIdentityInfoUser() throws Exception {
        TestJsonIdentityInfoVo1 parent = new TestJsonIdentityInfoVo1();
        parent.setName("jack");
        TestJsonIdentityInfoVo2 child = new TestJsonIdentityInfoVo2();
        child.setName("mike1");

        TestJsonIdentityInfoVo2 child2 = new TestJsonIdentityInfoVo2();
        child2.setName("mike2");

        TestJsonIdentityInfoVo2[] children = new TestJsonIdentityInfoVo2[]{child,child2};
        parent.setChildren(children);
        child.setParent(parent);
        child2.setParent(parent);
        ObjectMapper objectMapper = new ObjectMapper();
        String jsonStr = objectMapper.writeValueAsString(parent);
        System.out.println(jsonStr);
        Assert.assertEquals("{\"@id\":1,\"name\":\"jack\",\"children\":[{\"name\":\"mike1\",\"parent\":1},{\"name\":\"mike2\",\"parent\":1}]}",jsonStr);
    }

3.5、@JsonNaming

jackson 2.1+版本的注解,作用于类或方法,注意这个注解是在jackson-databind包中而不是在jackson-annotations包里,它可以让你定制属性命名策略,作用和前面提到的@JsonProperty的重命名属性名称相同。比如 你有一个JSON串{"in_reply_to_user_id":"abc123"},需要反序列化为POJO,POJO一般情况下则需要如此写

	public static class TestPOJO{
 
		private String in_reply_to_user_id;
 
		public String getIn_reply_to_user_id() {
			return in_reply_to_user_id;
		}
 
		public void setIn_reply_to_user_id(String in_reply_to_user_id) {
			this.in_reply_to_user_id = in_reply_to_user_id;
		}
	}

但这显然不符合JAVA的编码规范,你可以用@JsonProperty,比如:

	public static class TestPOJO{
 
		@JsonProperty("in_reply_to_user_id")
		private String inReplyToUserId;
 
		public String getInReplyToUserId() {
			return inReplyToUserId;
		}
 
		public void setInReplyToUserId(String inReplyToUserId) {
			this.inReplyToUserId = inReplyToUserId;
		}
	}

这样就符合规范了,可是如果POJO里有很多属性,给每个属性都要加上@JsonProperty是多么繁重的工作,这里就需要用到@JsonNaming了,它不仅能制定统一的命名规则,还能任意按自己想要的方式定制

	@Test
	public void jsonNaming() throws Exception{
		String jsonStr = "{\"in_reply_to_user_id\":\"abc123\"}";
		ObjectMapper objectMapper = new ObjectMapper();
		TestPOJO testPOJO = objectMapper.readValue(jsonStr,TestPOJO.class);
		Assert.assertEquals("abc123",testPOJO.getInReplyToUserId());
 
		TestPOJO testPOJO2 = new TestPOJO();
		testPOJO2.setInReplyToUserId("abc123");
		String jsonStr2 = objectMapper.writeValueAsString(testPOJO2);
		Assert.assertEquals(jsonStr,jsonStr2);
	}
 
	@JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)
	public static class TestPOJO{
 
		private String inReplyToUserId;
 
		public String getInReplyToUserId() {
			return inReplyToUserId;
		}
 
		public void setInReplyToUserId(String inReplyToUserId) {
			this.inReplyToUserId = inReplyToUserId;
		}
	}

@JsonNaming使用了jackson已经实现的PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy,它可以将大写转换为小写并添加下划线。你可以自定义,必须继承类PropertyNamingStrategy,建议继承PropertyNamingStrategyBase,我们自己实现一个类似LowerCaseWithUnderscoresStrategy的策略,只是将下划线改为破折号

	@Test
	public void jsonNaming() throws Exception{
		String jsonStr = "{\"in-reply-to-user-id\":\"abc123\"}";
		ObjectMapper objectMapper = new ObjectMapper();
		TestPOJO testPOJO = objectMapper.readValue(jsonStr,TestPOJO.class);
		Assert.assertEquals("abc123", testPOJO.getInReplyToUserId());
 
		TestPOJO testPOJO2 = new TestPOJO();
		testPOJO2.setInReplyToUserId("abc123");
		String jsonStr2 = objectMapper.writeValueAsString(testPOJO2);
		Assert.assertEquals(jsonStr, jsonStr2);
	}
 
	@JsonNaming(MyPropertyNamingStrategy.class)
	public static class TestPOJO{
 
		private String inReplyToUserId;
 
		public String getInReplyToUserId() {
			return inReplyToUserId;
		}
 
		public void setInReplyToUserId(String inReplyToUserId) {
			this.inReplyToUserId = inReplyToUserId;
		}
	}
 
	public static class MyPropertyNamingStrategy extends PropertyNamingStrategy.PropertyNamingStrategyBase {
		@Override
		public String translate(String input) {
			if (input == null) return input; // garbage in, garbage out
			int length = input.length();
			StringBuilder result = new StringBuilder(length * 2);
			int resultLength = 0;
			boolean wasPrevTranslated = false;
			for (int i = 0; i < length; i++)
			{
				char c = input.charAt(i);
				if (i > 0 || c != '-') // skip first starting underscore
				{
					if (Character.isUpperCase(c))
					{
						if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '-')
						{
							result.append('-');
							resultLength++;
						}
						c = Character.toLowerCase(c);
						wasPrevTranslated = true;
					}
					else
					{
						wasPrevTranslated = false;
					}
					result.append(c);
					resultLength++;
				}
			}
			return resultLength > 0 ? result.toString() : input;
		}
	}

如果你想让自己定制的策略对所有解析都实现,除了对每个具体的实体类对应的位置加上@JsonNaming外你还可以如下做全局配置:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setPropertyNamingStrategy(new MyPropertyNamingStrategy());

4、用于序列化和反序列化的注解类

4.1、@JsonSerialize和@JsonDeserialize

作用于方法和字段上,通过 using(JsonSerializer)和using(JsonDeserializer)来指定序列化和反序列化的实现,通常我们在需要自定义序列化和反序列化时会用到,比如下面的例子中对日期类型的字符处理

	@Test
	public void jsonSerializeAndDeSerialize() throws Exception {
		TestPOJO testPOJO = new TestPOJO();
		testPOJO.setName("myName");
		testPOJO.setBirthday(new Date());
		ObjectMapper objectMapper = new ObjectMapper();
		String jsonStr = objectMapper.writeValueAsString(testPOJO);
		System.out.println(jsonStr);
 
		String jsonStr2 = "{\"name\":\"myName\",\"birthday\":\"2014-11-11 19:01:58\"}";
		TestPOJO testPOJO2 = objectMapper.readValue(jsonStr2,TestPOJO.class);
		System.out.println(testPOJO2.toString());
	}
 
	public static class TestPOJO{
		private String name;
		@JsonSerialize(using = MyDateSerializer.class)
		@JsonDeserialize(using = MyDateDeserializer.class)
		private Date birthday;
 
		//getters、setters省略
 
		@Override
		public String toString() {
			return "TestPOJO{" +
				"name='" + name + '\'' +
				", birthday=" + birthday +
				'}';
		}
	}
 
	private static class MyDateSerializer extends JsonSerializer<Date>{
		@Override
		public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
			DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			String dateStr = dateFormat.format(value);
			jgen.writeString(dateStr);
		}
	}
 
	private static class MyDateDeserializer extends JsonDeserializer<Date>{
		@Override
		public Date deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
			String value = jp.getValueAsString();
			DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			try {
				return dateFormat.parse(value);
			} catch (ParseException e) {
				e.printStackTrace();
			}
			return null;
		}
	}

4.2、@JsonPropertyOrder

作用在类上,被用来指明当序列化时需要对属性做排序,它有2个属性一个是alphabetic:布尔类型,表示是否采用字母拼音顺序排序,默认是为false,即不排序.

@Test
	public void jsonPropertyOrder() throws Exception {
		TestPOJO testPOJO = new TestPOJO();
		testPOJO.setA("1");
		testPOJO.setB("2");
		testPOJO.setC("3");
		testPOJO.setD("4");
		ObjectMapper objectMapper = new ObjectMapper();
		String jsonStr = objectMapper.writeValueAsString(testPOJO);
		Assert.assertEquals("{\"a\":\"1\",\"c\":\"3\",\"d\":\"4\",\"b\":\"2\"}",jsonStr);
	}
 
	public static class TestPOJO{
		private String a;
		private String c;
		private String d;
		private String b;
 
		//getters、setters省略
	}

我们先看一个默认的排序方式,序列化单元测试结果依次为{"a":"1","c":"3","d":"4","b":"2"},即是没有经过排序操作的,在TestPOJO上加上@jsonPropertyOrder(alphabetic = true)再执行测试结果将会为{"a":"1","b":"2","c":"3","d":"4"} 还有一个属性是value:数组类型,表示将优先其他属性排序的属性名称.

@Test
	public void jsonPropertyOrder() throws Exception {
		TestPOJO testPOJO = new TestPOJO();
		testPOJO.setA("1");
		testPOJO.setB("2");
		testPOJO.setC("3");
		testPOJO.setD("4");
		ObjectMapper objectMapper = new ObjectMapper();
		String jsonStr = objectMapper.writeValueAsString(testPOJO);
		System.out.println(jsonStr);
		Assert.assertEquals("{\"c\":\"3\",\"b\":\"2\",\"a\":\"1\",\"d\":\"4\"}",jsonStr);
	}
 
	@JsonPropertyOrder(alphabetic = true,value = {"c","b"})
	public static class TestPOJO{
		private String a;
		private String c;
		private String d;
		private String b;
 
		//getters、setters省略
	}

上面例子可以看到value指定了c和b属性优先排序,所以序列化后为{"c":"3","b":"2","a":"1","d":"4"} 还记得本文上面最开始配置MapperFeature时也有属性排序么,对,就是objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY,true);只不过@JsonPropertyOrder颗粒度要更细一点,可以决定哪些属性优先排序.

4.3、@JsonView

视图模板,作用于方法和属性上,用来指定哪些属性可以被包含在JSON视图中,在前面我们知道已经有@JsonIgnore@JsonIgnoreProperties可以排除过滤掉不需要序列化的属性,可是如果一个POJO中有上百个属性,比如订单类、商品详情类这种属性超多,而我们可能只需要概要简单信息即序列化时只想输出其中几个或10几个属性,此时使用@JsonIgnore@JsonIgnoreProperties就显得非常繁琐,而使用@JsonView便会非常方便,只许在你想要输出的属性(或对应的getter)上添加@JsonView即可,举例:

  • 在查询列表请求中,不返回password字段
  • 在获取用户详情中,返回password字段

User对象定义:定义了两个视图接口 UserSimpleView 和 UserDetailView, 其中UserDetailView继承UserSimpleView,UserDetailView拥有视图UserSimpleView的属性,在相应的get方法或属性上声明JsonView,

	public class User{

    /**
     * 用户简单视图
     */
    public interface UserSimpleView{};

    /**
     * 用户详情视图
     */
    public interface UserDetailView extends UserSimpleView{};

    private String username;

    private String password;

    private String age;

    @JsonView(UserSimpleView.class)
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @JsonView(UserDetailView.class)
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @JsonView(UserSimpleView.class)
    public String getAge() {
        return age;
    }
    
    public void setAge(String age) {
        this.age = age;
    }
}

Controller的定义:

@RestController
@RequestMapping("/user")
public class UserController{
    @GetMapping
    @JsonView(User.SimpleView.class)
    public List<User> queryList(User user,
                //给分页对象设置默认值
                @PageableDefault(page = 2, size = 2, sort = "username,asc")  Pageable pageable)
    {
        System.out.println(user);
        List<User> list = new ArrayList<>();
        list.add(user);
        return list;
    }

    @GetMapping("/{id:\\d+}")  //正则表达式, 参数必须是全数字
    @JsonView(User.DetailView.class)
    public User getInfo(@PathVariable(name = "id") Integer userId){
        User user = new User();
        user.setId(userId);
        user.setUsername("Tom");
        return user;
    }
}

测试:

4.4、@JsonFilter

Json属性过滤器,作用于类,作用同上面的@JsonView,都是过滤掉不想要的属性,输出自己想要的属性。和@FilterView不同的是@JsonFilter可以动态的过滤属性,比如我不想输出以system开头的所有属性等待,应该说@JsonFilter更高级一点。

@Test
	public void jsonFilter() throws Exception {
		TestPOJO testPOJO = new TestPOJO();
		testPOJO.setA("1");
		testPOJO.setB("2");
		testPOJO.setC("3");
		testPOJO.setD("4");
		ObjectMapper objectMapper = new ObjectMapper();
		FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter",SimpleBeanPropertyFilter.filterOutAllExcept("a"));
		objectMapper.setFilters(filters);
		String jsonStr = objectMapper.writeValueAsString(testPOJO);
		Assert.assertEquals("{\"a\":\"1\"}",jsonStr);
	}
 
	@JsonFilter("myFilter")
	public static class TestPOJO{
		private String a;
		private String c;
		private String d;
		private String b;
 
		//getters、setters省略
	}

上面例子中在我们想要序列化的POJO上加上了@JsonFilter,表示该类将使用名为myFilter的过滤器。在测试中定义了一个名为myFilter的SimpleFilterProvider,这个过滤器将会过滤掉所有除a属性以外的属性。这只是最简单的输出指定元素的例子,你可以自己实现FilterProvider来满足你的过滤需求。 有时候我们可能需要根据现有的POJO来过滤属性,而这种情况下通常不会让你修改已有的代码在POJO上加注解,这种情况下我们就可以结合@JsonFilterMixInAnnotations来实现过滤属性,如下例所示:

	@Test
	public void jsonFilter() throws Exception {
		TestPOJO testPOJO = new TestPOJO();
		testPOJO.setA("1");
		testPOJO.setB("2");
		testPOJO.setC("3");
		testPOJO.setD("4");
		ObjectMapper objectMapper = new ObjectMapper();
		FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter",SimpleBeanPropertyFilter.filterOutAllExcept("a"));
		objectMapper.setFilters(filters);
		objectMapper.addMixInAnnotations(TestPOJO.class,MyFilterMixIn.class);
		String jsonStr = objectMapper.writeValueAsString(testPOJO);
		Assert.assertEquals("{\"a\":\"1\"}",jsonStr);
	}
 
	public static class TestPOJO{
		private String a;
		private String c;
		private String d;
		private String b;
		//getters、setters省略
	}
 
	@JsonFilter("myFilter")
	private static interface MyFilterMixIn{
	}

4.5、@JsonIgnoreType

作用于类,表示被注解该类型的属性将不会被序列化和反序列化,也跟上面几个一样属于过滤属性功能的注解,举例:

	@Test
	public void jsonFilter() throws Exception {
		TestPOJO testPOJO = new TestPOJO();
		testPOJO.setName("myName");
		Sub1 sub1 = new Sub1();
		sub1.setId(1);
		sub1.setName("sub1");
		Sub2 sub2 = new Sub2();
		sub2.setId(2);
		sub2.setAge(22);
		testPOJO.setMyIn(sub1);
		testPOJO.setSub1(sub1);
		testPOJO.setSub2(sub2);
		ObjectMapper objectMapper = new ObjectMapper();
		String jsonStr = objectMapper.writeValueAsString(testPOJO);
		System.out.println(jsonStr);
	}
 
	public static class TestPOJO{
		private Sub1 sub1;
		private Sub2 sub2;
		private MyIn myIn;
		private String name;
		//getters、setters省略
	}
 
	public static class MyIn{
		private int id;
		//getters、setters省略
	}
 
	@JsonIgnoreType
	public static class Sub1 extends MyIn{
		private String name;
		//getters、setters省略
	}
 
	@JsonIgnoreType
	public static class Sub2 extends MyIn{
		private int age;
		//getters、setters省略
	}

上面例子中我们在类Sub1和Sub2上都加上了@JsonIgnoreType,那么需要序列化和反序列时POJO中所有Sub1和Sub2类型的属性都将会被忽略,上面测试结果为{"myIn":{"id":1,"name":"sub1"},"name":"myName"},只输出了name和myIn属性。需要注意的是@JsonIgnoreType是可以继承的,即如果在基类上添加了该注解,那么子类也相当于加了该注解。在上例中,如果只在基类MyIn上添加@JsonIgnoreType那么序列化TestPOJO时将会过滤掉MyIn、Sub1、Sub2。输出结果为{"name":"myName"}

4.6、@JsonAnySetter

作用于方法,在反序列化时用来处理遇到未知的属性的时候调用,在本文前面我们知道可以通过注解@JsonIgnoreProperties(ignoreUnknown=true)来过滤未知的属性,但是如果需要这些未知的属性该如何是好?那么@JsonAnySetter就可以派上用场了,它通常会和map属性配合使用用来保存未知的属性,举例:

	@Test
	public void jsonAnySetter() throws Exception {
		ObjectMapper objectMapper = new ObjectMapper();
		String jsonStr = "{\"name\":\"myName\",\"code\":\"12345\",\"age\":12}";
		TestPOJO testPOJO = objectMapper.readValue(jsonStr,TestPOJO.class);
		Assert.assertEquals("myName",testPOJO.getName());
		Assert.assertEquals("12345",testPOJO.getOther().get("code"));
		Assert.assertEquals(12,testPOJO.getOther().get("age"));
	}
 
	public static class TestPOJO{
		private String name;
 
		private Map other = new HashMap();
 
		@JsonAnySetter
		public void set(String name,Object value) {
			other.put(name,value);
		}
 
		//getters、setters省略
	}

测试用例中我们在set方法上标注了@JsonAnySetter,每当遇到未知的属性时都会调用该方法

4.7、@JsonCreator

作用于方法,通常用来标注构造方法或静态工厂方法上,使用该方法来构建实例,默认的是使用无参的构造方法,通常是和@JsonProperty或@JacksonInject配合使用,举例:

	@Test
	public void jsonCreator() throws Exception {
		ObjectMapper objectMapper = new ObjectMapper();
		String jsonStr = "{\"full_name\":\"myName\",\"age\":12}";
		TestPOJO testPOJO = objectMapper.readValue(jsonStr,TestPOJO.class);
		Assert.assertEquals("myName",testPOJO.getName());
		Assert.assertEquals(12, testPOJO.getAge());
	}
 
	public static class TestPOJO{
		private String name;
		private int age;
 
		@JsonCreator
		public TestPOJO(@JsonProperty("full_name") String name,@JsonProperty("age") int age){
			this.name = name;
			this.age = age;
		}
		public String getName() {
			return name;
		}
		public int getAge() {
			return age;
		}
	}

上面示例中是在构造方法上标注了@JsonCreator,同样你也可以标注在静态工厂方法上,比如:

	@Test
	public void jsonCreator() throws Exception {
		ObjectMapper objectMapper = new ObjectMapper();
		String jsonStr = "{\"name\":\"myName\",\"birthday\":1416299461556}";
		TestPOJO testPOJO = objectMapper.readValue(jsonStr,TestPOJO.class);
		Assert.assertEquals("myName",testPOJO.getName());
		System.out.println(testPOJO.getBirthday());
	}
 
	public static class TestPOJO{
		private String name;
		private Date birthday;
 
		private TestPOJO(String name,Date birthday){
			this.name = name;
			this.birthday = birthday;
		}
 
		@JsonCreator
		public static TestPOJO getInstance(@JsonProperty("name") String name,@JsonProperty("birthday") long timestamp){
			Date date = new Date(timestamp);
			return new TestPOJO(name,date);
		}
 
		public String getName() {
			return name;
		}
 
		public Date getBirthday() {
			return birthday;
		}
	}

这个实例中,TestPOJO的构造方法是私有的,外面无法new出来该对象,只能通过工厂方法getInstance来构造实例,此时@JsonCreator就标注在工厂方法上。 除了这2种方式外,还有一种构造方式成为授权式构造器,也是我们平常比较常用到的,这个构造器只有一个参数,且不能使用@JsonProperty。举例:

	@Test
	public void jsonCreator() throws Exception {
		ObjectMapper objectMapper = new ObjectMapper();
		String jsonStr = "{\"full_name\":\"myName\",\"age\":12}";
		TestPOJO testPOJO = objectMapper.readValue(jsonStr,TestPOJO.class);
		Assert.assertEquals("myName",testPOJO.getName());
		Assert.assertEquals(12,testPOJO.getAge());
	}
 
	public static class TestPOJO{
		private String name;
		private int age;
		@JsonCreator
		public TestPOJO(Map map){
			this.name = (String)map.get("full_name");
			this.age = (Integer)map.get("age");
		}
 
		public String getName() {
			return name;
		}
 
		public int getAge() {
			return age;
		}
	}

4.8、@JacksonInject

作用于属性、方法、构造参数上,被用来反序列化时标记已经被注入的属性,举例:

	@Test
	public void jacksonInject() throws Exception {
		ObjectMapper objectMapper = new ObjectMapper();
		String jsonStr = "{\"age\":12}";
		InjectableValues inject = new InjectableValues.Std().addValue("name","myName");
		TestPOJO testPOJO = objectMapper.reader(TestPOJO.class).with(inject).readValue(jsonStr);
		Assert.assertEquals("myName", testPOJO.getName());
		Assert.assertEquals(12,testPOJO.getAge());
	}
 
	public static class TestPOJO{
		@JacksonInject("name")
		private String name;
		private int age;
 
		//getters、setters省略
	}

上面例子中我们在反序列化前通过InjectableValues来进行注入我们想要的属性

4.9、@JsonPOJOBuilder

作用于类,用来标注如何定制构建对象,使用的是builder模式来构建,比如Value v = new ValueBuilder().withX(3).withY(4).build();这种就是builder模式来构建对象,通常会和@JsonDeserialize.builder来配合使用,我们举个例子:

	@Test
	public void jacksonInject() throws Exception {
		ObjectMapper objectMapper = new ObjectMapper();
		String jsonStr = "{\"name\":\"myName\",\"age\":12}";
		TestPOJO testPOJO = objectMapper.readValue(jsonStr,TestPOJO.class);
		Assert.assertEquals("myName", testPOJO.getName());
		Assert.assertEquals(12,testPOJO.getAge());
	}
 
	@JsonDeserialize(builder=TestPOJOBuilder.class)
	public static class TestPOJO{
		private String name;
		private int age;
 
		public TestPOJO(String name, int age) {
			this.name = name;
			this.age = age;
		}
 
		public String getName() {
			return name;
		}
 
		public int getAge() {
			return age;
		}
	}
 
	@JsonPOJOBuilder(buildMethodName = "create",withPrefix = "with")
	public static class TestPOJOBuilder{
		private String name;
		private int age;
 
		public TestPOJOBuilder withName(String name) {
			this.name = name;
			return this;
		}
 
		public TestPOJOBuilder withAge(int age) {
			this.age = age;
			return this;
		}
 
		public TestPOJO create() {
			return new TestPOJO(name,age);
		}
	}

在TestPOJOBuilder上有@JsonPOJOBuilder注解,表示所有的参数传递方法都是以with开头,最终构建好的对象是通过create方法来获得,而在TestPOJO上使用了@JsonDeserializer,告诉我们在反序列化的时候我们使用的是TestPOJOBuilder来构建此对象的.