Jackson之JSON包的使用分析

http://blog.csdn.net/blueheart20/article/details/52212221

引言: JSON的流行推动了JSON开发包的大量使用,如何高效、简便的开发json字符串的输入和输出,是我们在开发中经常会碰到的问题,这里我们以jackson2为例来讲解如何使用它。
1. Jackson的版本以及maven依赖
Jackson的最新版本是2.8.1, 以下是在maven项目中的依赖信息:

 <dependency>  
    <groupId>com.fasterxml.jackson.core</groupId>  
    <artifactId>jackson-core</artifactId>  
    <version>2.8.1</version>  
</dependency>  

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->  
<dependency>  
    <groupId>com.fasterxml.jackson.core</groupId>  
    <artifactId>jackson-databind</artifactId>  
    <version>2.8.1</version>  
</dependency>  

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->  
<dependency>  
    <groupId>com.fasterxml.jackson.core</groupId>  
    <artifactId>jackson-annotations</artifactId>  
    <version>2.8.1</version>  
</dependency>  

在以下示例代码中使用的一个依赖包是apache-common中的lang增强包:

 <dependency>  
    <groupId>org.apache.commons</groupId>  
    <artifactId>commons-lang3</artifactId>  
    <version>3.4</version>  
</dependency>
  1. 使用示例
    实体类定义:
@JsonInclude(JsonInclude.Include.NON_EMPTY)  
public class JSONCaseA {  

    @JsonProperty("testa")  
    private int age;  

    @JsonIgnore  
    private String unknownAttr;  

    private String location;  

    private String city;  

    public int getAge() {  
        return age;  
    }  

    public void setAge(int age) {  
        this.age = age;  
    }  

    public String getUnknownAttr() {  
        return unknownAttr;  
    }  

    public void setUnknownAttr(String unknownAttr) {  
        this.unknownAttr = unknownAttr;  
    }  

    public String getLocation() {  
        return location;  
    }  

    public void setLocation(String location) {  
        this.location = location;  
    }  

    public String getCity() {  
        return city;  
    }  

    public void setCity(String city) {  
        this.city = city;  
    }  

        public String toString() {  
           return ReflectionToStringBuilder.toString(this);  
        }   
}  

2.1 将对象转换为json字符串
测试代码:

public class TestWriteJson {  

    @Test  
    public void writeJson() throws JsonProcessingException {  

        ObjectMapper mapper = new ObjectMapper();  

        JSONCaseA case1 = new JSONCaseA();  
        case1.setCity("BeiJing");  

        case1.setUnknownAttr("testVal");  
        case1.setAge(123);  

        String jsonStr = mapper.writeValueAsString(case1);  

        System.out.println("JSON:" + jsonStr);  
    }  
}  

结果输出为: JSON:{“city”:”BeiJing”,”testa”:123}
代码中使用的标注分析:
@JsonProperty(“xxx”): 将当前的属性名在json字符串中重新命名为当前设置的这个值,比如在示例中,将age–>testa
@JsonIgnore 将被标注的属性在生成json字符串的时候,直接忽略
@JsonInclude 是一个类级别的设置,JsonInclude.Include.NON_EMPTY标识只有非NULL的值才会被纳入json string之中,其余的都被忽略,比如这里的location属性,并没有出现在最终的结果字符串中。
2.2 根节点以及使用
实体类定义:

@JsonInclude(JsonInclude.Include.NON_EMPTY)  
@JsonRootName(value="rootNode")  
public class JsonCaseB {  

    @JsonRawValue  
    private String address = "$#";  

    private int age;  

    @JsonIgnore  
    private String sex;  

    public String getAddress() {  
        return address;  
    }  

    public void setAddress(String address) {  
        this.address = address;  
    }  

    public int getAge() {  
        return age;  
    }  

    public void setAge(int age) {  
        this.age = age;  
    }  

    public String getSex() {  
        return sex;  
    }  

    public void setSex(String sex) {  
        this.sex = sex;  
    }  

        public String toString() {  
           return ReflectionToStringBuilder.toString(this);  
        }  
}   

测试代码:

public class TestRootNode {  

    @Test  
    public void testRootNode() throws JsonProcessingException {  
        JsonCaseB caseb = new JsonCaseB();  

         ObjectMapper mapper = new ObjectMapper();  
         mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);  

         caseb.setAge(123);  
         caseb.setSex("Boy");  

         String jsonStr = mapper.writeValueAsString(caseb);  

         System.out.println("JSON:" + jsonStr);  
    }  
}  

输出结果:JSON:{“rootNode”:{“address”:$#,”age”:123}}

关于其中使用的annotation的说明:
@JsonRootName(value=”rootNode”) 使用在类上,标注为该类在转换为json string的时候,需要新增一个根节点rootNode.
mapper.enable(SerializationFeature.WRAP_ROOT_VALUE): 在mapper级别设置为使用root node方可输出根节点,否则根节点无效。
2.3 读取操作
测试代码:

@Test  
    public void testReadJson() throws JsonParseException, JsonMappingException, IOException {  
        ObjectMapper mapper = new ObjectMapper();  
        String inputjsonstr = "{\"city\":\"BeiJing\",\"testa\":123}";  

        JSONCaseA readcase = mapper.readValue(inputjsonstr, JSONCaseA.class);  

        System.out.println("object info:" + readcase);  
    } 

输出结果: object info:org.homework.test.json.JSONCaseA@7d907bac[age=123,unknownAttr=,location=,city=BeiJing]
结果分析:
json字符串中有2个属性, 目标对象中有4个属性,这个四个属性包含了json字符串中的2个属性;可以发现在JSONCaseA中的属性使用了缺省值,可以正常解析出来结果。
2.4 在读取过程中,如果json字符串中出现未知属性
测试代码:

@Test  
    public void testReadJson() throws JsonParseException, JsonMappingException, IOException {  
        ObjectMapper mapper = new ObjectMapper();  
        String inputjsonstr = "{\"city\":\"BeiJing\",\"testa\":123, \"who\":\"zhangsan\"}";  

        JSONCaseA readcase = mapper.readValue(inputjsonstr, JSONCaseA.class);  

        System.out.println("object info:" + readcase);  
    }  

输出结果:

Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "who" (class org.homework.test.json.JSONCaseAReadUnknowAttr), not marked as ignorable (3 known properties: "city", "location", "testa"])  
 at [Source: {"city":"BeiJing","testa":123, "who":"zhangsan"}; line: 1, column: 39] (through reference chain: org.homework.test.json.JSONCaseAReadUnknowAttr["who"])  
    at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:62)  
    at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:833)  
    at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:1096)  
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1467)  
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1445)  
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:282)  
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:140)  
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3789)  
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2833)  
    at org.homework.test.json.JSONCaseAReadUnknowAttr.main(JSONCaseAReadUnknowAttr.java:81)  

结果分析: 异常中提示未知属性who,无法解析。
那么该如何来解决这个问题呢? 新如下设置语句:

mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);  

这个设置将禁止使用出现未知属性之时,抛出异常。
重新执行代码,就可以正常使用了。
3. 其他相关的Annotation
@JsonSerialize 使用自定义的类来实现自定义的字段转换。写入操作。
@JsonDeserialize 解析的时候,自定义的转换器;读取操作。
@JsonAutoDetect: 设置类的访问策略,是否所有的属性都可以,还是按照一定的方式来提取。
@JsonRawValue: 无转换的将属性值写入到json 字符串中。 写入操作
@JsonValue: 标注方法,用以替代缺省的方法,由该方法来完成json的字符输出。
当然还有很多其他的标注,这里不再一一赘述,感兴趣或者需要的话,大家可以自行查找学习。 另外,jackson本身是支持xml和json两种输出的,基于同一套类库。
4. 参考资料
http://tutorials.jenkov.com/java-json/jackson-annotations.html#jsonautodetect
http://www.baeldung.com/jackson-annotations
https://github.com/FasterXML/jackson-databind/wiki/Databind-Annotations

上一篇:jackson学习和CVE-2019-12086漏洞


下一篇:用了几年的 Fastjson,我最终替换成了Jackson!