Gson
Gson為google發布的library。主要為方便將Java物件序列化Serialization至輕量化的封包格式JSON,或自JSON 反序列化Deserialization至JAVA物件,
簡單來說,就是Java與JSON格式的轉換上更為直覺,對應複雜的JSON資料時增加轉換效率。
相關library還有Jackson, JSON.simple, JSONP...等等。
參考文件
ref | link |
---|---|
github | https://github.com/google/gson |
API document | http://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/index.html |
先備知識
JSON是一種輕量級的數據交換語言,以文字為基礎,且易於讓人閱讀。基本上是一個鍵值對所組成:
大括弧代表物件(object)
{name:value}
中括弧代表陣列(array)
[collection, collection]
Ex:
{
"firstName": "John",
"lastName": "Smith",
"sex": "male",
"age": 25,
"address":
{
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021"
},
"phoneNumber":
[
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "fax",
"number": "646 555-4567"
}
]
}
注意事項
JSONObject及JSONArray(大寫),為原來JSON物件,
JsonObject及JsonArray(小寫),才是Gson library物件,使用時要注意是否import com.google.gson
如何使用
gradle設置
compile 'com.google.code.gson:gson:2.3.1'
基本應用
1.序列化-基本型態
Gson gson = new Gson();
gson.toJson(1); //int
gson.toJson("abcd"); //String
gson.toJson(new Long(10)); //Long
gson.toJson(true); //boolean
gson.toJson(new Date()); //Date
int[] values = {1, 2, 3};
gson.toJson(values); //array
2.反序列化-基本型態
int one = gson.fromJson("1", int.class);
Integer oneInteger = gson.fromJson("1", Integer.class);
Long oneLong = gson.fromJson("1", Long.class);
Boolean falseBoolean = gson.fromJson("false", Boolean.class);
String str = gson.fromJson("\"abc\"", String.class);
3.序列化後,Gson回傳JSON型態的string:
String jsonString = gson.toJson("John");
String valueString = gson.toJson(values);
String booleanString = gson.toJson(true);
String dateString = gson.toJson(new Date());
//print result
"John"
[1,2,3]
true
"Aug 4, 2015 11:16:48 PM"
4.可針對custom object做操作
//序列化user object
String custom = gson.toJson(new User());
class User {
private int id = 0;
private String name = "Hank";
private int gender = 0;
private int age = 35;
private double height = 0.0;
private boolean expired = false;
private Date registerDate = new Date();
private long lastUpdateTime = new Date().getTime();
public User() {
}
}
//輸出結果
{"id":0,"name":"Hank","gender":0,"age":35,"height":0.0,"expired":false,"registerDate":"Aug 4, 2015 11:16:48 PM","lastUpdateTime":1438701408703}
//反序列化至user object
User user = gson.fromJson(custom, User.class);
進階範例應用
Gson提供registerTypeAdapter方法,客製化serializers and deserializers,並提供了JsonObject及JsonArray物件解析JSON資料, 以下應用爲示範與Server用JSON資料溝通時,指定反序列的型態,使用JsonObject以及JsonArray將JSON資料分層拆解。 此範例為簡化後的表示,提供完整的概觀呈現,實作時可依功能及server需求做調整。
1.反序列化- custom with JsonObject
public class MyUserResponseTypeAdapter implements
JsonDeserializer<MyServerRespManager<MyUserProfileResponse>> {
@Override
public MyServerRespManager<MyUserProfileResponse> deserialize(JsonElement json,
Type typeOfT, JsonDeserializationContext context)throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
//以key取值, 轉作為目標型態。如果是將JSON型態的string轉為JsonObject,則用JsonParser
int status = jsonObject.get("status").getAsInt(); //http 200, 500 etc...
int version = jsonObject.get("version").getAsInt();
int index = jsonObject.get("index").getAsInt();
MyUserProfileResponse obj = new MyUserProfileResponse(jsonObject.get("obj").getAsJsonObject());
MyServerRespManager<MyUserProfileResponse> response = new MyServerRespManager<MyUserProfileResponse>(status, version, index, obj);
return response;
}
}
一個JsonObject type class,內含User的資料變數
public class MyUserProfileResponse {
private int id = 0;
private String name = "";
private short gender = 0;
private short age = 0;
private double height = 0.0;
List<WGExperienceObj> experiencesList = new ArrayList<WGExperienceObj>();
MyUserProfileResponse(JsonObject obj) {
this.id = obj.get("id").getAsInt();
this.name = obj.get("name").getAsString();
this.gender = obj.get("gender").getAsShort();
this.age = obj.get("age").getAsShort();
this.height = obj.get("height").getAsDouble();
//取出JsonArray
parseExperienceArray(obj.getAsJsonArray("experiences"));
}
//將JsonArray下的多個JsonObject,取出做處理
public void parseExperienceArray(JsonArray experiences) {
for (int i = 0; i < experiences.size(); i++) {
MyExperienceObj experience = new MyExperienceObj(experiences.get(i)
.getAsJsonObject());
experiencesList.add(experience);
}
//...variable getters
}
另一個JsonObject type class,內含User Experience的資料變數
public class MyExperienceObj {
private int eid = 0;
private String title = "";
private String company = "";
private String desc = "";
private boolean ifNow = false;
MyExperienceObj(JsonObject experience) {
this.eid = experience.get("eid").getAsInt();
this.title = experience.get("title").getAsString();
this.company = experience.get("company").getAsString();
this.desc = experience.get("desc").getAsString();
this.ifNow = experience.get("ifNow").getAsBoolean();
}
//...variable getters
}
2.處理Server response以及接收的資料
public class MyServerRespManager<T> {
private int status;
private int version;
private int index;
private T obj;
public MyServerRespManager(int status, int version, int index, T obj) {
this.status = status;
this.version = version;
this.index = index;
this.obj = obj;
}
//可以有其他多型method處理error message時的回傳
3.定義客製化deserializers至Adapter
以registerTypeAdapter定義客製化server回傳資料,以及deserializers
Gson gson = new GsonBuilder().registerTypeAdapter(MyServerRespManager.class,
new MyUserResponseTypeAdapter()).create();
可提供給Retrofit RestAdapter.Builder作為Converter
RestAdapter.Builder restAdapterBuilder = new RestAdapter.Builder();
restAdapterBuilder.setConverter(new GsonConverter(gson));
RestAdapter restAdapter = restAdapterBuilder.build();
Handle Structured JSON
現在許多 JSON 格式的 API,大部分為了彈性,都會在第一層以 JSON Object 的形態實作,讓 API 可以夾帶各種 meta-data 回來。如果有各種不對的操作,也應該直接回傳相對應的 Status code 在 Http Header 內,而不是回傳 200 內容為一個夾帶錯誤訊息的 JSON,而這也是現在比較建議的 API 格式,詳見:http://jsonapi.org/format
簡單的範例 JSON 如下:
取得某個使用者資料的 API /api/users/1
回傳:
{
"data": {
"id": 1,
"name": "Hank"
}
}
public class User {
int id;
String name;
}
取得某群組的 API /api/groups/5
回傳:
{
"data": {
"id": 5,
"title": "Music Club",
"members": [
{
"id": 1,
"name": "Hank"
},
{
"id": 23,
"name": "Alice"
}
]
}
}
public class Group {
int id;
String title;
@SerializedName("members")
List<User> members = new ArrayList<User>();
}
如果要處理這種將真正需要的資料放在 data 裡面這類型的 JSON 回傳,則可使用以下方式: 其實也就是將上面一節的 TypeAdapter 做一個統一的處理。
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new ItemTypeAdapterFactory())
.create();
public class ItemTypeAdapterFactory implements TypeAdapterFactory {
public final static String JSON_DATA = "data";
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<T>() {
@Override
public void write(JsonWriter out, T value) throws IOException {
delegate.write(out, value);
}
@Override
public T read(JsonReader in) throws IOException {
JsonElement jsonElement = elementAdapter.read(in);
if (jsonElement.isJsonObject()) {
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject.has(JSON_DATA)) {
jsonElement = jsonObject.get(JSON_DATA);
}
}
return delegate.fromJsonTree(jsonElement);
}
}.nullSafe();
}
}
Share GSON Model With Realm Database Model
如果你的 Database 剛好使用 Realm 並且想共用你的 GSON API Model,會有一些要注意的地方,相關 ISSUE https://github.com/realm/realm-java/issues/908
設定 GSON 不要去理 Class 內的某些來自 RealmObject 的屬性。
Gson gson = new GsonBuilder()
.setExclusionStrategies(new RealmExclusionStrategy()
.create();
public class RealmExclusionStrategy implements ExclusionStrategy {
@Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getDeclaringClass().equals(RealmObject.class); }
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}