Gson中@JsonAdater註解的幾種方式總結

Gson @JsonAdater註解的幾種方式

總結

可以通過自定義TypeAdapter和TypeAdapterFactory的方式,自定義gson的序列化和反序列規則,TypeAdapterFactory可以拿到上下文gson、TokenType類型;

也可以通過繼承JsonReader重新寫一次代碼,在beginArray和endArray想辦法跳過array的string形式的左右 雙引號", gson.fromJson(myJsonReader, Type)也可以實現 解析時自動將String變為List,但采用註解@JsonAdapter的方式更為正規一些;

問題描述

json字符串:

{
    "cityIds": "[1,2,1001,13131]",
    "types": "[{\"name\": \"biz\",\"details\": [1]}]"
}

java對象定義:

 @Data
    public static class RequestParams {
       // json字符串裡對應的是String
        private List<TypeItem> types;
        private List<Integer> cityIds;
    }
    @Data
    public static class TypeItem {
        private String name;
        private List<Integer> details;
    }

可以看到json裡面cityIds和types都是String,而pojo裡則是List,使用gson的fromJson將json轉為pojo會發生報錯

方式一

  @JsonAdapter(StringCollectionTypeAdapterFactory.class)
     private List<TagItem> tags;
     @JsonAdapter(StringCollectionTypeAdapterFactory.class)
     private List<Integer> cityIds;
public static class StringCollectionTypeAdapterFactory implements TypeAdapterFactory {
        @Override
        public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
            // 為瞭write的時候能使用到elementTypeAdapter
            Type type = typeToken.getType();
            Class<? super T> rawType = typeToken.getRawType();
            if (!Collection.class.isAssignableFrom(rawType)) {
                return null;
            }
            Type elementType = $Gson$Types.getCollectionElementType(type, rawType);
            TypeAdapter<?> elementTypeAdapter = gson.getAdapter(TypeToken.get(elementType));
            
            TypeAdapter<T> result = new Adapter(gson, elementTypeAdapter, typeToken);
            return result;
        }
        private static final class Adapter<E> extends TypeAdapter<Collection<E>> {
            private final TypeAdapter<E> elementTypeAdapter;
            private final Gson gson;
            private final TypeToken listType;
            public Adapter(Gson context, TypeAdapter<E> elementTypeAdapter, TypeToken listType) {
                this.elementTypeAdapter = elementTypeAdapter;
                this.gson = context;
                this.listType = listType;
            }
            @Override public Collection<E> read(JsonReader in) throws IOException {
                if (in.peek() == JsonToken.NULL) {
                    in.nextNull();
                    return null;
                }
                List<E> list = gson.fromJson(in.nextString(), listType.getType());
                return list;
            }
 
            // write後可以將array的string格式,重新變成array
            @Override public void write(JsonWriter out, Collection<E> collection) throws IOException {
                if (collection == null) {
                    out.nullValue();
                    return;
                }
                out.beginArray();
                for (E element : collection) {
                    elementTypeAdapter.write(out, element);
                }
                out.endArray();
            }
        }
    }

方式二-write原樣

public static class StringCollectionTypeAdapterFactory1 implements TypeAdapterFactory {
        @Override
        public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
            return new Adapter(gson, typeToken);
        }
        private static final class Adapter<E> extends TypeAdapter<Collection<E>> {
            private final Gson gson;
            private final TypeToken listType;
            public Adapter(Gson context, TypeToken listType) {
                this.gson = context;
                this.listType = listType;
            }
            @Override public Collection<E> read(JsonReader in) throws IOException {
                if (in.peek() == JsonToken.NULL) {
                    in.nextNull();
                    return null;
                }
                List<E> list = gson.fromJson(in.nextString(), listType.getType());
                return list;
            }
            @Override public void write(JsonWriter out, Collection<E> collection) throws IOException {
                if (collection == null) {
                    out.nullValue();
                    return;
                }
                out.value(gson.toJson(collection));
            }
        }
    }

方式三-簡單寫法

private static class CollectionStringTypeAdapterFactory implements TypeAdapterFactory {
        @Override
        public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
            return new TypeAdapter<T>() {
                @Override
                public void write(JsonWriter out, T value) throws IOException {
                    if (value == null) {
                        out.nullValue();
                        return;
                    }
                    gson.getAdapter(type).write(out, value);
                }
                @Override
                public T read(JsonReader in) throws IOException {
                    if (in.peek() == JsonToken.NULL) {
                        return null;
                    }
                    if (in.peek() == JsonToken.BEGIN_ARRAY) {
                        return gson.getAdapter(type).read(in);
                    }
                    T collection = gson.fromJson(in.nextString(), type.getType());
                    return collection;
                }
            };
        }
    }

測試用例如下所示:

@Test
    public void testGson1() {
        Gson gson = new Gson();
        RequestParams requestParam;
        String json;
        // 1.自動將string轉為屬性的List
        json = "{\"id\": \"000000\",\"types\": \"[{\\\"name\\\":\\\"name1\\\",\\\"list\\\":[1,2]},{\\\"name\\\":\\\"name2\\\",\\\"list\\\":[3,4]}]\",\"keywordIds\": \"[12,13]\"}";
        System.out.println(json);
        requestParam = gson.fromJson(json, new TypeToken<RequestParams>() {}.getType());
        Assert.assertTrue(requestParam.getTypes().get(0).getClass().getName().indexOf("TypeItem") >= 0);
        System.out.println(gson.toJson(requestParam));
        // 2.jsonArray也可以轉為List
        json = "{\"id\": \"000000\",\"keywordIds\": [12,13],\"types\": [{\"name\":\"name1\",\"list\":[1,2]},{\"name\":\"name2\",\"list\":[3,4]}]}";
        requestParam = gson.fromJson(json, new TypeToken<RequestParams>() {}.getType());
        Assert.assertTrue(requestParam.getTypes().get(0).getClass().getName().indexOf("TypeItem") >= 0);
        System.out.println(gson.toJson(requestParam));
        // 3.換行的方式呢
        json = "{\n" +
                "\t\"id\": \"000000\",\n" +
                "\t\"keywordIds\": [12,13],\n" +
                "\t\"types\": [{\"name\":\"name1\",\"list\":[1,2]},{\"name\":\"name2\",\"list\":[3,4]}]\n" +
                "}";
        requestParam = gson.fromJson(json, new TypeToken<RequestParams>() {}.getType());
        Assert.assertTrue(requestParam.getTypes().get(0).getClass().getName().indexOf("TypeItem") >= 0);
        System.out.println(gson.toJson(requestParam));
        // 4.能否將List裡面的Integer變成String呢
        json = "{\n" +
                "\t\"id\": \"000000\",\n" +
                "\t\"keywordIds\": [12,13],\n" +
                "\t\"types\": [{\"name\":\"name1\",\"list1\":[1,2]},{\"name\":\"name2\",\"list1\":[3,4]}]\n" +
                "}";
        requestParam = gson.fromJson(json, new TypeToken<RequestParams>() {}.getType());
        Assert.assertTrue(requestParam.getTypes().get(0).getClass().getName().indexOf("TypeItem") >= 0);
        System.out.println(gson.toJson(requestParam));
        // 5.能否將List裡面的Integer變成String
        json = "{\n" +
                "\t\"id\": \"000000\",\n" +
                "\t\"keywordIds1\": [12,13],\n" +
                "\t\"types\": [{\"name\":\"name1\",\"list1\":[1,2]},{\"name\":\"name2\",\"list1\":[3,4]}]\n" +
                "}";
        requestParam = gson.fromJson(json, new TypeToken<RequestParams>() {}.getType());
        Assert.assertTrue(requestParam.getTypes().get(0).getClass().getName().indexOf("TypeItem") >= 0);
        System.out.println(gson.toJson(requestParam));
        // 6.自動將string轉為屬性的List, 並且list裡面的Integer變為String
        json = "{\"id\": \"000000\",\"types\": \"[{\\\"name\\\":\\\"name1\\\",\\\"list1\\\":[1,2]},{\\\"name\\\":\\\"name2\\\",\\\"list1\\\":[3,4]}]\",\"keywordIds1\": \"[12,13]\"}";
        System.out.println(json);
        requestParam = gson.fromJson(json, new TypeToken<RequestParams>() {}.getType());
        Assert.assertTrue(requestParam.getTypes().get(0).getClass().getName().indexOf("TypeItem") >= 0);
        System.out.println(gson.toJson(requestParam));
    }
    @Data
    public static class RequestParams {
        private String id;
        @JsonAdapter(CollectionStringTypeAdapterFactory.class)
        private List<TypeItem> types;
        @JsonAdapter(CollectionStringTypeAdapterFactory.class)
        private List<Integer> keywordIds;
        @JsonAdapter(CollectionStringTypeAdapterFactory.class)
        private List<String> keywordIds1;
    }
    @Data
    public static class TypeItem {
        private String name;
        private List<Integer> list;
        private List<String> list1;
    }

Gson註解

@SerializedName

主要應用在Gson解析json字符串時。Gson能直接將json字符串解析成java對象或者集合,也能將java對象轉換為json字符串表示。例如有json數據如下:

{
    "id":"1"
    "n":"zhangsan"
    "p":"123456"
    "s":"0"
}

它能被解析到下面這個對象

public class User{
    private String id;
    private String n;
    private String p;
    private string s;
}

默認在字段名相同的字段間解析,所以User類必須要這樣寫才能直接使用Gson解析出來,但是java對象裡的屬性名和json裡的字段名有時會不一樣。Gson提供註解的方法來解決這個問題。

public class User{
 
    private String id;
 
    @SerializedName("n")
    private String userName;
 
    @SerializedName("p")
    private String password;
 
    @SerializedName("s")
    private String sex;
}

Expose

通常與@SerializedName連用,當我們不想把某個屬性包含到json中時可以用。

public class UserSimple {  
    @Expose()
    String name; // equals serialize & deserialize
 
    @Expose(serialize = false, deserialize = false)
    String email; 
 
    @Expose(serialize = false)
    int age; 
 
    @Expose(deserialize = false)
    boolean isDeveloper; // equals only serialize
}

序列化的結果將隻有name和isDeveloper出現在json中,因為serialize都是false。反序列化時,java對象將隻會擁有json中的name和age,因為diserialze是true。

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: