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的包。
- 1
- 2
- 3
- 4
- 5
- 6
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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
處理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; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
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; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
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; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
測試案例
@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屬性名稱 }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
運行得到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" } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
(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()); } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
輸出結果:
ChinaSat Oct 01 08:00:00 CST 1949 province:GD getBirthDate:Sat Feb 04 08:00:00 CST 2017 1 gz 2 dg
- 1
- 2
- 3
- 4
- 5
解析的時候如果碰到集合類,那么可以使用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()); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
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
- 1
- 2
- 3
但假如想讓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(); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
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; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
測試:
@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()); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
也可以簡單一點,使用注解,省去在ObjectMapper 中注冊SimpleModule
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
@JsonSerialize(using=CityJsonSerializer.class)
public class City {
... }
- 1
- 2
- 3
- 4
- 5
運行結果:
[{"cityName":"gz"},{"id":2,"cityName":"dg"}] id:null cityName:gz id:2 cityName:dg
- 1
- 2
- 3
###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); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
結果:
{
"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"} }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
讀取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()); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
運行結果:
node JsonNodeType:OBJECT
---------得到所有node節點的子節點名稱-------------------------
id countryName establishTime provinces lakes forest ----------------------------------------------------- lakes:["QingHai Lake","Poyang Lake","Dongting Lake","Taihu Lake"] JsonNodeType:ARRAY
- 1
- 2
- 3
- 4
- 5
結束
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