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;
    }
}

results matching ""

    No results matching ""