Parsing Json using FasterXML for generic elements - java

I´m using java spring boot to read data via REST api.
Incoming data is specified as followed:
public class Element<T>
{
public String error;
public Boolean state;
public T subelement;
}
public class Response<T>
{
public Element<T> element;
}
Generic Type T can be any generic class such as Boolean or complex classes.
In a simple sample I tried to parse an Response element:
ObjectMapper mapper = new ObjectMapper();
mapper.readValue(value, Response<Boolean>.class);
Eclipse marks me the Response.class part as followed :
Boolean cannot be resolved to a variable
I´m used to use generics on fly in order to dynamicly interact in Delphi and .NET. There has never been the urge of generating classes or pass types since you can classify T such being a class, owning an empty constructor and json wrapper takes T arguments.
Since JacksonXML requires a Class I need to find a way to solve it.
I tried a few posts here on stack overflow to obtain data on following way:
ParameterizedType
JacksonXMLbind Javatype
any ideas?
Edit:
Well it could be based on the List Element that it looks differently.
Here is a sample of my json:
{"id":1,"state":true,"element":{"response":{"data":{"client_id":"sock1591427021","protocol_version":2}}},"errormsg":"OK"}
this part i use to convert the json :
private <T extends Object> APIResponse<T> readResponse(String data) {
try {
var value = objectMapper.readValue(data, new TypeReference<APIResponse<T>>() {
});
return value;
} catch (Exception e) {
logger.warn(e.getMessage());
return null;
}
}

Related

How to convert List<?> to List<Object> in java?

How to convert List<?> to List in java?
For example I have this class
#Data
public class Example {
private List<?> data;
}
and I used in this function
#PostMapping("/getResult")
#ResponseBody
public Result getResult(#RequestBody String json) {
Gson gson = new Gson();
Example xmpl = gson.fromJson(json, Example.class);
List<MyObject> source = (List<MyObject>)xmpl.getData(); //==> error
// get Result
return result;
}
It will give this error
class com.google.gson.internal.LinkedTreeMap cannot be cast to class com.myproject.MyObject
EDITED:
The real problem is not from converting ? to object, but from converting LinkedTreeMap to the object
WORKAROUND :
String jsonData = gson.toJson(xmpl.getData());
MyObjectBean[] objs = gson.fromJson(jsonData,MyObjectBean[].class);
You could go with two solutions, to start with:
You can change the Generic type, this way You don't say data is any collection, but it's a collection of type <T>. Now You can create classes with given type anywhere You need it.
Generic value <?> means in general that you don't care what is inside, and probably You won't read it anyway. When You are interested only if collection is null or what it's size.
When You need to do something with it, then use Generic types.
Example:
public class Example<T> {
private List<T> data;
}
Now inside of your controller, create a private class, to deserialize your payload.
static class MyObjectExample extends Example<MyObject>{
}
Now you can use it do decode JSON:
MyObjectExample xmpl = gson.fromJson(json, MyObjectExample.class);
List<MyObject> source = xmpl.getData();
Now if your code can be serialized to MyObject it will work.
Spring supports deserialization also.
If you have a #RestController annotation added to your Controller class
Example:
#PostMapping("/getResult")
public Result getResult(#RequestBody MyObjectExample xmpl) {
// get Result
return result;
}
Or you can add
consumes = MediaType.APPLICATION_JSON_VALUE
to your REST method.
Try using Spring to convert your value for you.
You can find more
GetMapping and PostMapping
tutotrial
The real issue is not when converting ? to MyObject, but the LinkedTreeMap to MyObject, from
this explanation
by #harsh
so I did this workaround
String jsonData = gson.toJson(xmpl.getData());
MyObjectBean[] objs = gson.fromJson(jsonData,MyObjectBean[].class);

GSON TypeAdapter: DeSerialize Polymorphic Objects Based on "Type" Field

I'm using Retrofit with the default Gson parser for JSON processing. Oftentimes, I have a series of 4~5 related but slightly different objects, which are all subtypes of a common base (let's call it "BaseType"). I know we can deserialize the different JSONs to their respective child models by checking the "type" field. The most commonly prescribed way is to extend a JsonDeserializer and register it as a type adapter in the Gson instance:
class BaseTypeDeserializer implements JsonDeserializer<BaseType> {
private static final String TYPE_FIELD = "type";
#Override
public BaseType deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json.isJsonObject() && json.getAsJsonObject().has(TYPE_FIELD)) {
JsonObject jsonObject = json.getAsJsonObject();
final String type = jsonObject.get(TYPE_FIELD).getAsString();
if ("type_a".equals(type)) {
return context.deserialize(json, AType.class);
} else if ("type_b".equals(type)) {
return context.deserialize(json, BType.class);
} ...
// If you need to deserialize as BaseType,
// deserialize without the current context
// or you will infinite loop
return new Gson().fromJson(json, typeOfT);
} else {
// Return a blank object on error
return new BaseType();
}
}
}
However, in my experience this is really slow, and seemingly because we have to load up the entire JSON document into a JsonElement and then traverse it to find the type field. I also don't like it that this deserializer has to be run on every one of our REST calls, even though the data isn't always necessarily being mapped to a BaseType (or its children).
This foursquare blog post mentioned using TypeAdapters as an alternative but it didn't really go further with an example.
Anybody here know how to use TypeAdapterFactory to deserialize based on a 'type' field without having to read up the entire json stream into a JsonElement object tree?
The custom deserializer should only be run when you have a BaseType or a sub-classes in the deserialization data, not every request. You register it based on the type, and it is only called when gson need to serialize that type.
Do you deserialize BaseType as well as the sub-classes? If so, this line is going to kill your performance --
return new Gson().fromJson(json, typeOfT);
creation of new Gson objects is not cheap. You are creating one each time you deserialize a base class object. Moving this call to a constructor of BaseTypeDeserializer and stashing it in a member variable will improve performance (assuming you do deserialize the base class).
The issue with creating a TypeAdapter or TypeAdapterFactory for selecting type based on the field is that you need to know the type before you start consuming the stream. If the type field is part of the object, you cannot know the type at that point. The post you linked to mentions as much --
Deserializers written using TypeAdapters may be less flexible than
those written with JsonDeserializers. Imagine you want a type field to
determine what an object field deserializes to. With the streaming
API, you need to guarantee that type comes down in the response before
object.
If you can get the type before the object in the JSON stream, you can do it, otherwise your TypeAdapter implementation is probably going to mirror your current implementation, except that the first thing you do is convert to Json tree yourself so you can find the type field. That is not going to save you much over your current implementation.
If your subclasses are similar and you don't have any field conflicts between them (fields with the same name but different types), you can use a data transfer object that has all the fields. Use gson to deserialize that, and then use it create your objects.
public class MyDTO {
String type;
// Fields from BaseType
String fromBase;
// Fields from TypeA
String fromA;
// Fields from TypeB
// ...
}
public class BaseType {
String type;
String fromBase;
public BaseType(MyDTO dto) {
type = dto.type;
fromBase = dto.fromBase;
}
}
public class TypeA extends BaseType {
String fromA;
public TypeA(MyDTO dto) {
super(dto);
fromA = dto.fromA;
}
}
you can then create a TypeAdapterFactory that handles the conversion from DTO to your object --
public class BaseTypeAdapterFactory implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
if(BaseType.class.isAssignableFrom(type.getRawType())) {
TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
return newItemAdapter((TypeAdapter<BaseType>) delegate,
gson.getAdapter(new TypeToken<MyDTO>(){}));
} else {
return null;
}
}
private TypeAdapter newItemAdapter(
final TypeAdapter<BaseType> delagateAdapter,
final TypeAdapter<MyDTO> dtoAdapter) {
return new TypeAdapter<BaseType>() {
#Override
public void write(JsonWriter out, BaseType value) throws IOException {
delagateAdapter.write(out, value);
}
#Override
public BaseType read(JsonReader in) throws IOException {
MyDTO dto = dtoAdapter.read(in);
if("base".equals(dto.type)) {
return new BaseType(dto);
} else if ("type_a".equals(dto.type)) {
return new TypeA(dto);
} else {
return null;
}
}
};
}
}
and use like this --
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new BaseTypeAdapterFactory())
.create();
BaseType base = gson.fromJson(baseString, BaseType.class);

Gson How to covert json to Custom Class

Somary for below: I need to deserilize A<B<C>> from json with Java Gson library
I have an api that response in this templete (Json):
{code:string, message:string, data:(any json type/object/array)}
In Java I define a class to read results:
public class ApiResult <DATA> {
public String code;
public String message;
public DATA data;
}
And I have another Data Class:
public class Ball {
public String color;
public String weight;
}
One of my api methods returns list of ball in top template.
I use GSON.fromJson(reader, new TypeToken<ApiResult<List<Ball>>>() {}.getType());
But Gson read data in List<com.google.gson.internal.LinkedTreeMap> not List
How can I fix this?
Finally I solve my question but not for all A<B<C>> problems
I redefine my ApiResult class to:
public class ApiResult {
public String code;
public String message;
public JsonElement data;
}
Using result = GSON.fromJson(reader, ApiResult.class); I extract global response (All api responses return this part).
and check code and message that I need
then I extract real data I want with final List<Ball> data = GSON.fromJson(result.data, new TypeToken<List<Ball>>() {}.getType()); and return it
I think the main bug/limit in Gson is it only check Generic Type only once for parent type and doesn't check it for generic Type[s]
and I think it's better that they (its Developers) add it to next versions

java jackson parse object containing a generic type object

i have the following problem.
I have to parse a json request into an object that contains a generic type field.
EDIT
i have made some tests using a regular class type (so i make it work before i replace it with generic). Now parsing for a single element works great.
The issue is when i need to parse out a list object out of that class.
So i have to inform jackson somehow that my T is of type list instead of just AlbumModel.
Here is what i have tried.
#Override
public ListResponseModel<AlbumModel> parse(String responseBody) throws Exception {
JavaType type = mapper.getTypeFactory().constructParametricType(ResponseModel.class,
AlbumModel.class);
return mapper.readValue(responseBody,
mapper.getTypeFactory().constructParametricType(ResponseModel.class, type));
}
But the code above doesn't work. what is the solution for something like this?
my generic type in the ListResponseModel is defined like: List<T> data
succeeded like:
public class BaseResponseModel<T> {
#JsonProperty("data")
private T data;
#JsonProperty("paginations")
private PaginationModel pagination;
}
so far i have the following code but it always parses into a Hash.
public class ResponseParser extends BaseJacksonMapperResponseParser<ResponseModel<AlbumModel>> {
public static final String TAG = ResponseParser.class.getSimpleName();
#Override
public ResponseModel<AlbumModel> parse(String responseBody) throws Exception {
return mapper.readValue(responseBody,
mapper.getTypeFactory().constructParametricType(ResponseModel.class, AlbumModel.class));
}
}
public abstract class BaseJacksonMapperResponseParser<T> implements HttpResponseParser<T> {
public static final String TAG = BaseJacksonMapperResponseParser.class.getSimpleName();
public static ObjectMapper mapper = new ObjectMapper();
static {
mapper.disable(Feature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.enable(Feature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
mapper.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true);
}
}
I agree with eugen's answer but just wanted to expand on it a bit. The first step is to refactor your parse method so it takes a second argument. Instead of allocating the type reference in your method, you require the caller to pass in a TypeReference instance.
public BaseResponseModel<T> parse(String responseBody, TypeReference<T> ref) throws Exception {
return mapper.readValue(responseBody, ref);
}
Unfortunately your snippet does not show the code which calls parse - so I'll make something up:
BaseResponseParser<Collection<Person>> parser = new BaseResponseParser<Collection<Person>>();
BaseResponseModel<Collection<Person>> result = parser.parse(jsonText, new TypeReference<Collection<Person>>(){});
Notice that when the TypeReference instance is compiled in this case, it a type reference to the real concrete class that we expect.
You could do the same thing passing in a Class at runtime, however TypeReference is a bit more powerful because it even works when type T is a generic collection. There is some magic in the TypeReference implementation that allows it to hold onto type information that would normally be erased.
[update]
Updated to use Collection<Person>. Note - as far as I know as List<Whatever> should work also, but I double checked a project where I was using jackson to deserialize collections. Base class Collection definitely worked so I stayed with that.
Your type T will be "erased" at runtime, so Jackson does not know what is the real type of T and deserializes it to a Map. You need a second parameter to your parse method that will be Class<T> clazz or TypeReference<T> or java.lang.reflect.Type.
EDIT
Small explanation on the magic of TypeReference. When you do new XX() {} you are creating a anonymous class, so if it is a class with typevariables (parameterized if you prefer), new X<List<Y>>() {}, you will be able to retrieve List<Y> as a java Type at runtime. It is very similar as if you had done :
abstract class MyGenericClass<T> {}
class MySpecializedClass extends MyGenericClass<List<Y>> {}
Since you're using Jackson you probably need to create a custom JsonDeserializer or JsonSerializer depending on whether you're handing the response or request. I've done this with Dates because on my response I want a standard view. I'm not 100% positive it will work with a generic field though. Here is an example of what I'm doing:
public class DateSerializer extends JsonSerializer<Date> {
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZ");
#Override
public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
String dateString = dateFormat.format(value);
jgen.writeString(dateString);
}
}
Then I just add it to my class like so:
#JsonSerialize(using = DateSerializer.class)
public Date getModifiedDate() {
return modifiedDate;
}

How to create object that can be added to a List<T> array?

I am trying to create a JSONMessage class that can parse a json string and add the objects contained in the message to the List<T> t.
The object contained in the message implements the interface JSONSerialisation and implements the two methods toJSON() and fromJSON().
The code below does not work because I cannot instantiate the Type T and I get an error on the row t2.fromJSON... (as t2 has not been initialized).
I am not sure if my approach here is correct and if what I try to is achievable (create a generic JSONMessage) that I can use to encode/parse different type of objects. If that approach is not possible, I would appreciate hints of how I could achieve a similar result.
Regards
Interface
public interface JSONSerialisation {
public JSONObject toJSON();
public void fromJSON(JSONObject jsonObject);
}
Class
public class JSONMessage<T extends JSONSerialisation> {
private List<T> t;
public JSONMessage(String json) {
parseJSONMessage(json);
}
public void parseJSONMessage(String json) {
try {
this.t = new ArrayList<T>();
JSONObject jsonObject;
JSONArray lineItems;
jsonObject = new JSONObject(json);
this.messageHeader = new MessageHeader(jsonObject.getJSONObject("Header"));
lineItems = jsonObject.getJSONArray("Data");
int size = lineItems.length();
for (int i = 0; i < size; i++) {
T t2;
t2.fromJSON(lineItems.getJSONObject(i));
t.add(t2);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
Looks like you are trying to replace the functionality available from most JSON deserialization libraries. You will notice that most of them require a parametized class, as a solution to the very problem you are facing. It's not the most elegant solution, but it will work. I would drop the generic parameter from the deserialization class, and the stateful variables as well:
public class JSONMessage {
public static <T extends JSONSerialisation> Collection<T> parseJSONMessage(Class<T> clazz, String json) {
try {
final JSONObject jsonObject = new JSONObject(json);
final JSONArray lineItems = jsonObject.getJSONArray("Data");
Collection<T> results = new ArrayList<T>(lineItems.size());
for (final JSONElement elem: lineItems) {
T result = clazz.newInstance();
result.fromJSON(elem);
results.add(result);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
public JSONMessage() {
super();
}
}
The code is obviously missing some error handling, and I made up a bogus JSONElement type in the enhanced for loop, but you get the general idea. Good luck.
Try this:
this.t = new ArrayList<JSONSerialisation>();
If you eliminate all references to this inside that method you can make it static. Just create a List in scope and return it.
I don't see what generics are buying you here. How likely is it that you're going to have different implementations of that JSON interface?
Have you considered including the object class in your JSON message?
What about a factory class that implements fromJSON(JSONObject) so you can create an instance of the object without first having an instance of the object? The factory could use information about the context within which the JSON was generated to determine the appropriate Java classes. For example, if you've just invoked web-service X you might know the response will either be an piece of data of type Y or an error message of type Z.
In your code above, what if T has a sub-class? How would you distinguish between them?
Ignoring JSON, I thought I'd also answer the part of your question on generics: if you know you have a List, what is T? Java generics work by type-erasure, which means during compilation a lot of the generic type information is erased. However, if you are serializing a POJO into JSON there is good news. The generic type information is retained for classes, fields and methods, so if you had a class like this:
class Example extends ArrayList<String> {
private Set<Integer> someIDs;
public Map<String,Long> getLongMap() {
...
}
}
Then it is possible to use reflection to discover that the class implements List where T is String, that the someIDs field is an instance of Set where T is Integer and the getLongMap method returns an instance of Map where K is String and V is Long. However working your way through the reflection API to get to this information can get quite involved. My code to just identify Collection, Iterator and Enumeration types runs to over 100 lines. I would not recommend trying to do it if you have alternatives.

Categories