问题
I have a helper class calling a REST web service. This helper class receives the information from the web service within a class defined as a generic "R". I can easily use GSON to read my JSON in a POJO, but I can't figure out how to give it a List and read it properly.
So far I have tried to extract the type of R in a variable and pass it to the List without success. I have tried using Jackson with its TypeFactory, also without success, it won't take R as its parameter.
What I want to do is something like this :
resource = new GsonBuilder().create().fromJson(response,List<R>)
or with Jackson
resource = mapper.readValue(response, List<R>);
"response" being my JSON string and resource the instance of List I want to return.
I can use either GSON or Jackson. I have been trying with both since some things looked more possible with one or the other.
I was able to make it work with a non generic, but all I get with a generic is a list of list of Map of the JSON fields. It's basically ignoring the type of R.
Also, R is extended from a class called BaseResource, so I know R will always be a child of BaseResource, but the fields I need are in the child.
Bonus: my true end goal is to have my generic R be a list of a specific object. So if I could instead have :
resource = new GsonBuilder().create().fromJson(response,R)
and R can be either a BaseResource or a List. I already made it work for BaseResource, but I need it to make it work for List somehow either by my duplicating most of it and explicitely states it's a List or passing List to R.
Our current workaround is to have a wrapper that contains a list:
private class Result{
private List<BaseResource> result;
}
but we want the web service to return lists instead of wrappers like that.
回答1:
With GSON you have to implement your own serializer see: Type Information within serialized JSON using GSON
If you were to use Jackson you can solve this using @JsonTypeInfo annotation polymorphic types: https://www.logicbig.com/tutorials/misc/jackson/jackson-json-type-info-annotation.html
回答2:
did not read if you use any framework, well if you use spring you can use restTemplate and Jackson, here is an example of a generic class to call a rest service
private <Q, R> R callGeneric(String url, Q query, Class<R> responseClass) {
R response = null;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Q> req = new HttpEntity<Q>(query, headers);
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
ResponseEntity<R> callResult = null;
try{
callResult = restTemplate.exchange(url, HttpMethod.POST, req, responseClass);
}catch(HttpClientErrorException e){
logger.error(e.getStatusCode());
logger.error(e.getResponseBodyAsString());
throw e;
}
if (callResult == null) {
logger.error("callGeneric: No response (void) of the rest service");
return null;
}
response = callResult.getBody();
if (response == null) {
logger.error("callGeneric: empty response) ");
return null;
}
return response;
}
public ObjectResponse setCustCommunicationSetup(objectRequest request) {
String url = restCommunicationHostServer + custCommunicationContext + custCommunicationServiceSetCommunicationSetup;
logger.debug("callGeneric: "+url);
return callGeneric(url, request, ObjectResponse.class);
}
回答3:
a similar problem is described hear maybe it will still help you
You must create correctly Type if you want deserialize List of generic
public static <T> List<T> deserialize(String json, Class<T> clazz) {
Type type = TypeToken.getParameterized(List.class,clazz).getType();
return new Gson().fromJson(json, type);
}
OR
Type myType = new TypeToken<List<DTO1>>() {}.getType();
List<DTO1> deserialize = new Gson().fromJson(json, myType);
OR
Yours workaround -- for me is good
problem is T because Java does not know what i kind and generate Type of T
public static <T> List<T> sec(String json, Class<T> clazz) {
Type type1 = new TypeToken<List<T>>() {}.getType();
Type type2 = new TypeToken<List<DTO1>>() {}.getType();
Type type = TypeToken.getParameterized(List.class, clazz).getType();
System.out.println(type1); //==>....*List<T>
System.out.println(type2); //==>....*List<....DTO1> --> this declaration In Java
System.out.println(type); //==>....*List<....DTO1>
return new Gson().fromJson(json, type);
}
Testing
this is test for more example to correct run
package pl.jac.litsofgeneriric;
import java.lang.reflect.Type;
import java.util.List;
import java.util.UUID;
import org.junit.Assert;
import org.junit.Test;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
public class DeserializeListGenericTest {
@Test
public void deserializeDTO1() {
//given
String json = "[{\"id\":1,\"name\":\"test1\"},{\"id\":2,\"name\":\"test2\"}]";
//when
List<DTO1> deserialize = DeserializeListGeneric.deserialize(json, DTO1.class);
//then
Assert.assertEquals("test1", deserialize.get(0).name);
}
@Test
public void deserializeDTO2() {
//given
String json = "[{\"uuid\":\"97e98a6d-aae8-42cb-8731-013c207ea384\",\"name\":\"testUUID1\"},{\"uuid\":\"36c60080-7bb2-4c71-b4d8-c5a0a5fa47a2\",\"name\":\"testUUID1\"}]";
//when
List<DTO2> deserialize = DeserializeListGeneric.deserialize(json, DTO2.class);
//then
Assert.assertEquals("testUUID1", deserialize.get(0).name);
}
@Test
public void deserializeMyType() {
//given
String json = "[{\"id\":1,\"name\":\"test1\"},{\"id\":2,\"name\":\"test2\"}]";
//when
Type myType = new TypeToken<List<DTO1>>() {
}.getType();
List<DTO1> deserialize = new Gson().fromJson(json, myType);
//then
Assert.assertEquals("test1", deserialize.get(0).name);
}
@Test
public void deserializeGsonBuilder() {
//given
String json = "[{\"id\":1,\"name\":\"test1\"},{\"id\":2,\"name\":\"test2\"}]";
//when
Type myType = new TypeToken<List<DTO1>>() {
}.getType();
List<DTO1> deserialize = new GsonBuilder().create().fromJson(json, myType);
//then
Assert.assertEquals("test1", deserialize.get(0).name);
}
@Test
public void useSystemOut() {
//given
String json = "[{\"id\":1,\"name\":\"test1\"},{\"id\":2,\"name\":\"test2\"}]";
//when
List<DTO1> deserialize = sec(json, DTO1.class);
//then
Assert.assertEquals("test1", deserialize.get(0).name);
}
public static <T> List<T> sec(String json, Class<T> clazz) {
Type type1 = new TypeToken<List<T>>() {}.getType();
Type type2 = new TypeToken<List<DTO1>>() {}.getType();
Type type = TypeToken.getParameterized(List.class, clazz).getType();
System.out.println(type1); //==>....*List<T>
System.out.println(type2); //==>....*List<....DTO1> --> this declaration In Java
System.out.println(type); //==>....*List<....DTO1>
return new Gson().fromJson(json, type);
}
public static class DTO1 {
public Long id;
public String name;
}
public static class DTO2 {
public UUID uuid;
public String name;
}
}
and class DeserializeListGeneric
package pl.jac.litsofgeneriric;
import java.lang.reflect.Type;
import java.util.List;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
public class DeserializeListGeneric {
public static <T> List<T> deserialize(String json, Class<T> clazz) {
Type type = TypeToken.getParameterized(List.class,clazz).getType();
return new Gson().fromJson(json, type);
}
}
来源:https://stackoverflow.com/questions/57676552/how-to-read-json-in-list-of-generic