Java中將JSON反序列化為泛型對象

將嵌套List的Map轉(zhuǎn)換為Json應(yīng)該都沒什么問題,使用Gson和Jackson都能實現(xiàn),在Gson中使用new Gson().toJson()方法,在Jackson中使用new ObjectMapper().writeValueAsString()即可。
將json轉(zhuǎn)換為形如Map<String,List<Long>>的時候遇到了一點問題,雖然返回類型是Map<String,List<Long>>但是,Map的value的值卻并不是List<Long>,而是Integer類型的,這里面顯然是有問題的,查看Jackson的源碼和Gson的源碼發(fā)現(xiàn)
將json反序列化為對象確實有兩個方法,一種適用于泛型對象,一種適用于非泛型的一般對象。

使用Gson

在gson中將json字符串轉(zhuǎn)反序列化為對象有兩個方法:


  /**
   * This method deserializes the specified Json into an object of the specified class. It is not
   * suitable to use if the specified class is a generic type since it will not have the generic
   * type information because of the Type Erasure feature of Java. Therefore, this method should not
   * be used if the desired type is a generic type. Note that this method works fine if the any of
   * the fields of the specified object are generics, just the object itself should not be a
   * generic type. For the cases when the object is of generic type, invoke
   * {@link #fromJson(String, Type)}. If you have the Json in a {@link Reader} instead of
   * a String, use {@link #fromJson(Reader, Class)} instead.
   *
   * @param <T> the type of the desired object
   * @param json the string from which the object is to be deserialized
   * @param classOfT the class of T
   * @return an object of type T from the string. Returns {@code null} if {@code json} is {@code null}.
   * @throws JsonSyntaxException if json is not a valid representation for an object of type
   * classOfT
   */
 public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {
    Object object = fromJson(json, (Type) classOfT);
    return Primitives.wrap(classOfT).cast(object);
  }

  /**
   * This method deserializes the specified Json into an object of the specified type. This method
   * is useful if the specified object is a generic type. For non-generic objects, use
   * {@link #fromJson(String, Class)} instead. If you have the Json in a {@link Reader} instead of
   * a String, use {@link #fromJson(Reader, Type)} instead.
   *
   * @param <T> the type of the desired object
   * @param json the string from which the object is to be deserialized
   * @param typeOfT The specific genericized type of src. You can obtain this type by using the
   * {@link com.google.gson.reflect.TypeToken} class. For example, to get the type for
   * {@code Collection<Foo>}, you should use:
   * <pre>
   * Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
   * </pre>
   * @return an object of type T from the string. Returns {@code null} if {@code json} is {@code null}.
   * @throws JsonParseException if json is not a valid representation for an object of type typeOfT
   * @throws JsonSyntaxException if json is not a valid representation for an object of type
   */
  @SuppressWarnings("unchecked")
  public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
    if (json == null) {
      return null;
    }
    StringReader reader = new StringReader(json);
    T target = (T) fromJson(reader, typeOfT);
    return target;
  }

觀察fromJson(String json, Class<T> classOfT)的注釋:

It is not suitable to use if the specified class is a generic type since it will not have the generic type information because of the Type Erasure feature of Java

也就是說,由于Java泛型的擦除機制,這個方法不適用于傳入泛型的類,比如Map<String,Long>,List<String>等,這個時候可以用T fromJson(String json, Type typeOfT)替代。

下面還有一段話:

Note that this method works fine if the any of the fields of the specified object are generics, just the object itself should not be a generic type

** 注意:** 如果對象不是泛型的,只是字段是泛型的話這個方法是可以使用的

剛開始不太理解這句話,后來想通了,也就是類定義上不能帶有泛型比如 public interface Map<K,V> 這樣的就不行,但是如果是下面這樣的只有域上帶有的泛型是可以:

static class JsonDemo{

        private List<Long> list;

        public List<Long> getList() {
            return list;
        }

        public void setList(List<Long> list) {
            this.list = list;
        }
    }

下面的fromJson(String json, Type typeOfT)就是專門提供給泛型類的對象使用的,如果你自己反序列化的對象帶有泛型的話需要用這個方法。

使用Jackson

和gson一樣,jackson也提供了兩個方法,一個適用于普通的類,一個適用于泛型類,只不過jackson源碼的注釋沒有Gson的豐富,從注釋上看不出來,功能和Gson的一致。

  /**
     * Method to deserialize JSON content from given JSON content String.
     * 
     * @throws IOException if a low-level I/O problem (unexpected end-of-input,
     *   network error) occurs (passed through as-is without additional wrapping -- note
     *   that this is one case where {@link DeserializationFeature#WRAP_EXCEPTIONS}
     *   does NOT result in wrapping of exception even if enabled)
     * @throws JsonParseException if underlying input contains invalid content
     *    of type {@link JsonParser} supports (JSON for default case)
     * @throws JsonMappingException if the input JSON structure does not match structure
     *   expected for result type (or has other mismatch issues)
     */
   @SuppressWarnings("unchecked")
    public <T> T readValue(String content, Class<T> valueType)
        throws IOException, JsonParseException, JsonMappingException
    {
        return (T) _readMapAndClose(_jsonFactory.createParser(content), _typeFactory.constructType(valueType));
    } 

    /**
     * Method to deserialize JSON content from given JSON content String.
     * 
     * @throws IOException if a low-level I/O problem (unexpected end-of-input,
     *   network error) occurs (passed through as-is without additional wrapping -- note
     *   that this is one case where {@link DeserializationFeature#WRAP_EXCEPTIONS}
     *   does NOT result in wrapping of exception even if enabled)
     * @throws JsonParseException if underlying input contains invalid content
     *    of type {@link JsonParser} supports (JSON for default case)
     * @throws JsonMappingException if the input JSON structure does not match structure
     *   expected for result type (or has other mismatch issues)
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public <T> T readValue(String content, TypeReference valueTypeRef)
        throws IOException, JsonParseException, JsonMappingException
    {
        return (T) _readMapAndClose(_jsonFactory.createParser(content), _typeFactory.constructType(valueTypeRef));
    } 

簡單實驗

使用兩種方式反序列一個json,使用Class來反序列化泛型類型的對象,在printType的時候會出現(xiàn)ClassCastException類型轉(zhuǎn)換異常。

package org.xuan;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * Created by zhaohongxuan
 */
public class JsonTest {
    private static ObjectMapper mapper = new ObjectMapper();
    private static Gson gson = new Gson();
    public static void main(String[] args) throws IOException {
        Map<String, List<Long>> map = Maps.newHashMap();
        map.put("one", Arrays.asList(10001L, 10002L, 10003L, 10004L));
        map.put("two", Arrays.asList(20001L, 20002L, 20003L, 20004L));
        map.put("three", Arrays.asList(30001L, 30002L, 30003L, 30004L));
        map.put("four", Arrays.asList(40001L, 40002L, 40003L, 40004L));

        String json = new Gson().toJson(map);
        System.err.println("=======================錯誤示范=====================");
        //Gson
        Map<String, List<Long>> mapResult  = gson.fromJson(json,Map.class);
        System.out.println("通過Gson轉(zhuǎn)換...");
//      printType(mapResult);
        System.out.println(mapResult);
        //Json
        Map<String, List<Long>> jsonMapResult = mapper.readValue(json,Map.class);
        System.out.println("通過Jackson轉(zhuǎn)換...");
//      printType(jsonMapResult);

        System.out.println(jsonMapResult);
        System.out.println("=======================正確做法=====================");
        //Gson
        Map<String, List<Long>> mapResult1  = gson.fromJson(json,new TypeToken<Map<String, List<Long>>>(){}.getType());
        System.out.println("通過Gson轉(zhuǎn)換...");
        printType(mapResult1);
        System.out.println(mapResult1);
        //Json
        ObjectMapper mapper = new ObjectMapper();
        Map<String, List<Long>> jsonMapResult1 = mapper.readValue(json,new TypeReference< Map<String,List<Long>>>() {});
        System.out.println("通過Jackson轉(zhuǎn)換...");
        printType(jsonMapResult1);

        System.out.println(jsonMapResult1);

    }

    public static void printType(Map<String, List<Long>> map){
        for (Map.Entry<String, List<Long>> entry: map.entrySet()){
            System.out.println("key 類型:"+entry.getKey().getClass()+", value類型:"
            +entry.getValue().getClass()+", List中元素類型"+entry.getValue().get(0).getClass());
        }

    }
}


總 結(jié)

在Gson中:
如果使用fromJson(String json, Class<T> classOfT)來反序列化Map的話,不會造成編譯錯誤,返回的類型就會變化,Long類型變成了Double類型,使用的時候就會出現(xiàn)異常,例如在遍歷Map的entrySet的時候就會出現(xiàn)異常。

    java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.Long

因此:

  1. 反序列化泛型對象如Map<K,V>等需要使用 fromJson(String json, Type typeOfT)
  2. 一般對象使用fromJson(String json, Class<T> classOfT)
    在Jackson中:
    如果使用T readValue(String content, Class<T> valueType)來反序列化Map的話,返回的類型就會由Long類型變成了Integer類型。
  3. 反序列化泛型對象如Map<K,V>等需要使用 T readValue(String content, TypeReference valueTypeRef)
  4. 一般對象使用T readValue(String content, Class<T> valueType)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.概述2.Gson的目標3.Gson的性能和擴展性4.Gson的使用者5.如何使用Gson 通過Maven來使用...
    人失格閱讀 14,557評論 2 18
  • 為了更好的學習Gson,特將Gson User Guide翻譯如下。由于本人英文水平有限,如有錯誤,還請指正,謝謝...
    WeberLisper閱讀 7,059評論 0 6
  • 為了這通電話,我準備了兩天,或者說兩年。 兩年前,就跟你們失去了聯(lián)系,從此開始了對你的苦苦追尋,我們甚至還去到了你...
    五月成長筆記閱讀 238評論 0 0
  • 今天不知怎么回事,總有心亂如麻的感覺,準確的說 我感到大廈將傾 我感到莫大的壓力。 秋天很認真地到了。 ...
    南無NAMO閱讀 150評論 0 0
  • 前幾天過完年返程的路途中,有這樣一個故事。一對夫妻,30多歲左右,兩人只有一張有座位的票,還有一張站票。理所當然的...
    愛思考的安妮閱讀 395評論 0 0

友情鏈接更多精彩內(nèi)容