Google Gson-목록 <class> 객체를 직렬화 해제 하시겠습니까? (일반 타입)


441

Google Gson을 통해 목록 객체를 전송하고 싶지만 일반 유형을 역 직렬화하는 방법을 모르겠습니다.

이것을 보고 난 후에 시도한 것 (BalusC의 답변) :

MyClass mc = new Gson().fromJson(result, new List<MyClass>(){}.getClass());

그러나 "새 List () {} 형식은 상속 된 추상 메소드를 구현해야합니다 ..."라는 식의 오류가 발생합니다. 빠른 수정을 사용하면 20 개가 넘는 메소드 스텁의 몬스터가 생깁니다.

더 쉬운 해결책이 있다고 확신하지만 찾을 수없는 것 같습니다!

편집하다:

지금 나 한테있어

Type listType = new TypeToken<List<MyClass>>()
                {
                }.getType();

MyClass mc = new Gson().fromJson(result, listType);

그러나 "fromJson"줄에서 다음 예외가 발생합니다.

java.lang.NullPointerException
at org.apache.harmony.luni.lang.reflect.ListOfTypes.length(ListOfTypes.java:47)
at org.apache.harmony.luni.lang.reflect.ImplForType.toString(ImplForType.java:83)
at java.lang.StringBuilder.append(StringBuilder.java:203)
at com.google.gson.JsonDeserializerExceptionWrapper.deserialize(JsonDeserializerExceptionWrapper.java:56)
at com.google.gson.JsonDeserializationVisitor.invokeCustomDeserializer(JsonDeserializationVisitor.java:88)
at com.google.gson.JsonDeserializationVisitor.visitUsingCustomHandler(JsonDeserializationVisitor.java:76)
at com.google.gson.ObjectNavigator.accept(ObjectNavigator.java:106)
at com.google.gson.JsonDeserializationContextDefault.fromJsonArray(JsonDeserializationContextDefault.java:64)
at com.google.gson.JsonDeserializationContextDefault.deserialize(JsonDeserializationContextDefault.java:49)
at com.google.gson.Gson.fromJson(Gson.java:568)
at com.google.gson.Gson.fromJson(Gson.java:515)
at com.google.gson.Gson.fromJson(Gson.java:484)
at com.google.gson.Gson.fromJson(Gson.java:434)

내가 캐치 JsonParseExceptions 및 "결과를"null이 아닙니다.

디버거로 listType을 확인하고 다음을 얻었습니다.

  • 목록 유형
    • args = ListOfTypes
      • 목록 = null
      • resolvedTypes = 유형 [1]
    • 로더 = PathClassLoader
    • ownerType0 = null
    • ownerTypeRes = null
    • rawType = 클래스 (java.util.ArrayList)
    • rawTypeName = "java.util.ArrayList"

"getClass"호출이 제대로 작동하지 않은 것 같습니다. 어떤 제안 ...?

Edit2 : Gson User Guide 에서 확인했습니다 . 제네릭 형식을 Json으로 구문 분석하는 동안 발생하는 런타임 예외에 대해 설명합니다. 예제에서와 같이 "잘못된"(위에 표시되지 않음)을 수행했지만 예외는 전혀 얻지 못했습니다. 따라서 사용자 안내서에서 제안한대로 직렬화를 변경했습니다. 그래도 도움이되지 않았습니다.

편집 3 : 해결되었습니다. 아래 답변을 참조하십시오.


1
당신이 지적한 대답은를 사용합니다 TokenType. 그런 식으로 시도 했습니까?
Nishant

답과 같은 힌트를 얻었습니다. 다음에는 예제를 자세히 살펴 보겠습니다. ;)
해파리

유형 토큰으로 목록 구현을 시도 할 수 있습니까? 원시 유형은 배열 목록이므로 배열 목록을 사용해보십시오.
uncaught_exceptions

답변:


954

일반 컬렉션을 역 직렬화하는 방법 :

import java.lang.reflect.Type;
import com.google.gson.reflect.TypeToken;

...

Type listType = new TypeToken<ArrayList<YourClass>>(){}.getType();
List<YourClass> yourClassList = new Gson().fromJson(jsonArray, listType);

의견에 여러 사람들이 언급 했으므로 다음은 TypeToken수업이 어떻게 사용 되는지에 대한 설명입니다 . 구조는 new TypeToken<...>() {}.getType()컴파일 시간 유형 (사이 캡처 <>런타임으로) java.lang.reflect.Type객체. Class원시 (삭제 된) 유형 만 나타낼 수 있는 객체 와 달리 객체 Type는 일반 유형의 매개 변수화 된 인스턴스화를 포함하여 Java 언어의 모든 유형을 나타낼 수 있습니다.

TypeToken당신이 직접 구성 안하고 있기 때문에 클래스 자체는, public 생성자가 없습니다. 대신 항상 익명 서브 클래스를 구성합니다 (따라서이 {}표현식의 필수 부분입니다).

유형 삭제로 인해 TypeToken클래스는 컴파일 타임에 완전히 알려진 유형 만 캡처 할 수 있습니다. 즉, new TypeToken<List<T>>() {}.getType()type 매개 변수 는 수행 할 수 없습니다 T.

자세한 내용 은 해당 TypeToken클래스설명서를 참조하십시오 .


31
새로운 버전의 GSON에서는 TypeToken 생성기가 공개되어 있지 않으므로 생성자가 보이지 않는 오류가 발생합니다. 이 경우 어떻게해야합니까?
Pablo

8
실제 버전의 GSON (2.2.4)을 사용하면 다시 작동합니다. 여기에서 생성자에 액세스 할 수 있습니다.

내 json 객체는 객체로 시작한 다음 원하는 객체의 배열을 포함합니다 { "myObjectArray":[ {....} , {....} , {....} ] }. 모델 파일을 만들었습니다. {....}이 일반 컬렉션 코드를 가져와 루트 요소가 새로운 중첩 된 객체 파일을 만들지 않고 배열이라고 가정하지 않는 방법
CQM

7
다음과 같은 가져 오기가 필요합니다. --- import java.lang.reflect.Type; import com.google.gson.reflect.TypeToken
Umair Saleem 2016 년

4
YourClass가 코드로 고정되어 있으면 좋습니다. 수업이 런타임에 오면 어떻게 되나요?
jasxir

273

또 다른 방법은 배열을 유형으로 사용하는 것입니다. 예 :

MyClass[] mcArray = gson.fromJson(jsonString, MyClass[].class);

이렇게하면 Type 객체로 인한 번거 로움을 피할 수 있으며 실제로 목록이 필요한 경우 항상 다음을 통해 배열을 목록으로 변환 할 수 있습니다.

List<MyClass> mcList = Arrays.asList(mcArray);

IMHO 이것은 훨씬 더 읽기 쉽습니다.

그리고 실제 목록 (수정 가능, 제한 사항 참조 Arrays.asList())으로 만들려면 다음을 수행하십시오.

List<MyClass> mcList = new ArrayList<>(Arrays.asList(mcArray));

4
대단해! 리플렉션과 함께 사용하려면 어떻게해야합니까? 나는 MyClass가치를 모른다. 그리고 그것은 동적으로 정의 될 것이다!
Amin Sh

1
참고 : 이것으로 mcList가 본격적인 목록이 아닌지주의하십시오. 많은 것들이 작동하지 않습니다.
njzk2

4
제네릭과 함께 사용하는 방법? T[] yourClassList = gson.fromJson(message, T[].class);// 유형 변수에서 선택할 수 없음
Pawel Cioch

2
그 의견에 @MateusViccari는 mcList이 답변에 대한 호출의 결과였습니다 Arrays.asList. 이 메소드는 모든 선택적 메소드가 구현되지 않은 상태에서 예외를 발생시키는 목록을 리턴합니다. 예를 들어, 해당 목록에 요소를 추가 할 수 없습니다. 나중에 편집 할 수 있듯이 Arrays.asList제한 사항이 있으며 실제로 래핑하면 ArrayList많은 경우에 더 유용한 목록을 얻을 수 있습니다.
njzk2

2
런타임에 임의의 요소 유형에 대해 배열 유형을 구성해야하는 경우 David Wood 's answer에Array.newInstance(clazz, 0).getClass() 설명 된대로 사용할 수 있습니다 .
Daniel Pryden 12

31

이 게시물을 참조하십시오. GSON의 인수로 Java 유형 일반

이에 대한 더 나은 해결책이 있습니다. 다음은 목록에 대한 래퍼 클래스입니다. 래퍼가 정확한 유형의 목록을 저장할 수 있습니다.

public class ListOfJson<T> implements ParameterizedType
{
  private Class<?> wrapped;

  public ListOfJson(Class<T> wrapper)
  {
    this.wrapped = wrapper;
  }

  @Override
  public Type[] getActualTypeArguments()
  {
      return new Type[] { wrapped };
  }

  @Override
  public Type getRawType()
  {
    return List.class;
  }

  @Override
  public Type getOwnerType()
  {
    return null;
  }
}

그런 다음 코드는 간단 할 수 있습니다.

public static <T> List<T> toList(String json, Class<T> typeClass)
{
    return sGson.fromJson(json, new ListOfJson<T>(typeClass));
}

무엇입니까 mEntity.rulePattern?
Al Lelopath

테스트 용 샘플 개체 일뿐입니다. 신경 쓰지 않아도됩니다. toList 메소드를 사용하면 모든 것이 잘됩니다.
행복한

@ Happier이 Gson 2.8.2를 구현하려고하는데 작동하지 않는 것 같습니다. 어떤 기회 stackoverflow.com/questions/50743932/… 당신은보고 내가 잃어버린 것을 알려주세요
Praveen

1
@Praveen 2.8.2 에서이 방법을 시도했지만 원래대로 작동합니다.
행복한

31

이후 Gson 2.8, 우리는 다음과 같은 util 함수를 만들 수 있습니다

public <T> List<T> getList(String jsonArray, Class<T> clazz) {
    Type typeOfT = TypeToken.getParameterized(List.class, clazz).getType();
    return new Gson().fromJson(jsonArray, typeOfT);
}

사용 예

String jsonArray = ...
List<User> user = getList(jsonArray, User.class);

2
TypeToken#getParameterized더 좋은 방법이 익명의 서브 클래스로 해킹 보인다
니콜라이 Kulachenko

이것은 받아 들여지는 대답이어야합니다. 적어도 최신 버전의 경우
ccpizza

2
이것이 완벽한 해답입니다. 내 문제를 해결합니다.
donmj

메소드를 "있는 그대로"복사했지만 작동하지 않습니다. 컴파일러에서 "TypeToken 유형에 대해 getParameterized (Class <List>, Class <T>) 메소드가 정의되어 있지 않습니다"라고 말합니다. 내가 모두 내 GSON 버전 (2.8.0) 및 문서를 확인하고 모든이 측면에 괜찮습니다 ... 나는 잘 작동 @Happier 솔루션을 사용하여 종료
leguminator

@leguminator TypeToken을 올바르게 가져 왔습니까? 그리고 당신은 자바 또는 kotlin을 사용하고 있습니다. 다시 테스트하려고합니다
판 반 린

26

같은 결과를 얻는 또 다른 방법 인 Wep. 가독성을 위해 사용합니다.

이 읽기 어려운 문장을 수행하는 대신 :

Type listType = new TypeToken<ArrayList<YourClass>>(){}.getType();
List<YourClass> list = new Gson().fromJson(jsonArray, listType);

객체의 목록을 확장하는 빈 클래스를 만듭니다.

public class YourClassList extends ArrayList<YourClass> {}

JSON을 파싱 할 때 사용하십시오.

List<YourClass> list = new Gson().fromJson(jsonArray, YourClassList.class);

9

코 틀린의 경우 :

import java.lang.reflect.Type
import com.google.gson.reflect.TypeToken
...
val type = object : TypeToken<List<T>>() {}.type

또는 다음은 유용한 기능입니다.

fun <T> typeOfList(): Type {
    return object : TypeToken<List<T>>() {}.type
}

그런 다음 사용하십시오.

val type = typeOfList<YourMagicObject>()

코드를 사용하여 reified 유형을 사용하여이 함수를 만들고 inline fun <reified T> buildType() = object : TypeToken<T>() {}.type!!List 유형으로 호출했습니다.buildType<List<YourMagicObject>>()
coffeemakr

@coffeemakr 여기서는 유형을 조정할 필요가 없습니다.
채드 빙엄

오. 그러나 왜 ArrayList의 유형 토큰을 만들고 buildType일반 유형으로 함수를 호출합니까? 오타입니까? -이 ArrayList를 <ArrayList를 <YourMagicObject >> 만들 것
coffeemakr

@coffeemakr 아, 예. 오타
차드 빙엄

7
public static final <T> List<T> getList(final Class<T[]> clazz, final String json)
{
    final T[] jsonToObject = new Gson().fromJson(json, clazz);

    return Arrays.asList(jsonToObject);
}

예:

getList(MyClass[].class, "[{...}]");

좋아요 그러나 이것은 DevNG2 년 전에 작성된 위의 답변과 중복됩니다 : stackoverflow.com/a/17300003/1339923 (그리고이 접근법에 대한주의 사항에 대한 답을 읽으십시오)
Lambart

6

원래 질문에 대한 답변으로 doc_180의 답변을 수락했지만 누군가이 문제가 다시 발생하면 내 질문의 후반에도 답변합니다.

내가 설명한 NullPointerError는 List 자체와 관련이 없지만 그 내용과 관련이 있습니다!

"MyClass"클래스에는 "args 없음"생성자가없고 수퍼 클래스 클래스도 없습니다. 간단한 "MyClass ()"생성자를 MyClass 및 해당 수퍼 클래스에 추가하면 doc_180에서 제안한대로 목록 직렬화 및 역 직렬화를 포함하여 모든 것이 제대로 작동했습니다.


1
추상 클래스 목록이 있으면 동일한 오류가 발생합니다. "클래스를 인스턴스화 할 수 없습니다"에 대한 GSON의 일반 오류 메시지 인 것 같습니다.
Drew

생성자를 추가하는 방법에 대한 팁을 통해 왜 모든 null 값이 있었는지 알 수있었습니다. JSON 문자열에 "To"및 "From"과 같은 필드 이름이 있지만 소문자로 오브젝트의 해당 필드가 "to"및 "from"이므로 생략되었습니다.
Rune

4

다음은 동적으로 정의 된 형식으로 작동하는 솔루션입니다. 요령은 Array.newInstance ()를 사용하여 적절한 유형의 배열을 만드는 것입니다.

    public static <T> List<T> fromJsonList(String json, Class<T> clazz) {
    Object [] array = (Object[])java.lang.reflect.Array.newInstance(clazz, 0);
    array = gson.fromJson(json, array.getClass());
    List<T> list = new ArrayList<T>();
    for (int i=0 ; i<array.length ; i++)
        list.add(clazz.cast(array[i]));
    return list; 
}

이 응답은로 class.cast()전송하여 확인되지 않은 경고를 피하는 데 더 좋습니다 (T). 또는 더 나은 아직 캐스팅을 방해하지 않고 Arrays.asList()배열에서를로 변환 하는 것과 같은 것을 사용 하십시오 List<T>. 또한 길이를 전달할 필요가 없습니다 Array.newInstance()-크기가 0 인 배열은 호출하기 getClass()에 충분합니다 .
Daniel Pryden 12

제안 해 주셔서 감사합니다. 제안한 내용을 변경하고 위의 게시물을 업데이트했습니다.
David Wood

2

하나 더 가능성을 추가하고 싶습니다. TypeToken을 사용하지 않고 json 객체 배열을 ArrayList로 변환하려면 다음과 같이 진행하십시오.

json 구조가 다음과 같은 경우 :

{

"results": [
    {
        "a": 100,
        "b": "value1",
        "c": true
    },
    {
        "a": 200,
        "b": "value2",
        "c": false
    },
    {
        "a": 300,
        "b": "value3",
        "c": true
    }
]

}

수업 구조는 다음과 같습니다.

public class ClassName implements Parcelable {

    public ArrayList<InnerClassName> results = new ArrayList<InnerClassName>();
    public static class InnerClassName {
        int a;
        String b;
        boolean c;      
    }
}

그런 다음 다음과 같이 구문 분석 할 수 있습니다.

Gson gson = new Gson();
final ClassName className = gson.fromJson(data, ClassName.class);
int currentTotal = className.results.size();

이제 className 객체의 각 요소에 액세스 할 수 있습니다.


1

Gson의 '유형'클래스 이해에 대해서는 예 2를 참조하십시오.

예제 1 :이 deserilizeResturant에서 Employee [] 배열을 사용하여 세부 사항을 얻었습니다.

public static void deserializeResturant(){

       String empList ="[{\"name\":\"Ram\",\"empId\":1},{\"name\":\"Surya\",\"empId\":2},{\"name\":\"Prasants\",\"empId\":3}]";
       Gson gson = new Gson();
       Employee[] emp = gson.fromJson(empList, Employee[].class);
       int numberOfElementInJson = emp.length();
       System.out.println("Total JSON Elements" + numberOfElementInJson);
       for(Employee e: emp){
           System.out.println(e.getName());
           System.out.println(e.getEmpId());
       }
   }

예 2 :

//Above deserilizeResturant used Employee[] array but what if we need to use List<Employee>
public static void deserializeResturantUsingList(){

    String empList ="[{\"name\":\"Ram\",\"empId\":1},{\"name\":\"Surya\",\"empId\":2},{\"name\":\"Prasants\",\"empId\":3}]";
    Gson gson = new Gson();

    // Additionally we need to se the Type then only it accepts List<Employee> which we sent here empTypeList
    Type empTypeList = new TypeToken<ArrayList<Employee>>(){}.getType();


    List<Employee> emp = gson.fromJson(empList, empTypeList);
    int numberOfElementInJson = emp.size();
    System.out.println("Total JSON Elements" + numberOfElementInJson);
    for(Employee e: emp){
        System.out.println(e.getName());
        System.out.println(e.getEmpId());
    }
}

0

kays1의 답변을 좋아했지만 구현할 수 없었습니다. 그래서 나는 그의 개념을 사용하여 나만의 버전을 만들었습니다.

public class JsonListHelper{
    public static final <T> List<T> getList(String json) throws Exception {
        Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
        Type typeOfList = new TypeToken<List<T>>(){}.getType();
        return gson.fromJson(json, typeOfList);
    }
}

용법:

List<MyClass> MyList= JsonListHelper.getList(jsonArrayString);

컴파일 타임에 T를 사용하려고하기 때문에 분명히 작동하지 않습니다. 이것은 효과적으로 StringMap의 목록으로 직렬화를 해제합니다.
JHH

0

내 경우에는 @uncaught_exceptions의 답변이 효과가 없었습니다. List.class대신 java.lang.reflect.Type다음 을 사용해야 했습니다 .

String jsonDuplicatedItems = request.getSession().getAttribute("jsonDuplicatedItems").toString();
List<Map.Entry<Product, Integer>> entries = gson.fromJson(jsonDuplicatedItems, List.class);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.