Deserialize a List≪T≫ Object With Gson

Deserialize a List T object with Gson?

Method to deserialize generic collection:

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

Since several people in the comments have mentioned it, here's an explanation of how the TypeToken class is being used. The construction new TypeToken<...>() {}.getType() captures a compile-time type (between the < and >) into a runtime java.lang.reflect.Type object. Unlike a Class object, which can only represent a raw (erased) type, the Type object can represent any type in the Java language, including a parameterized instantiation of a generic type.

The TypeToken class itself does not have a public constructor, because you're not supposed to construct it directly. Instead, you always construct an anonymous subclass (hence the {}, which is a necessary part of this expression).

Due to type erasure, the TypeToken class is only able to capture types that are fully known at compile time. (That is, you can't do new TypeToken<List<T>>() {}.getType() for a type parameter T.)

For more information, see the documentation for the TypeToken class.

How to deserialize a list using GSON or another JSON library in Java?

With Gson, you'd just need to do something like:

List<Video> videos = gson.fromJson(json, new TypeToken<List<Video>>(){}.getType());

You might also need to provide a no-arg constructor on the Video class you're deserializing to.

Unable to deserialize the list of object data using GSON library


  1. Why JsonProperty instead of SerializableName? Are you mixing Jackson and Gson?
  2. What is the output of ser.getKeywordStats()?

Because I have tested your code by hardcoding that json-string instead of ser.getKeywordStats(), and it worked without any issue.

Sample Image

Gson deserialize generic list into a generic method

SOLUTION - THIS WORKED FOR ME: Gson TypeToken with dynamic ArrayList item type

public <T> List<T> listEntity(Class<T> clazz)
throws WsIntegracaoException {
try {
// Consuming remote method
String strJson = getService().listEntity(clazz.getName());

JsonParser parser = new JsonParser();
JsonArray array = parser.parse(strJson).getAsJsonArray();

List<T> lst = new ArrayList<T>();
for(final JsonElement json: array){
T entity = GSON.fromJson(json, clazz);
lst.add(entity);
}

return lst;

} catch (Exception e) {
throw new WsIntegracaoException(
"WS method error [listEntity()]", e);
}
}

GSON deserialization of a wrapped list of objects

Use the below class

public class Devices {

@Expose
private List<Device> devices = new ArrayList<Device>();

/**
*
* @return
* The devices
*/
public List<Device> getDevices() {
return devices;
}

/**
*
* @param devices
* The devices
*/
public void setDevices(List<Device> devices) {
this.devices = devices;
}

}

Device class

public class Device extends Entity {
@Expose
String id;
@Expose
String device_id;
@Expose
String device_type;
}

or

public class Device extends Entity {
@Expose @SerializedName("id")
String deviceId;
@Expose @SerializedName("device_id")
String devicePushId;
@Expose @SerializedName("device_type")
String deviceType;
}

update retrofit method to

@GET("/api/v1/protected/devices")
public void getDevices(Callback<Devices> callback);

devices.getDevices() //call inside callback method will give you the list

Also, you wont require the custom deserializer

Gson: indexed object to list

It's pretty easy in Gson.

public final class MapToListTypeAdapterFactory
implements TypeAdapterFactory {

private MapToListTypeAdapterFactory() {
}

@Override
@Nullable
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
if ( !List.class.isAssignableFrom(typeToken.getRawType()) ) {
return null;
}
final Type elementType = typeToken.getType() instanceof ParameterizedType
? ((ParameterizedType) typeToken.getType()).getActualTypeArguments()[0]
: Object.class;
final TypeAdapter<?> elementTypeAdapter = gson.getAdapter(TypeToken.get(elementType));
final TypeAdapter<List<Object>> listTypeAdapter = new TypeAdapter<List<Object>>() {
@Override
public void write(final JsonWriter out, final List<Object> value) {
throw new UnsupportedOperationException();
}

@Override
public List<Object> read(final JsonReader in)
throws IOException {
in.beginObject();
final ArrayList<Object> list = new ArrayList<>();
while ( in.hasNext() ) {
final int index = Integer.parseInt(in.nextName());
final Object element = elementTypeAdapter.read(in);
ensureSize(list, index + 1);
list.set(index, element);
}
in.endObject();
return list;
}
};
@SuppressWarnings("unchecked")
final TypeAdapter<T> typeAdapter = (TypeAdapter<T>) listTypeAdapter
.nullSafe();
return typeAdapter;
}

// https://stackoverflow.com/questions/7688151/java-arraylist-ensurecapacity-not-working/7688171#7688171
private static void ensureSize(final ArrayList<?> list, final int size) {
list.ensureCapacity(size);
while ( list.size() < size ) {
list.add(null);
}
}

}

The type adapter factory above does the following things:

  • checks if the target type is List (it does not really work with linked lists but it's fine for simplification);
  • extracts the type parameter of the target list, hence its elements type and resolves a corresponding type adapter;
  • substitutes the original type adapter with a map-reading one that reads map keys assuming them as the new list indices (and enlarges the result list if necessary) and reads every map value using the original element type adapter. Note that the assumption that the list may have sparse indices is vulnerable and I only put it for demo purposes (say, the input JSON declares a single-element map with the only index "999999" that enlarges the list dramatically), so you can ignore the index value using the add(element) method only.

Simply annotate the pointData field in the Canvas class with @JsonAdapter(MapToListTypeAdapterFactory.class) and it will work.

GSON Deserialize object with member ArrayList String

I'm seeing a few problems in your code, but I was able to get it to work without any issues.

package org.nuttz.gsonTest;
import java.util.ArrayList;

public class MondoConfig {
private String merchantURL;
public ArrayList<String> targets = new ArrayList<String>();

MondoConfig () {}

public String getMerchantURL() {
return this.merchantURL;
}

public void setMerchantURL(String url) {
this.merchantURL = url;
}

public ArrayList<String> getTargets() {
return this.targets;
}

public void setTargets(ArrayList<String> t) {
this.targets = t;
}
}

The setMerchantURL() function in your original code wasn't quite right, so I fixed it. Then I used this code to test it:

package org.nuttz.gsonTest;

import java.io.*;
import java.util.List;
import com.google.gson.*;

public class App
{
public static void main( String[] args )
{
Gson gson = new Gson();
try {
BufferedReader br = new BufferedReader(new FileReader("/home/jim/mondoconfig.json"));
MondoConfig config = gson.fromJson(br, MondoConfig.class);
System.out.println("Contents of config:");
System.out.println(config.getMerchantURL());
List<String> targets = config.targets;
for (String t : targets) {
System.out.println(t);
}
}
catch (Exception x) {
x.printStackTrace();
}
}
}

And got the following results:

Contents of config:
https://example.com/collections/posters
testing
another
one more

This is using the 2.8.2 version of GSON. In other words, you're on the right track, you've just need to fix the MondoConfig class.



Related Topics



Leave a reply



Submit