Java对象和Json之间的互转,一般用的比较多的两个类库是Jackson和Gson,下面记录一下Gson的学习使用。
基础概念 Serialization:序列化,使Java对象到Json字符串的过程。 Deserialization:反序列化,字符串转换成Java对象
JSON数据中的JsonElement
有下面这四种类型
JsonPrimitive —— 例如一个字符串或整型 JsonObject—— 一个以 JsonElement 名字(类型为 String)作为索引的集合。也就是说可以把 JsonObject 看作值为 JsonElement 的键值对集合。 JsonArray—— JsonElement 的集合。注意数组的元素可以是四种类型中的任意一种,或者混合类型都支持。 JsonNull—— 值为null
依赖引用 使用Maven管理Gson,pom.xml导入gson的依赖
1 2 3 4 5 <dependency > <groupId > com.google.code.gson</groupId > <artifactId > gson</artifactId > <version > 2.3.1</version > </dependency >
基本语法 Gson的两个基础方法
Gson中的一些注解 1 @SerializedName注解 该注解能指定该字段在JSON中对应的字段名称
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Box { @SerializedName("w") private int width; @SerializedName("h") private int height; @SerializedName("d") private int depth; }
也就是说{"w":10,"h":20,"d":30}
这个JSON 字符串能够被解析到上面的width,height和depth字段中。
2 @Expose注解 该注解能够指定该字段是否能够序列化或者反序列化,默认的是都支持(true)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Account { @Expose (deserialize = false ) private String accountNumber; @Expose private String iban; @Expose (serialize = false ) private String owner; @Expose (serialize = false , deserialize = false ) private String address; private String pin; }
需要注意的通过 builder.excludeFieldsWithoutExposeAnnotation()
方法是该注解生效。
1 2 3 4 final GsonBuilder builder = new GsonBuilder(); builder.excludeFieldsWithoutExposeAnnotation(); final Gson gson = builder.create();
3 @Since和@Until注解 Since代表“自从”,Until 代表”一直到”。它们都是针对该字段生效的版本。比如说 @Since(1.2)
代表从版本1.2之后才生效,@Until(0.9)
代表着在0.9版本之前都是生效的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class SoccerPlayer { private String name; @Since(1.2) private int shirtNumber; @Until(0.9) private String country; private String teamName; }
也就是说我们利用方法builder.setVersion(1.0)
定义版本1.0,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 final GsonBuilder builder = new GsonBuilder(); builder.setVersion(1.0 ); final Gson gson = builder.create(); final SoccerPlayer account = new SoccerPlayer(); account.setName("Albert Attard" ); account.setShirtNumber(10 ); account.setTeamName("Zejtun Corinthians" ); account.setCountry("Malta" ); final String json = gson.toJson(account); System.out.printf("Serialised (version 1.0)%n %s%n" , json);
由于shirtNumber
和country
作用版本分别是1.2之后,和0.9之前,所以在这里都不会得到序列化,所以输出结果是:
1 2 3 Serialised (version 1.0) {"name" :"Albert Attard" ,"teamName" :"Zejtun Corinthians" }
Gson 序列化 英文Serialize和format都对应序列化,这是一个Java对象到JSON字符串的过程。 接着看一个例子,下面分别是java类和以及我们期望的JSON数据:
1 2 3 4 5 6 7 8 9 public class Book { private String [] authors; private String isbn10; private String isbn13; private String title; }
1 2 3 4 5 6 7 8 9 10 { "title" : "Java Puzzlers: Traps, Pitfalls, and Corner Cases" , "isbn-10" : "032133678X" , "isbn-13" : "978-0321336781" , "authors" : [ "Joshua Bloch" , "Neal Gafter" ] }
你肯定能发现JSON数据中出现了isbn-10
和isbn-13
, 我们怎么把字段数据isbn10
和isbn13
转化为JSON数据需要的isbn-10
和isbn-13
,Gson当然为我们提供了对应的解决方案
1 序列化方案1 采用上面提到的@SerializedName
注解。
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Book { private String [] authors; @SerializedName ("isbn-10" ) private String isbn10; @SerializedName ("isbn-13" ) private String isbn13; private String title; }
2 序列化方案2 利用JsonSerializer
类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class BookSerialiser implements JsonSerializer { @Override public JsonElement serialize (final Book book, final Type typeOfSrc, final JsonSerializationContext context) { final JsonObject jsonObject = new JsonObject (); jsonObject.addProperty("title" , book.getTitle()); jsonObject.addProperty("isbn-10" , book.getIsbn10()); jsonObject.addProperty("isbn-13" , book.getIsbn13()); final JsonArray jsonAuthorsArray = new JsonArray (); for (final String author : book.getAuthors()) { final JsonPrimitive jsonAuthor = new JsonPrimitive (author); jsonAuthorsArray.add(jsonAuthor); } jsonObject.add("authors" , jsonAuthorsArray); return jsonObject; } }
下面对序列化过程进行大致的分析:
JsonSerializer是一个接口,我们需要提供自己的实现,来满足自己的序列化要求。
1 2 3 4 5 6 7 8 9 10 11 public interface JsonSerializer <T> { public JsonElement serialize (T src, Type typeOfSrc, JsonSerializationContext context) ; }
首先在上面的代码中,我们需要创建的是一个JsonElement对象,这里对应Book是一个对象,所以创建一个JsonObject类型。final JsonObject jsonObject = new JsonObject();
然后我们将相应字段里面的数据填充到jsonObject里面。
1 2 3 jsonObject.addProperty ... jsonObject.add ...
下面是jsonObject中的添加方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 final GsonBuilder gsonBuilder = new GsonBuilder (); gsonBuilder.registerTypeAdapter(Book.class, new BookSerialiser ()); gsonBuilder.setPrettyPrinting(); final Gson gson = gsonBuilder.create(); final Book javaPuzzlers = new Book (); javaPuzzlers.setTitle("Java Puzzlers: Traps, Pitfalls, and Corner Cases" ); javaPuzzlers.setIsbn10("032133678X" ); javaPuzzlers.setIsbn13("978-0321336781" ); javaPuzzlers.setAuthors(new String [] { "Joshua Bloch" , "Neal Gafter" }); final String json = gson.toJson(javaPuzzlers); System.out.println(json);
,这里对应的是gsonBuilder.registerTypeAdapter(Book.class, new BookSerialiser())
方法进行JsonSerializer的配置。在上面例子中,通过调用gsonBuilder.setPrettyPrinting();
方法还告诉了 Gson 对生成的 JSON 对象进行格式化
Gson 反序列化 英文parse和deserialise对应反序列化,这是一个字符串转换成Java对象的过程。 我们同样采用上面一小节的代码片段,只不过现在我们需要做的是将:
1 2 3 4 5 6 7 8 9 10 { "title" : "Java Puzzlers: Traps, Pitfalls, and Corner Cases" , "isbn-10" : "032133678X" , "isbn-13" : "978-0321336781" , "authors" : [ "Joshua Bloch" , "Neal Gafter" ] }
转化为对应的Book实体类,
1 反序列化方案1 利用@SerializedName
注解 也就是说我们的实体类Book.java可以这么写:
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Book { private String [] authors; @SerializedName ("isbn-10" ) private String isbn10; @SerializedName (value = "isbn-13" , alternate = {"isbn13" ,"isbn.13" }) private String isbn13; private String title; }
可以看到这里我们在@SerializedName
注解使用了一个value
, alternate
字段,value
也就是默认的字段,对序列化和反序列化都有效,alternate
只有反序列化才有效果。也就是说一般服务器返回给我们JSON数据的时候可能同样的一个图片,表示”image”,”img”,”icon”等,我们利用@SerializedName
中的alternate
字段就能解决这个问题,全部转化为我们实体类中的图片字段。
2 反序列化方案2 我们在序列化的时候使用的是JsonSerialize
,这里对应使用JsonDeserializer
我们将解析到的json数据传递给Book的setter方法即可。
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 public class BookDeserializer implements JsonDeserializer <Book > { @Override public Book deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException { final JsonObject jsonObject = json.getAsJsonObject(); final JsonElement jsonTitle = jsonObject.get ("title" ); final String title = jsonTitle.getAsString(); final String isbn10 = jsonObject.get ("isbn-10" ).getAsString(); final String isbn13 = jsonObject.get ("isbn-13" ).getAsString(); final JsonArray jsonAuthorsArray = jsonObject.get ("authors" ).getAsJsonArray(); final String [] authors = new String [jsonAuthorsArray.size()]; for (int i = 0 ; i < authors.length; i++) { final JsonElement jsonAuthor = jsonAuthorsArray.get (i); authors[i] = jsonAuthor.getAsString(); } final Book book = new Book(); book.setTitle(title); book.setIsbn10(isbn10); book.setIsbn13(isbn13); book.setAuthors(authors); return book; } }
和Gson序列化章节一样,我们这里接着分析我们是怎么将JSON数据解析(反序列化)为实体类的:
因为我们可以发现上面的JSON数据是一个{}
大括号包围的,也就意味着这是一个Json对象。所以首先我们通过final JsonObject jsonObject = json.getAsJsonObject();
将我们的JsonElement转化为JsonObject
通过jsonObject.get("xxx").getAsString()
的形式获取相应String的值
通过jsonObject.get("xx").getAsJsonArray();
获取相应的json数组,并遍历出其中的相应字段值
通过setter方法,将获取到的值设置给Book类。
最终返回的是 Book的对象实例。完成了JSON->javaBean的转化
同样需要配置
关于从本地流中读取Json数据可以使用 InputStreamReader
完成
1 2 3 4 5 6 7 8 9 10 11 12 GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(Book.class , new BookDeserializer()); Gson gson = gsonBuilder.create(); try (Reader reader = new InputStreamReader(Main.class .getResourceAsStream("/part1/sample.json" ), "UTF-8" )){ Book book = gson.fromJson(reader, Book.class ); System.out .println(book); }
泛类型反序列化方案 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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 import com.google.common.reflect.TypeToken;import com.google.gson.Gson;import com.google.gson.JsonSyntaxException;import java.lang.reflect.Type;public class JsonHelper { public static void main (String[] args) { String respStr = "{\"status\":0,\"msg\":\"\",\"data\":{\"cardNo\":\"12345677\"}}" ; BaseResponse<CardInfo> cardInfo = parseResponse(respStr); System.out.println(cardInfo.data); respStr = "{\"status\":0,\"msg\":\"\",\"data\":{\"id\":\"123445678\",\"name\":\"张三\"}}" ; BaseResponse<UserInfo> userInfo = parseResponse(respStr); System.out.println(userInfo.data); } private static <T> BaseResponse<T> parseResponse (String responseData) throws JsonSyntaxException { Gson gson = new Gson (); Type jsonType = new TypeToken <BaseResponse<T>>() { }.getType(); BaseResponse<T> result = gson.fromJson(responseData, jsonType); return result; } class BaseResponse <T> { private int status; public int getStatus () { return status; } public void setStatus (int status) { this .status = status; } public String getMsg () { return msg; } public void setMsg (String msg) { this .msg = msg; } public T getData () { return data; } public void setData (T data) { this .data = data; } private String msg; private T data; } class UserInfo { private String name; public String getName () { return name; } public void setName (String name) { this .name = name; } @Override public String toString () { return "UserInfo{" + "name='" + name + '\'' + ", Id='" + Id + '\'' + '}' ; } public String getId () { return Id; } public void setId (String id) { Id = id; } private String Id; } class CardInfo { public String getCardNo () { return cardNo; } public void setCardNo (String cardNo) { this .cardNo = cardNo; } @Override public String toString () { return "CardInfo{" + "cardNo='" + cardNo + '\'' + '}' ; } private String cardNo; } }