1、官方参考
2、简介 Jackson 有两个主要分支,1.x 处于维护状态,只会发布 bug 修复版本。2.x 还在积极地开发当中。这两个版本的 Java 包名和 Maven artifact 不一样,所以它们不互相兼容,但是可以和平共存,项目可以同时依赖 1.x 和 2.x 而不会发生冲突。
Jackson 1.x(目前版本从 1.1~1.9)与 2.x 版本的依赖从包名可以看出来。1.x 的类库中,包命名以:org.codehaus.jackson.xxx
开头,而 2.x 类库中包命令:com.fastxml.jackson.xxx
开头。
3、主要模块 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
4)jackon 的三个核心类库
maven 依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <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 >
4、处理 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。
要点:
1 2 3 ObjectMapper mapper = new ObjectMapper ();mapper.writeValue(jsonFile, Bean); mapper.readValue(jsonFile, Bean.class 或 Collection<Bean>);
1)生成 json
city.java 文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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 文件:
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 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; } }
country.java 文件:
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 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 @Test public void Bean2JsonStr () throws ParseException, JsonGenerationException, JsonMappingException, IOException{ 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 ); mapper.setSerializationInclusion(Include.NON_EMPTY); mapper.writeValue(new File ("country.json" ), country); }
运行得到 country.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 { "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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Test public void JsonStr2Bean () throws JsonParseException, JsonMappingException, IOException{ ObjectMapper mapper = new ObjectMapper (); File jsonFile = new File ("country.json" ); 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 ChinaSat Oct 01 08:00:00 CST 1949 province:GD getBirthDate:Sat Feb 04 08:00:00 CST 2017 1 gz 2 dg
解析的时候如果碰到集合类,那么可以使用 TypeReference 类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @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);这行,结果为:
1 2 3 [{"id" :null,"cityName" :"gz" },{"id" :2,"cityName" :"dg" }] id :null cityName:gzid :2 cityName:dg
但假如想让 id 为 null 的不输出,不为 null 的输出除了 mapper.setSerializationInclusion(Include.NON_EMPTY); // 配置 mapper 忽略空属性 这种方法外还可以在 ObjectMapper 中注册一个自定义的序列化 JsonSerializer 和反序列化 JsonDeSerializer:
CityJsonSerializer.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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
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 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>(); if (!JsonToken.START_ARRAY.equals(parser.getCurrentToken())) { System.out.println(parser.getCurrentToken()); return null ; } while (!parser.isClosed()) { JsonToken token = parser.nextToken(); if (token == null ) break ; if (!JsonToken.START_OBJECT.equals(token)) { break ; } City city = null ; 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 @Test public void StreamJsonStr2List () throws IOException{ City city1 = new City (); 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
1 2 3 4 5 6 import com.fasterxml.jackson.databind.annotation.JsonSerialize;@JsonSerialize(using=CityJsonSerializer.class) public class City {... }
运行结果:
1 2 3 [{"cityName" :"gz" },{"id" :2,"cityName" :"dg" }] id :null cityName:gzid :2 cityName:dg
3)Tree Mode
如果不想为 Json 结构写一个 class 的话,Tree Mode 是一个很好的选择。
生成 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 39 40 41 42 @Test public void TreeMode2Json () throws IOException{ JsonNodeFactory factory = new JsonNodeFactory (false ); JsonFactory jsonFactory = new JsonFactory (); JsonGenerator generator = jsonFactory.createGenerator(new FileWriter (new File ("country2.json" ))); 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.writeTree(generator, country); }
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 { "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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Test public void TreeModeReadJson () throws IOException{ ObjectMapper mapper = new ObjectMapper (); 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 node JsonNodeType:OBJECT ---------得到所有 node 节点的子节点名称------------------------- id countryName establishTime provinces lakes forest --- lakes:["QingHai Lake" ,"Poyang Lake" ,"Dongting Lake" ,"Taihu Lake" ] JsonNodeType:ARRAY
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 方法轻松查找内容。
6、解析 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 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 66 67 68 69 70 71 72 73 74 75 76 77 78 79 import java.io.IOException;import java.text.SimpleDateFormat;import java.util.Date;import java.util.TimeZone;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.JsonNode;import com.fasterxml.jackson.databind.ObjectMapper;public class ParseJsonTest { public static void main (String[] args) { String data = "{\"type\":2,\"range\":1,\"start\":1368417600,\"end\":1368547140," + "\"cityName\":\"天津\",\"companyIds\":[\"12000001\"],\"companyNames\":[\"天津\"]," + "\"12000001\":{\"data\":[47947,48328,48573,48520]," + "\"timestamps\":[1368417600,1368417900,1368418200,1368418500]}}" ; String data2 = parseJson(data); System.out.println(data2); } public static String parseJson (String data) { StringBuffer buf = new StringBuffer (); ObjectMapper mapper = new ObjectMapper (); JsonNode rootNode = mapper.readTree(data); int type = rootNode.path("type" ).asInt(); int range = rootNode.path("range" ).asInt(); long start = rootNode.path("start" ).asLong(); long end = rootNode.path("end" ).asLong(); String cityName = rootNode.path("cityName" ).asText(); SimpleDateFormat sdf = new SimpleDateFormat ("yyyyMMddHHmm" ); sdf.setTimeZone(TimeZone.getTimeZone("GMT+8" )); String str = "类型(type):" + type + "\r\n" + "范围(range):" + range + "\r\n" + "开始时间(start):" + sdf.format(new Date (start * 1000 )) + "\r\n" + "结束时间(end):" + sdf.format(new Date (end * 1000 )) + "\r\n" + "城市名称(cityName):" + cityName; buf.append(str); JsonNode companyIds = rootNode.path("companyIds" ); JsonNode companyNames = rootNode.path("companyNames" ); for (int i = 0 ; i < companyIds.size(); i++) { String companyId = companyIds.get(i).asText(); String companyName = companyNames.get(i).asText(); JsonNode infoNode = rootNode.path(companyId); JsonNode dataNode = infoNode.path("data" ); JsonNode timestampsNode = infoNode.path("timestamps" ); buf.append("\r\n{\r\n 公司ID(companyId):" + companyId + "\r\n 公司名称(companyName):" + companyName + "\r\n" + " data:" ); for (int j = 0 ; j < dataNode.size(); j++) { long dataValue = dataNode.get(j).asLong(); buf.append(dataValue + "," ); } buf.append("\r\n time:" ); for (int k = 0 ; k < timestampsNode.size(); k++) { long timeValue = timestampsNode.get(k).asLong(); buf.append(sdf.format(new Date (timeValue * 1000 )) + "," ); } buf.append("\r\n}\r\n" ); } return buf.toString(); } }
测试结果:
1 2 3 4 5 6 7 8 9 10 11 类型(type ):2 范围(range):1 开始时间(start):201305131200 结束时间(end):201305142359 城市名称(cityName):天津 { 公司ID(companyId):12000001 公司名称(companyName):天津 data:47947,48328,48573,48520, time:201305131200,201305131205,201305131210,201305131215 }