Java解析json(二):jackson

官方参考

Jackson Home Page:https://github.com/FasterXML/jackson
Jackson Wiki:http://wiki.fasterxml.com/JacksonHome
Jackson doc: https://github.com/FasterXML/jackson-docs
Jackson Download Page:http://wiki.fasterxml.com/JacksonDownload

简介

Jackson框架是基于Java平台的一套数据处理工具,被称为“最好的Java Json解析器”。

Jackson有两个主要分支,1.x处于维护状态,只会发布bug修复版本。2.x还在积极地开发当中。这两个版本的Java包名和Maven artifact不一样,所以它们不互相兼容,但是可以和平共存,也就是项目可以同时依赖1.x和2.x而不会发生冲突。

Jackson版本: 1.x (目前版本从1.1~1.9)与2.x。1.x与2.x从包的命名上可以看出来,1.x的类库中,包命名以:org.codehaus.jackson.xxx开头,而2.x类库中包命令:com.fastxml.jackson.xxx开头。

本文以2.x为主…

主要模块

1. 核心模块

 核心模块是扩展模块构建的基础,到2.7版本为止,共有3个核心模块(依赖关系从上到下):

 ***Streaming*** :   jackson-core jar,定义了底层的streaming API和实现了Json特性。
 ***Annotations*** :  jackson-annotations jar,包含了标准的Jackson注解。
 ***Databind*** :   jackson-databind jar,实现了数据绑定和对象序列化,它依赖于streaming和annotations的包。

2. 第三方数据类型模块

这些扩展是插件式的Jackson模块,用ObjectMapper.registerModule()注册,并且通过添加serializers和deserializers以便Databind包(ObjectMapper / ObjectReader / ObjectWriter)可以读写这些类型,来增加对各种常用的Java库的数据类型的支持。参考https://github.com/FasterXML/jacksonThird-party datatype modules。

3. 数据格式模块

Jackson也有处理程序对JAX-RS标准实现者例如Jersey, RESTeasy, CXF等提供了数据格式支持。处理程序实现了MessageBodyReader和MessageBodyWriter,目前支持的数据格式包括JSON, Smile, XML, YAML和CBOR。

数据格式提供了除了Json之外的数据格式支持,它们绝大部分仅仅实现了streaming API abstractions,以便数据绑定组件可以按照原来的方式使用。另一些(几乎不需要)提供了databind标准功能来处理例如schemas。参考https://github.com/FasterXML/jacksonData format modules

准备工作

JDK1.7,依赖jackon的三个核心类库:

  • jackson-core-2.5.3.jar
  • jackson-annotations-2.5.3.jar
  • jackson-databind-2.5.3.jar

maven依赖:

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

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

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

处理Json

Jackson提供了三种可选的Json处理方法:流式API(Streaming API) 、树模型(Tree Model)、数据绑定(Data Binding)。三种处理Json的方式的特性:

  • Streaming API:是效率最高的处理方式(开销低、读写速度快,但程序编写复杂度高)
  • Tree Model:是最灵活的处理方式
  • Data Binding:是最常用的处理方式

1.Data Binding

主要使用ObjectMapper来操作Json,默认情况下会使用BeanSerializer来序列化POJO。
如果是解析,那么如下的例子里的TestJson必须要有setters,且setters必须是public修饰的,否则属性的值将会为null。
如果是生成,那么必须有getters,且getters必须是public修饰的。
如果属性不是private修饰,那么可以不用有getters和setters。(参考访问修饰符)
要点:
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(jsonFile, Bean);
mapper.readValue(jsonFile, Bean.class/Collection< Bean >);

(1)生成json

city.java

package com.myjackson.databinding;
//市
public class City {
    private Integer id;
    private String cityName;
    public City(){}
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getCityName() {
        return cityName;
    }
    public void setCityName(String cityName) {
        this.cityName = cityName;
    }

}

province.java

package com.myjackson.databinding;
import java.util.Date;
import java.util.List;
//省
public class Province {
    private Integer id;
    private String name;
    private Date birthDate;
    private List<City> cities;
    public Province(){}
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public List<City> getCities() {
        return cities;
    }
    public void setCities(List<City> cities) {
        this.cities = cities;
    }
    public Date getBirthDate() {
        return birthDate;
    }
    public void setBirthDate(Date birthDate) {
        this.birthDate = birthDate;
    }
}

counry.java

package com.myjackson.databinding;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

//国家
public class Country {

    private Integer id;
    private String countryName;
    private Date establishTime;
    private List<Province> provinces;
    private String[] lakes;  
    private Map<String, String> forest = new HashMap<String, String>();
    public Country(){

    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getCountryName() {
        return countryName;
    }
    public void setCountryName(String countryName) {
        this.countryName = countryName;
    }
    public Date getEstablishTime() {
        return establishTime;
    }
    public void setEstablishTime(Date establishTime) {
        this.establishTime = establishTime;
    }
    public List<Province> getProvinces() {
        return provinces;
    }
    public void setProvinces(List<Province> provinces) {
        this.provinces = provinces;
    }
    public String[] getLakes() {
        return lakes;
    }
    public void setLakes(String[] lakes) {
        this.lakes = lakes;
    }
    public Map<String, String> getForest() {
        return forest;
    }
    public void setForest(Map<String, String> forest) {
        this.forest = forest;
    }  

}

测试案例

@Test
    public void Bean2JsonStr() throws ParseException, JsonGenerationException, JsonMappingException, IOException{
        // 使用ObjectMapper转化对象为Json  
        ObjectMapper mapper = new ObjectMapper(); 
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");  
        mapper.setDateFormat(dateFormat);   //设置日期序列化格式
        City city1 = new City();
        city1.setId(1);
        city1.setCityName("gz");
        City city2 = new City();
        city2.setId(2);
        city2.setCityName("dg");

        Province province = new Province();
        province.setId(1);
        province.setName("GD");
        province.setBirthDate(new Date());
        List<City> cities = new ArrayList<City>();
        cities.add(city1);
        cities.add(city2);
        province.setCities(cities);

        Country country = new Country();
        country.setCountryName("China");
        country.setId(1);
        country.setEstablishTime(dateFormat.parse("1949-10-01"));
        country.setLakes(new String[] { "Qinghai Lake", "Poyang Lake","Dongting Lake", "Taihu Lake" });
        HashMap<String, String> forest = new HashMap<String, String>();
        forest.put("no.1", "dxal");
        forest.put("no.2", "xxal");
        country.setForest(forest);
        List<Province> provinces = new ArrayList<Province>();
        provinces.add(province);
        country.setProvinces(provinces);
        mapper.configure(SerializationFeature.INDENT_OUTPUT, true);     // 为了使JSON视觉上的可读性,在生产中不需如此,会增大Json的内容  
        mapper.setSerializationInclusion(Include.NON_EMPTY);  // 配置mapper忽略空属性  
        mapper.writeValue(new File("country.json"), country);  // 默认情况,Jackson使用Java属性字段名称作为 Json的属性名称,也可以使用Jackson annotations(注解)改变Json属性名称    
    }

运行得到country.json:

{
  "id" : 1,
  "countryName" : "China",
  "establishTime" : "1949-10-01",
  "provinces" : [ {
    "id" : 1,
    "name" : "GD",
    "birthDate" : "2017-02-04",
    "cities" : [ {
      "id" : 1,
      "cityName" : "gz"
    }, {
      "id" : 2,
      "cityName" : "dg"
    } ]
  } ],
  "lakes" : [ "Qinghai Lake", "Poyang Lake", "Dongting Lake", "Taihu Lake" ],
  "forest" : {
    "no.1" : "dxal",
    "no.2" : "xxal"
  }
}

(2)解析json

@Test
    public void JsonStr2Bean() throws JsonParseException, JsonMappingException, IOException{
         ObjectMapper mapper = new ObjectMapper();  
         File jsonFile = new File("country.json");
         //当反序列化json时,未知属性会引起的反序列化被打断,这里我们禁用未知属性打断反序列化功能,  
         //因为,例如json里有10个属性,而我们的bean中只定义了2个属性,其它8个属性将被忽略  
         mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
         Country country = mapper.readValue(jsonFile, Country.class);
         System.out.println(country.getCountryName()+country.getEstablishTime());
         List<Province> provinces = country.getProvinces();  
            for (Province province : provinces) {  
                System.out.println("province:"+province.getName() + "\n" + "birthDate:"+province.getBirthDate());  
                for (City city: province.getCities()) {
                     System.out.println(city.getId()+" "+city.getCityName());
                }
            }  
    }

输出结果:

ChinaSat Oct 01 08:00:00 CST 1949
province:GD
getBirthDate:Sat Feb 04 08:00:00 CST 2017
1 gz
2 dg

解析的时候如果碰到集合类,那么可以使用TypeReference类

@Test
    public void JsonStr2List() throws IOException{
         City city1 = new City();
         city1.setId(1);
         city1.setCityName("gz");
         City city2 = new City();
         city2.setId(2);
         city2.setCityName("dg");

         List<City> cities = new ArrayList<City>();
         cities.add(city1);
         cities.add(city2);

         ObjectMapper mapper = new ObjectMapper();  
         String listJsonStr = mapper.writeValueAsString(cities);
         System.out.println(listJsonStr);
         List<City> list = mapper.readValue(listJsonStr, new  TypeReference<List<City>>(){} );
         for (City city: list) {
            System.out.println("id:"+city.getId()+" cityName:"+city.getCityName());
         }

    }

2.Streaming API

Jackson提供了一套底层API来解析Json字符串,这个API为每个Json对象提供了符号。例如, ‘{’ 是解析器提供的第一个对象(writeStartObject()),键值对是解析器提供的另一个单独对象(writeString(key,value))。这些API很强大,但是需要大量的代码。大多数情况下,Tree Model和Data Binding可以代替Streaming API。

上面代码如果注释掉 city1.setId(1);这行,结果为:

[{"id":null,"cityName":"gz"},{"id":2,"cityName":"dg"}]
id:null cityName:gz
id:2 cityName:dg

但假如想让id为null的不输出,不为null的输出除了 mapper.setSerializationInclusion(Include.NON_EMPTY); // 配置mapper忽略空属性 这种方法外还可以在ObjectMapper中注册一个自定义的序列化JsonSerializer和反序列化
JsonDeSerializer:

CityJsonSerializer.java

package com.myjackson.databinding;

import java.io.IOException;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

public class CityJsonSerializer  extends JsonSerializer<City>{

    @Override
    public void serialize(City city, JsonGenerator jsonGenerator, SerializerProvider arg2)
            throws IOException, JsonProcessingException {
         jsonGenerator.writeStartObject();  
         if ( city.getId()!=null) {
             jsonGenerator.writeNumberField("id", city.getId());  
         }
         jsonGenerator.writeStringField("cityName", city.getCityName());  
         jsonGenerator.writeEndObject();  

    }

}

CityJsonDeSerializer.java

package com.myjackson.databinding;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;

public class CityJsonDeSerializer  extends JsonDeserializer<List<City>>{


    @Override
    public List<City> deserialize(JsonParser parser,DeserializationContext deserializationcontext) throws IOException,
            JsonProcessingException {
        List<City> list = new ArrayList<City>();
        // 开始解析数组,第一个JsonToken必须是JsonToken.START_ARRAY"["
         if (!JsonToken.START_ARRAY.equals(parser.getCurrentToken())) {
             System.out.println(parser.getCurrentToken());
             return null;
         }
        // 解析符号直到字符串结尾  
        while (!parser.isClosed()) { 
            // 如果有必要的话,这个方法会沿着流前进直到足以确下一个JsonToken的类型  
            JsonToken token = parser.nextToken();  
            // 如果是最后一个JsonToken,那么就结束了  
            if (token == null)  
                break; 
            // 数组的每个元素都是对象,因此下一个JsonToken是JsonToken.START_OBJECT"{"  
            if (!JsonToken.START_OBJECT.equals(token)) {  
                break;  
            }  
            City city = null;
            // 输出id字段的值  
            while (true) { 
                if (JsonToken.START_OBJECT.equals(token)) {  
                    city = new City();
                } 
                token = parser.nextToken();  
                if (token == null)  
                    break;  

                if (JsonToken.FIELD_NAME.equals(token) ) {

                    if("id".equals(parser.getCurrentName())){
                        token = parser.nextToken();  
                        city.setId(parser.getIntValue());
                    }else if("cityName".equals(parser.getCurrentName())){
                        token = parser.nextToken();
                        city.setCityName(parser.getText());
                    }

                }  
                if(JsonToken.END_OBJECT.equals(token)){
                    list.add(city);
                }

            }  

        }  
        return list;
    }
}

测试:

@Test
    public void StreamJsonStr2List() throws IOException{
         City city1 = new City();
         //city1.setId(1);
         city1.setCityName("gz");
         City city2 = new City();
         city2.setId(2);
         city2.setCityName("dg");

         List<City> cities = new ArrayList<City>();
         cities.add(city1);
         cities.add(city2);

         ObjectMapper mapper = new ObjectMapper();  
         SimpleModule module = new SimpleModule();  
         module.addSerializer(City.class, new CityJsonSerializer());  
         mapper.registerModule(module);  
         String listJsonStr = mapper.writeValueAsString(cities);

         System.out.println(listJsonStr);

         ObjectMapper mapper2 = new ObjectMapper();  
         SimpleModule module2 = new SimpleModule();  
         module2.addDeserializer(List.class, new CityJsonDeSerializer());  
         mapper2.registerModule(module2);  
         List<City> list = mapper2.readValue(listJsonStr, new  TypeReference<List<City>>(){} );

         for (City city: list) {
            System.out.println("id:"+city.getId()+" cityName:"+city.getCityName());
         }

    }

也可以简单一点,使用注解,省去在ObjectMapper 中注册SimpleModule

import com.fasterxml.jackson.databind.annotation.JsonSerialize;
@JsonSerialize(using=CityJsonSerializer.class)
public class City {
    ...
}

运行结果:

[{"cityName":"gz"},{"id":2,"cityName":"dg"}]
id:null cityName:gz
id:2 cityName:dg

###3.Tree Mode
如果不想为Json结构写一个class的话,Tree Mode是一个很好的选择。

生成json:

@Test
    public void TreeMode2Json() throws IOException{

        //创建一个节点工厂,为我们提供所有节点  
        JsonNodeFactory factory = new JsonNodeFactory(false);  
        //创建一个json factory来写tree modle为json  
        JsonFactory jsonFactory = new JsonFactory();  
        //创建一个json生成器  
        JsonGenerator generator = jsonFactory.createGenerator(new FileWriter(new File("country2.json")));  
        //注意,默认情况下对象映射器不会指定根节点,下面设根节点为country  
        ObjectMapper mapper = new ObjectMapper();  
        ObjectNode country = factory.objectNode();  
        country.put("id",   1);
        country.put("countryName","China");
        country.put("establishTime", "1949-10-01");

        ArrayNode provinces = factory.arrayNode(); 
        ObjectNode province = factory.objectNode();
        ObjectNode city1 = factory.objectNode();
        city1.put("id", 1);
        city1.put("cityName", "gz");
        ObjectNode city2 = factory.objectNode();
        city2.put("id", 1);
        city2.put("cityName", "dg");
        ArrayNode cities = factory.arrayNode();
        cities.add(city1).add(city2);
        province.put("cities", cities);
        provinces.add(province);
        country.put("provinces",provinces);

        ArrayNode lakes = factory.arrayNode();  
        lakes.add("QingHai Lake").add("Poyang Lake").add("Dongting Lake").add("Taihu Lake");
        country.put("lakes",lakes);

        ObjectNode forest = factory.objectNode();  
        forest.put("no.1","dxal");  
        forest.put("no.2", "xxal"); 
        country.put("forest", forest);

        mapper.setSerializationInclusion(Include.NON_EMPTY);  // 配置mapper忽略空属性  
        mapper.writeTree(generator, country);  
    }

结果:

{
    "id":1,
    "countryName":"China",
    "establishTime":"1949-10-01",
    "provinces":[
        {"cities":[
            {"id":1,"cityName":"gz"},
            {"id":1,"cityName":"dg"}
            ]
        }
    ],
    "lakes":["QingHai Lake","Poyang Lake","Dongting Lake","Taihu Lake"],
    "forest":{"no.1":"dxal","no.2":"xxal"}
}

读取json:

@Test
public void TreeModeReadJson() throws IOException{
         ObjectMapper mapper = new ObjectMapper();  
         // Jackson提供一个树节点被称为"JsonNode",ObjectMapper提供方法来读json作为树的JsonNode根节点  
         JsonNode node = mapper.readTree(new File("country2.json"));  
         // 看看根节点的类型  
         System.out.println("node JsonNodeType:"+node.getNodeType());  
         System.out.println("---------得到所有node节点的子节点名称----------------------");  
         Iterator<String> fieldNames = node.fieldNames();  
         while (fieldNames.hasNext()) {  
             String fieldName = fieldNames.next();  
             System.out.print(fieldName+" ");  
         }   
         System.out.println("\n---------------------------------------------------");  

         JsonNode lakes = node.get("lakes");  
         System.out.println("lakes:"+lakes+" JsonNodeType:"+lakes.getNodeType());  
}

运行结果:

node JsonNodeType:OBJECT
---------得到所有node节点的子节点名称-------------------------
id countryName establishTime provinces lakes forest 
-----------------------------------------------------
lakes:["QingHai Lake","Poyang Lake","Dongting Lake","Taihu Lake"] JsonNodeType:ARRAY

结束

Stream API方式是开销最低、效率最高,但编写代码复杂度也最高,在生成Json时,需要逐步编写符号和字段拼接json,在解析Json时,需要根据token指向也查找json值,生成和解析json都不是很方便,代码可读性也很低。
Databinding处理Json是最常用的json处理方式,生成json时,创建相关的java对象,并根据json内容结构把java对象组装起来,最后调用writeValue方法即可生成json,
解析时,就更简单了,直接把json映射到相关的java对象,然后就可以遍历java对象来获取值了。
TreeModel处理Json,是以树型结构来生成和解析json,生成json时,根据json内容结构,我们创建不同类型的节点对象,组装这些节点生成json。解析json时,它不需要绑定json到java bean,根据json结构,使用path或get方法轻松查找内容。
以上为个人参考网上博客以及一些个人实践,不对之处烦请指正,感激不尽~

参考:

http://blog.csdn.net/gjb724332682/article/details/51586701#
http://blog.csdn.net/java_huashan/article/details/46375857

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值