2013-07-17 11 views
7

Mam api, który zwraca JSON. Odpowiedź jest w pewnym formacie, który może zmieścić się w obiekcie o nazwie ApiResult i zawiera kod Context <T> i int.Konwertuj ciąg JSON na ogólny obiekt w języku JAVA (z GSON)

ApiResult jest zadeklarowany w sposób ogólny, np. ApiResult<SomeObject>

Chciałbym wiedzieć, jak uzyskać GSON do konwersji przychodzącego JSON String do ApiResult<T>

Do tej pory mam:

Type apiResultType = new TypeToken<ApiResult<T>>() { }.getType(); 
ApiResult<T> result = gson.fromJson(json, apiResultType); 

Ale to wciąż powraca konwertuje kontekstowe w LinkedHashMap zamiast (które zakładam, do czego cofa się GSON)

Odpowiedz

1

Musisz podać Gsonowi typ T. Ponieważ gson nie wie, jaki adapter powinien zostać zastosowany, po prostu zwraca strukturę danych.

Twoje musieli zapewnić ogólne, jak:

Type apiResultType = new TypeToken<ApiResult<String>>() { }.getType(); 

Jeśli typ T jest znana tylko w czasie wykonywania, używam czegoś trudne:

static TypeToken<?> getGenToken(final Class<?> raw, final Class<?> gen) throws Exception { 
    Constructor<ParameterizedTypeImpl> constr = ParameterizedTypeImpl.class.getDeclaredConstructor(Class.class, Type[].class, Type.class); 
    constr.setAccessible(true); 
    ParameterizedTypeImpl paramType = constr.newInstance(raw, new Type[] { gen }, null); 

    return TypeToken.get(paramType); 
    } 

Rozmowa byłaby (ale zastępując String.class ze zmienną):

Type apiResultType = getGenToken(ApiResult.class, String.class); 
+0

Więc biorę, że T jest typu String w przykładzie . Ale nie mam Stringa, ale T. Jak zmieni się ostatnie stwierdzenie (przypisanie)? – Yannis

+0

Tak, zakładałem, że masz 'Class ' var, aby ustawić zamiast 'String.class' (w' getGenToken'). Ale może to nie dotyczy twojej sprawy. – PomPom

4

Musisz wiedzieć, kim będzie T. Nadchodzący JSON to zasadniczo tylko tekst. GSON nie ma pojęcia, jaki obiekt ma się stać. Jeśli istnieje coś w tym JSON, które mogą trop off, aby utworzyć instancję T, można zrobić coś takiego:

public static class MyJsonAdapter<X> implements JsonDeserializer<ApiResult<X>> 
{ 
    public ApiResult<X> deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context) 
     throws JsonParseException 
    { 
     String className = jsonElement.getAsJsonObject().get("_class").getAsString(); 
     try 
     { 
     X myThing = context.deserialize(jsonElement, Class.forName(className)); 
     return new ApiResult<>(myThing); 
     } 
     catch (ClassNotFoundException e) 
     { 
     throw new RuntimeException(e); 
     } 
    } 
} 

Używam pole „_class”, aby zdecydować, co mój X musi być i tworzenie ich za pomocą odbicia (podobnie jak w przypadku PomPom). Prawdopodobnie nie ma tak oczywistego pola, ale musi być jakiś sposób, aby spojrzeć na JsonElement i zdecydować na podstawie tego, jaki jest jego typ X.

Kod ten jest zmodyfikowaną wersją coś podobnego zrobiłem z GSON jakiś czas temu, patrz linia 184+ w: https://github.com/chriskessel/MyHex/blob/master/src/kessel/hex/domain/GameItem.java

+0

Sprawdzę kod i podejrzewam, że musiałem zrobić coś takiego jak sugerowałeś, ale ten sam (lub podobny) kod działa w .NET. Czy to oznacza, że ​​muszę zmienić ogólny interfejs API, aby dostosować go do konkretnego języka? .NET może znaleźć w czasie wykonywania, co jest T bez konieczności posiadania specjalnego pola _class w jsonie. – Yannis

+0

Bez znajomości kodu .NET, nie mam pojęcia, jak to zadziała. T nie ma wartości klasy, więc .NET nie wie, której klasy użyć, chyba że coś jej powie. –

0

używam JacksonJson biblioteka, całkiem podobny do GSon. Jest to możliwe do konwersji json ciąg do jakiegoś rodzajowego typu obiektu w ten sposób:

String data = getJsonString(); 
ObjectMapper mapper = new ObjectMapper(); 
List<AndroidPackage> packages = mapper.readValue(data, List.class); 

Może to jest właściwy sposób z GSON w przypadku:

ApiResult<T> result = gson.fromJson(json, ApiResult.class);