Retrofit Gson LinkedTreeMap to custom object - json

I have the following JSON file:
https://www.mediamarkt.de/de/product/productlistajax.json?categoryId=563612&sort=topseller&lazyLoading=true
EDIT: In case the above link does not work: https://pastebin.com/cTxp1RZ6
Now the only possibility I have found so far to fetch this JSON is using a Map:
Call<Map<String, Object>> call = liveApi.loadProductList(request.categoryId, request.sort, request.lazyLoading)
call.enqueue(new Callback<Map<String, Object>>() {
#Override
public void onResponse(Call<Map<String, Object>> call, Response<Map<String, Object>> response) {
Call<Map<String, Object> map = response.body();
}
});
But then I need to find all objects within the lower layers by keys. I would love to map those objects to my model classes with #SerializedName(), How could I do that?

All you have to do is creating custom mappings. Since your JSON document is pretty complex, you can try automatic mapping generators, but if they fail for any reason (dynamic properties, polymorphic values, improper camelCaseNaming detection, etc), you can always create your custom mappings:
final class Response {
#SerializedName("categories") final List<Category> categories = null;
#SerializedName("facettes") final List<Facet> facettes = null;
#SerializedName("productlistentries") final List<Map<String, Product>> productListEntries = null;
#SerializedName("last") final boolean isLast = Boolean.valueOf(false);
}
final class Category {
#SerializedName("amount") final int amount = Integer.valueOf(0);
}
final class Facet {
// ???
}
final class Product {
#SerializedName("name") final String name = null;
#SerializedName("modelNumber") final int modelNumber = Integer.valueOf(0);
#SerializedName("brandLogo") final String brandLogo = null;
#SerializedName("detailLink") final String detailLink = null;
#SerializedName("online") final boolean isOnline = Boolean.valueOf(false);
#SerializedName("imageURL") final String imageUrl = null;
#SerializedName("addToBasketUrl") final String addToBasketUrl = null;
#SerializedName("rating") final int rating = Integer.valueOf(0);
#SerializedName("ratingCount") final int ratingCount = Integer.valueOf(0);
#SerializedName("features") final List<Feature> features = null;
#SerializedName("price") final String price = null;
#SerializedName("vatLabel") final String vatLabel = null;
#SerializedName("fees") final List<Fee> fees = null;
#SerializedName("gtm") final Gtm gtm = null;
#SerializedName("productComparison") final ProductComparison productComparison = null;
#SerializedName("productWishlist") final ProductWishlist productWishlist = null;
#SerializedName("clubProduct") final boolean isClubProduct = Boolean.valueOf(false);
#SerializedName("onlineOnlyProduct") final boolean isOnlineOnlyProduct = Boolean.valueOf(false);
}
final class Feature {
#SerializedName("key") final String key = null;
#SerializedName("value") final String value = null;
}
final class Fee {
#SerializedName("value") final String value = null;
#SerializedName("dataLayer") final String dataLayer = null;
}
final class Gtm {
#SerializedName("name") final String name = null;
#SerializedName("id") final String id = null;
#SerializedName("price") final String price = null;
#SerializedName("brand") final String brand = null;
#SerializedName("category") final String category = null;
#SerializedName("dimension9") final String dimension9 = null;
#SerializedName("dimension10") final String dimension10 = null;
}
final class ProductComparison {
#SerializedName("dataLayer") final String dataLayer = null;
#SerializedName("dataUrl") final String dataUrl = null;
#SerializedName("text") final String text = null;
#SerializedName("additionalClasses") final String additionalClasses = null;
}
final class ProductWishlist {
#SerializedName("requestUrl") final String requestUrl = null;
#SerializedName("text") final String text = null;
}
It took about 15 minutes to write the mappings by hand, so they might have mistakes or typos. Note that I'm assuming your response is read-only and not supposed to be created manually to be sent elsewhere, so all of fields are declared final. One remark regarding the primitive fields: if you use 0 or false, then Java compiler can inline known-at-compile-time constants, so Type.value(...) is a sort of cheating to let javac think it's a runtime value that cannot be inlined. (You might want to generate getters, but IMHO fields for simple databags are easier to use and add less noise).
All you have to do is changing Call<Map<String, Object>> to Call<Response>.
Example in vanilla Java, not Retrofit:
try ( final Reader reader = getPackageResourceReader(Q43535942.class, "response.json") ) {
final Response response = gson.fromJson(reader, Response.class);
System.out.println(response.productListEntries.get(1).get("3486143").imageUrl);
}
Output:
//picscdn.redblue.de/doi/pixelboxx-mss-70874441/mobile_220_310_png/CRUNCH-GTO-4125-Verst%C3%A4rker-%28Class-D%29

Related

How to get inner JSON object with GSON/Retrofit when outer name is variable?

I have a JSON response from an API like this:
{"asalas77":
{"id":23519033,"name":"Asalas77","profileIconId":22,"revisionDate":1487214366000,"summonerLevel":30}
}
And I need to extract the inner object from it. I tried using a deserializer like shown in this question Get nested JSON object with GSON using retrofit but it doesn't work for me.
public class SummonerDeserializer implements JsonDeserializer<Summoner> {
#Override
public Summoner deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
throws JsonParseException {
long id = je.getAsJsonObject().get("id").getAsLong();
String name = je.getAsJsonObject().get("name").getAsString();
int profileIconId = je.getAsJsonObject().get("profileIconId").getAsInt();
long revisionDate = je.getAsJsonObject().get("revisionDate").getAsLong();
long summonerLevel = je.getAsJsonObject().get("summonerLevel").getAsLong();
Summoner s = new Summoner();
s.setId(id);
s.setName(name);
s.setProfileIconId(profileIconId);
s.setRevisionDate(revisionDate);
s.setSummonerLevel(summonerLevel);
return s;
}
}
But the problem is I can't access the inner fields from JsonElement je and the name asalas77 is a variable (it's a search query) so I can't extract the inner object directly.
You must have a wrapper class in order not to clash deserialization strategies. Assume it's as follows:
final class SummonerResponse {
private final Summoner summoner;
private SummonerResponse(final Summoner summoner) {
this.summoner = summoner;
}
static SummonerResponse summonerResponse(final Summoner summoner) {
return new SummonerResponse(summoner);
}
Summoner getSummoner() {
return summoner;
}
}
Then you can either create a custom response deserializer:
final class SummonerWrapperDeserializer
implements JsonDeserializer<SummonerResponse> {
private static final JsonDeserializer<SummonerResponse> summonerDeserializer = new SummonerWrapperDeserializer();
private SummonerWrapperDeserializer() {
}
static JsonDeserializer<SummonerResponse> getSummonerResponseDeserializer() {
return summonerDeserializer;
}
#Override
public SummonerResponse deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context)
throws JsonParseException {
// Pick the root as a JSON object
final JsonObject outerJsonObject = jsonElement.getAsJsonObject();
// And check how many properties does it have
final Iterable<? extends Entry<String, JsonElement>> outerJsonObjectEntries = outerJsonObject.entrySet();
if ( outerJsonObject.size() != 1 ) {
throw new JsonParseException("Expected one property object, the actual properties are: " + getPropertyName(outerJsonObjectEntries));
}
// If it has only one property, just get the property and take its inner value
final JsonElement innerJsonElement = outerJsonObjectEntries.iterator().next().getValue();
// Once it's obtained, just delegate the parsing to a downstream parser - no need to create Summoner instances by hands
return summonerResponse(context.deserialize(innerJsonElement, Summoner.class));
}
private static Set<String> getPropertyName(final Iterable<? extends Entry<String, JsonElement>> entries) {
final Set<String> keys = new LinkedHashSet<>();
for ( final Entry<String, JsonElement> entry : entries ) {
keys.add(entry.getKey());
}
return keys;
}
}
Or save some memory (the JSON (de)serializers require some memory because they work with JSON trees) and create a more low level type adapter:
final class SummonerResponseTypeAdapterFactory
implements TypeAdapterFactory {
private static final TypeAdapterFactory summonerResponseTypeAdapterFactory = new SummonerResponseTypeAdapterFactory();
private SummonerResponseTypeAdapterFactory() {
}
static TypeAdapterFactory getSummonerResponseTypeAdapterFactory() {
return summonerResponseTypeAdapterFactory;
}
#Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
// Check if we can handle SummonerResponse. Classes can be compared with `==`
if ( typeToken.getRawType() == SummonerResponse.class ) {
final TypeAdapter<SummonerResponse> typeAdapter = getSummonerResponseTypeAdapter(gson);
#SuppressWarnings("unchecked")
final TypeAdapter<T> castTypeAdapter = (TypeAdapter<T>) typeAdapter;
return castTypeAdapter;
}
return null;
}
}
final class SummonerResponseTypeAdapter
extends TypeAdapter<SummonerResponse> {
private final Gson gson;
private SummonerResponseTypeAdapter(final Gson gson) {
this.gson = gson;
}
static TypeAdapter<SummonerResponse> getSummonerResponseTypeAdapter(final Gson gson) {
return new SummonerResponseTypeAdapter(gson);
}
#Override
#SuppressWarnings("resource")
public void write(final JsonWriter out, final SummonerResponse summonerResponse)
throws IOException {
// The incoming object may be null
if ( summonerResponse == null && gson.serializeNulls() ) {
out.nullValue();
return;
}
// Generate the inner object
out.beginObject();
out.name(summonerResponse.getSummoner().name);
gson.toJson(summonerResponse.getSummoner(), Summoner.class, out);
out.endObject();
}
#Override
public SummonerResponse read(final JsonReader in)
throws IOException {
// is it a null?
if ( in.peek() == NULL ) {
return null;
}
// make sure that the inner read JSON contains an inner object
in.beginObject();
// ignore the name
in.nextName();
// delegate parsing to the backing Gson instance in order to apply downstream parsing
final Summoner summoner = gson.fromJson(in, Summoner.class);
// check if there are more properties within the inner object
if ( in.peek() == NAME ) {
throw new MalformedJsonException("Unexpected: " + in.nextName());
}
// consume the "}" token
in.endObject();
return summonerResponse(summoner);
}
}
Then any of the options above can be used like this:
final Gson gson = new GsonBuilder()
.registerTypeAdapter(SummonerResponse.class, getSummonerResponseDeserializer())
.create();
final SummonerResponse summonerResponse = gson.fromJson(JSON, SummonerResponse.class);
final Summoner summoner = summonerResponse.getSummoner();
out.println(summoner.id + " => " + summoner.name);
or
final Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(getSummonerResponseTypeAdapterFactory())
.create();
final SummonerResponse summonerResponse = gson.fromJson(JSON, SummonerResponse.class);
final Summoner summoner = summonerResponse.getSummoner();
out.println(summoner.id + " => " + summoner.name);
out.println(gson.toJson(summonerResponse));
The outputs are
23519033 => Asalas77
and
23519033 => Asalas77
{"Asalas77":{"id":23519033,"name":"Asalas77","profileIconId":22,"revisionDate":1487214366000,"summonerLevel":30}}
respectively.

Restlet accepts JSON input from client and respond with POST

I am writing a program which accepts a JSON input with the following format from client:
{
"campaignID": 1,
"clientID": 1,
"pmapID": 1,
"ward": "1-Bedded (Private)",
"age": 20,
"attr1": "EXA1(A)",
"attr2": "EO",
"attr3": "11/02/2012",
"attr4": "SIN",
"attr5": "N",
"attr6": "Y"
}
I'd like to read the JSON input, save all the attributes into local variables (String, int, ...) and finally respond with a POST("JSON") which will return a single float/double value (e.g. {"PMC": 30.12} ).
public class RestletApplication extends Application
{
#Override
public synchronized Restlet createInboundRoot()
{
Router router = new Router(getContext());
router.attach("/pmc/calculate", PMCResource.class);
return router;
}
}
I have written the function so far but am lost how to read the JSON input:
public class PMCResource extends ServerResource
{
#Post("JSON")
public Representation post(Representation entity) throws ResourceException {
try {
if (entity.getMediaType().isCompatible(MediaType.APPLICATION_JSON))
{
// Read JSON file and parse onto local variables
// Do processing & return a float value
}
} catch (Exception e) {
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
}
}
}
5 May 2016 - Edited the resource class
// Imports
public class PMCResource extends ServerResource
{
static Logger LOGGER = LoggerFactory.getLogger(PMCResource.class);
#Override
#Post("JSON")
public Representation post(Representation entity) throws ResourceException
{
PMCMatrixDAO matrix = new PMCMatrixDAOImpl();
JsonObjectBuilder response = Json.createObjectBuilder();
try
{
if (entity.getMediaType().isCompatible(MediaType.APPLICATION_JSON))
{
InputStream is = new FileInputStream(getClass().getResource("/input.json").getFile());
try (JsonReader reader = Json.createReader(is)) {
JsonObject obj = reader.readObject();
double result = matrix.calculatePMC(obj);
response.add("PMC", result);
}
}
} catch (Exception e) {
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
}
return new StringRepresentation(response.build().toString());
}
}
The Implementation class
public class PMCMatrixDAOImpl implements PMCMatrixDAO
{
public double calculatePMC(JsonObject obj)
{
int campaignID = obj.getInt("campaignID");
int clientID = obj.getInt("clientID");
int pmapID = obj.getInt("pmapID");
String ward = obj.getString("ward");
int age = obj.getInt("age");
String attr1 = obj.getString("attr1");
String attr2 = obj.getString("attr2");
String attr3 = obj.getString("attr3");
String attr4 = obj.getString("attr4");
String attr5 = obj.getString("attr5");
String attr6 = obj.getString("attr6");
// SQL processing
double dPMC = sqlQueryCall(...);
return dPMC;
}
}
In order to parse your JSON file, and since you're using Maven I'll assume you have it on your classpath, you can do it using a FileInputStream or a FileReader. So, assuming your JSON file is called input.json and it is on the root of your src/main/resources folder, you can load it the following way:
using a FileInputStream:
InputStream is = new FileInputStream(getClass().getResource("/input.json").getFile());
try (JsonReader reader = Json.createReader(is)) {
// file processing is done here
}
using a FileReader:
FileReader fr = new FileReader(getClass().getResource("/input.json").getFile());
try (JsonReader reader = Json.createReader(fr)) {
// file processing is done here
}
Ok, so now that we have our JsonReader created, lets retrieve the contents of our JSON file:
InputStream is = new FileInputStream(getClass().getResource("/input.json").getFile());
try (JsonReader reader = Json.createReader(is)) {
JsonObject obj = reader.readObject();
// retrieve JSON contents
int campaingID = obj.getInt("campaignID");
int clientID = obj.getInt("clientID");
int pmapID = obj.getInt("pmapID");
String ward = obj.getString("ward");
int age = obj.getInt("age");
String attr1 = obj.getString("attr1");
String attr2 = obj.getString("attr2");
String attr3 = obj.getString("attr3");
String attr4 = obj.getString("attr4");
String attr5 = obj.getString("attr5");
String attr6 = obj.getString("attr6");
}
As an alternative of having several variables across your method, you could create a simple POJO, having those variable as attributes, and then populate it using Jackson:
public class MyPojo {
private int campaingID;
private int clientID;
private int pmapID;
private String ward;
private int age;
private String attr1;
private String attr2;
private String attr3;
private String attr4;
private String attr5;
private String attr6;
// getters & setters
}
Finally, in order to send the response back to your client, you could do this:
JsonObject response = Json.createObjectBuilder().add("PMC", 30.12).build();
return new StringRepresentation(response.toString());
So, the entire solution could look like this:
#Override
#Post("JSON")
public Representation post(Representation entity) throws ResourceException {
JsonObjectBuilder response = Json.createObjectBuilder();
try {
if (entity.getMediaType().isCompatible(MediaType.APPLICATION_JSON)) {
InputStream is = new FileInputStream(getClass().getResource("/input.json").getFile());
try (JsonReader reader = Json.createReader(is)) {
JsonObject obj = reader.readObject();
// retrieve JSON contents
int campaingID = obj.getInt("campaignID");
int clientID = obj.getInt("clientID");
int pmapID = obj.getInt("pmapID");
String ward = obj.getString("ward");
int age = obj.getInt("age");
String attr1 = obj.getString("attr1");
String attr2 = obj.getString("attr2");
String attr3 = obj.getString("attr3");
String attr4 = obj.getString("attr4");
String attr5 = obj.getString("attr5");
String attr6 = obj.getString("attr6");
}
// Do processing & execute your SQL query call here
double result = sqlQueryCall(...);
response.add("PMC", result);
}
} catch (Exception e) {
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
}
return new StringRepresentation(response.build().toString());
}
As a side note, the JsonReader class belongs to the Java EE API which, for compiling purposes it's okay. Although, for running purposes, one requires the declaration of a JSON-API implementation dependency in one's Maven project. For instance:
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
<version>1.0.4</version>
</dependency>
Below is the way one can communicate to the REST web service through a client:
Create a simple POJO object that will contain the information to send, as mentioned above (MyPojo).
Your REST service would look something like this:
public class PMCResource extends ServerResource {
static Logger LOGGER = Logger.getLogger(RestletMain.class.getName());
#Post("JSON")
public Representation post(MyPojo entity) throws ResourceException {
PMCMatrixDAO matrix = new PMCMatrixDAOImpl();
JsonObjectBuilder response = Json.createObjectBuilder();
try {
double result = matrix.calculatePMC(entity);
response.add("PMC", result);
} catch (Exception e) {
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
}
return new StringRepresentation(response.build().toString());
}
}
Modify your PMCMatrixDAOImpl in order to process your POJO:
public double calculatePMC(MyPojo pojo) {
(...)
}
Create a client that allows you to test your REST service:
public class PMCResourceMain {
public static void main(String[] args) {
// take into account the context-root, if exists, and path to your REST service
ClientResource resource = new ClientResource("http://<host>:<port>");
MyPojo myPojo = new MyPojo();
myPojo.setCampaingID(1);
myPojo.setClientID(1);
myPojo.setPmapID(1);
myPojo.setWard("1-Bedded (Private)");
myPojo.setAge(20);
myPojo.setAttr1("EXA1(A)");
myPojo.setAttr2("EO");
myPojo.setAttr3("11/02/2012");
myPojo.setAttr4("SIN");
myPojo.setAttr5("N");
myPojo.setAttr6("Y");
try {
resource.post(myPojo, MediaType.APPLICATION_JSON).write(System.out);
} catch (ResourceException | IOException e) {
e.printStackTrace();
}
}
}
Full Restlet documentation can be found here.
For the benefit of those who landed in the same situation as me, here's my solution:
Resource class
#Override
#Post("JSON")
public Representation post(Representation entity) throws ResourceException
{
PMCMatrixDAO matrix = new PMCMatrixDAOImpl();
JsonObjectBuilder response = Json.createObjectBuilder();
try {
String json = entity.getText(); // Get JSON input from client
Map<String, Object> map = JsonUtils.toMap(json); // Convert input into Map
double result = matrix.calculatePMC(map);
response.add("PMC", result);
} catch (IOException e) {
LOGGER.error(this.getClass() + " - IOException - " + e);
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
}
return new StringRepresentation(response.build().toString());
}
JSON conversion utility class
public class JsonUtils {
private static final Logger LOG = LoggerFactory.getLogger(JsonUtils.class);
private JsonUtils() {
}
public static String toJson(Object object) {
String jsonString = null;
ObjectMapper mapper = new ObjectMapper();
try {
jsonString = mapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
LOG.error(e.getMessage(), e);
}
return jsonString;
}
public static Map<String, Object> toMap(String jsonString) {
Map<String, Object> map = new ConcurrentHashMap<>();
ObjectMapper mapper = new ObjectMapper();
try {
map = mapper.readValue(jsonString, new TypeReference<Map<String, Object>>() {
});
} catch (IOException e) {
LOG.error(e.getMessage(), e);
}
return map;
}
}
And the implementation class which handles all the processing
public class PMCMatrixDAOImpl implements PMCMatrixDAO
{
public double calculatePMC(Map<String, Object> map)
{
int campaignID = (int) map.get("campaignID");
int clientID = (int) map.get("clientID");
int pmapID = (int) map.get("pmapID");
String ward = (String) map.get("ward");
int age = (int) map.get("age");
String attr1 = (String) map.get("attr1");
String attr2 = (String) map.get("attr2");
String attr3 = (String) map.get("attr3");
String attr4 = (String) map.get("attr4");
String attr5 = (String) map.get("attr5");
String attr6 = (String) map.get("attr6");
// SQL processing
double dPMC = sqlQueryCall(...);
return dPMC;
}
}

Change return type if it can't be deserialized in rest template?

I call restTemplate:
restTemplate.exchange(uri, HttpMethod.GET, prepareHttpEntity(), MyDeserializedClass.class);
MyDeserializedClass:
public class MyDeserializedClass {
private final String id;
private final String title;
#JsonCreator
public MyDeserializedClass(#JsonProperty("id") String id,
#JsonProperty("title") String title) {
this.pageId = pageId;
this.title = title;
}
}
When there is no object inside json I'm getting MyDeserializedClass with null values.
I've tried to annotate MyDeserializedClass with
#JsonInclude(JsonInclude.Include.NON_NULL) or #JsonIgnoreProperties(ignoreUnknown = true) but with no luck.
Is there any way to retrieve another object (or some kind of callback) in such situation?
You can use static function as your main #JsonCreator instead of constructor
public class MyDeserializedClass {
private final String id;
private final String title;
public MyDeserializedClass () {}
#JsonCreator
public static MyDeserializedClass JsonCreator(#JsonProperty("id") String id, #JsonProperty("title") String title){
if (id == null || title == null) return null;
//or some other code can go here
MyDeserializedClass myclass = new MyDeserializedClass();
myclass.id = id; // or use setters
myclass.title = title;
return myclass;
}
}
This way you can return null or some sort of MyDeserializedClass subclass instead of MyDeserializedClass with null values
You could try to deserialize object by yourself i.e.:
ResponseEntity<String> response = restTemplate.exchange(uri, HttpMethod.GET, prepareHttpEntity(), String.class);
try {
MyDeserializedClass myClass = new ObjectMapper().readValue(response.body, MyDeserialized.class);
return ResponseEntity.ok(myClass);
} catch(IOException e) {
//log exception
return ResponseEntity.notFound().build();
}

Java Play! toJson ignore certain methods

Is it possible to serialize an object using toJson(object) and have the toJson-parser ignore certain methods?
We have a method in a User class (getSocial - which is concerned with Facebook integration) that makes the toJson()-parsing fail - and we'd like it go ignore that method when serializing if possible.
Can this be done?
You can just iterate the object and rewrite it to Map or List with specified values only.
Note that if you are choosing your objects with Ebean it fetches whole object, also data, which shouldn't be fetched (as password or other credentials)
use fastjson & PropertyFilter:
Sample Code
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.PropertyFilter;
import com.alibaba.fastjson.serializer.SerializeWriter;
PropertyFilter filter = new PropertyFilter() {
public boolean apply(Object source, String name, Object value) {
return false;
}
};
SerializeWriter out = new SerializeWriter();
JSONSerializer serializer = new JSONSerializer(out);
serializer.getPropertyFilters().add(filter);
A a = new A();
serializer.write(a);
String text = out.toString();
Assert.assertEquals("{}", text);
PropertyFilter
PropertyFilter filter = new PropertyFilter() {
public boolean apply(Object source, String name, Object value) {
if("name".equals(name)) {
return true;
}
return false;
}
};
SerializeWriter out = new SerializeWriter();
JSONSerializer serializer = new JSONSerializer(out);
serializer.getPropertyFilters().add(filter);
A a = new A();
a.setName("chennp2008");
serializer.write(a);
String text = out.toString();
Assert.assertEquals("{\"name\":\"chennp2008\"}", text);
ValueObject:
public static class A {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

deserializing generics with gson

I am using GSON 1.4 and serializing an object with two generic arraylist<myObject> as follows
String data = Gson.toJson(object, object.class). When I desirialize it I do gson.fromJson(json, type);
sadly I get
java.lang.IllegalArgumentException: Can not set java.util.ArrayList
field ... to java.util.LinkedList
Why is that ? GSON doc notes that if I serialize with object.class parameter it supports generics. any idea? thanks.
my class is :
public class IndicesAndWeightsParams {
public List<IndexParams> indicesParams;
public List<WeightParams> weightsParams;
public IndicesAndWeightsParams() {
indicesParams = new ArrayList<IndexParams>();
weightsParams = new ArrayList<WeightParams>();
}
public IndicesAndWeightsParams(ArrayList<IndexParams> indicesParams, ArrayList<WeightParams> weightsParams) {
this.indicesParams = indicesParams;
this.weightsParams = weightsParams;
}
}
public class IndexParams {
public IndexParams() {
}
public IndexParams(String key, float value, String name) {
this.key = key;
this.value = value;
this.name = name;
}
public String key;
public float value;
public String name;
}
Gson has some limitations regarding collections because of Java's type erasure. You can read more about it here.
From your question I see you're using both ArrayList and LinkedList. Are you sure you didn't mean to use just List, the interface?
This code works:
List<String> listOfStrings = new ArrayList<String>();
listOfStrings.add("one");
listOfStrings.add("two");
Gson gson = new Gson();
String json = gson.toJson(listOfStrings);
System.out.println(json);
Type type = new TypeToken<Collection<String>>(){}.getType();
List<String> fromJson = gson.fromJson(json, type);
System.out.println(fromJson);
Update: I changed your class to this, so I don't have to mess around with other classes:
class IndicesAndWeightsParams {
public List<Integer> indicesParams;
public List<String> weightsParams;
public IndicesAndWeightsParams() {
indicesParams = new ArrayList<Integer>();
weightsParams = new ArrayList<String>();
}
public IndicesAndWeightsParams(ArrayList<Integer> indicesParams, ArrayList<String> weightsParams) {
this.indicesParams = indicesParams;
this.weightsParams = weightsParams;
}
}
And using this code, everything works for me:
ArrayList<Integer> indices = new ArrayList<Integer>();
ArrayList<String> weights = new ArrayList<String>();
indices.add(2);
indices.add(5);
weights.add("fifty");
weights.add("twenty");
IndicesAndWeightsParams iaw = new IndicesAndWeightsParams(indices, weights);
Gson gson = new Gson();
String string = gson.toJson(iaw);
System.out.println(string);
IndicesAndWeightsParams fromJson = gson.fromJson(string, IndicesAndWeightsParams.class);
System.out.println(fromJson.indicesParams);
System.out.println(fromJson.weightsParams);