I have a class in Java that is generically typed. It is supposed to return an object of type T after receiving some json. I am using the following code to create the object:
ObjectMapper mapper = new ObjectMapper();
this.object = mapper.readValue(json, type);
This method throws a JsonMappingException, and should do so if the object isn't of the proper type. The problem I'm running into (when unit testing) is that if I pass in json of an incorrect type, as long as both objects are pojos no exception is being thrown. I am simply getting back an object of the correct type where all it's fields are null.
Why is the exception not getting thrown here? If I pass in some json with a bunch of fields that don't exist on the type of object it should be mapping to, shouldn't I get an exception from that?
You possibly have:
#JsonIgnoreProperties(ignoreUnknown = true)
set somewhere, so jackson doesn't complain about the mismatch.
How do you expect Jackson to know JSON does not represent expected type? JSON data does not have type, beyond just basic Object/Array/scalars structure. So as long as structure is compatible things work, and this is by design.
Related
I have a situation in which I want to return a request to a user if it fails validation along with an appropriate error message. The problem that I've run across is that I'm using Jackson to deal with the JSON request, and the failure in validation also causes Jackson to fail to serialize the message.
For instance, if I have this object:
public class SomeRequest
{
#Size(min=1, max=10)
private String someField;
//getter and setter here
}
...when I go to serialize when the field is invalid (let's say it has 11 characters in it)...
ObjectMapper mapper = new ObjectMapper();
output = mapper.writeValueAsString(someRequestInstance);
...I get a JsonProcessingException (Error(s) validating object). I've confirmed that a valid object has no problem with serialization. So my question is this: How do I get Jackson to ignore the javax.validation annotations when serializing?
Thanks in advance!
By default Jackson does not invoke bean validation (javax.validation.constraints) on JSON serialization. Either default behavior is overridden/customized or there's any kind of interceptor/hook which does that.
Research the stack trace in order to find out where exactly exception occurs and dig around that.
I am parsing JSON string from a byte-array and casting it as an object.
How do I determine the class of the object?
Object objDeserialized = gson.fromJson(jsonFromString, Object.class);
//It could be type Message or RoomDetail
gson.fromJson(jsonFromString, Object.class);
In general, this won't work because of Object.class. Gson prohibits overriding the Object class deserialization and uses ObjectTypeAdapter (see the primary Gson constructor as of Gson 2.8.0 and probably much earlier):
// built-in type adapters that cannot be overridden
factories.add(TypeAdapters.JSON_ELEMENT_FACTORY);
factories.add(ObjectTypeAdapter.FACTORY);
// the excluder must precede all adapters that handle user-defined types
factories.add(excluder);
// user's type adapters
factories.addAll(typeAdapterFactories);
If you want to use Object.class, you have to cast the result to either a primitive wrapper, null, or a List<E> or Map<K,V> -- and make some sort of analysis yourself. The rationale behind it is that you must know the result class in advance to make sure you're getting a proper deserialized object.
The best thing you can do here is making your custom parent super-type (does not really matter if it's a class or an interface), say class Message extends Base and class RoomDetail extends Base, and then registering a JsonDeserializer<Base> implementation to a GsonBuilder which can attempt to detect the real type of the Base instance. After that you can do:
gson.fromJson(jsonSource, Base.class);
See more:
Polymorphic objects deserialization:
How to parse dynamic json in android with retrofit 2 using annotations
How do I parse a nested JSON array of object with a custom Gson deserializer?
Json response parser for Array or Object
Google Gson extras, never been published as artifacts, but may be an inspiration point for you:
https://github.com/google/gson/blob/master/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java
If you do not know the type of the JSON you want to parse you could use the JsonParser from the Gson lib to parse the JSON instead of the Gson class directly. e.g.
JsonParser parser = new JsonParser(jsonFromString);
JsonObject obj = parser.parse().getAsJsonObject();
You could then look at the properties of the JsonObject you have created to see what it is. e.g.
if (obj.has("somePropertyNameIKnownIsAMemberOfRoomDetail")) {
RoomDetail roomDetail = gson.fromJson(jsonFromString, RoomDetail.class);
} else {
Message message = gson.fromJson(jsonFromString, Message.class);
}
I'm trying to deserialize using ObjectMapper to a POJO and im getting the error:
No suitable constructor found for type [simple type, class LambdaResult<java.lang.Object>]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
My Pojo is quite simple
class LambdaResult<T> {
LambdaResult() {}
String Status
ArrayList<T> Results
}
And my deserialization code is the following
static <T> T Deserialize(final TypeReference<T> type,
final String json) {
return new ObjectMapper().readValue(json, type)
}
LambdaResult<Object> result = Serialization.Deserialize(new TypeReference<LambdaResult<Object>>() {},jsonResult)
Json example:
{"status": "success", "locale": "sg", "results": [{"status": "pending"}]}
I come from a C# background so there's something im probably missing here.
Thanks
I would recommend creating and configuring an ObjectMapper like this (Java, sorry don't speak Groovy):
ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper in this case should probably be a field in the same class as Deserialize method that would then look like:
return mapper.readValue(json, type);
Explanation for configuration:
This MapperFeature tells Jackson to match field names in a case insensitive manner. So it will find String Status although it doesn't follow Java Beans naming conventions. This is neat in your case but comes with a small performance penalty for transforming a String to lowercase. You can alternatively annotate a field in a class with #JsonProperty and specify any JSON field name you want.
This tells Jackson to match JSON fiels to Java object fields regardless of field visibility private, public etc. You may also annotate your class with #JsonAutoDetect and specify visibility there. In both cases you don't need to define a constructor but you can keep it if you don't want it to be public.
The last one tells Jackson to not fail when encountering fields in JSON that don't exist in your class. In this case "locale".
I have a generic response wrapper class:
public class Response <T> {
T response;
}
and unrelated classes to be wrapped:
public class ServiceResponse {
String someField;
}
When I make a service request, I get a JSON response that looks something like:
{ "code":200, "response":{"someField":"some text"} }
Now, all my service responses have the same outer wrapper, i.e., they all have:
{ "code":200, "timestamp":"....", "response":... }
But the actual format/type of the response field is different for each service request. When I deserialize the response, I need to know the type of the response field so I can create the appropriate instance, if the deserialization was done within Response, I could use:
response = new T(jsonParser);
However, I'm doing all of this from within a library that is driven by reflection, so I normally deserialize the whole tree with code like:
wrapper = deserializer.parseObject(Response<ServiceResponse>.class)
but, at this point my parseObject method can't correctly determine the type of T.
I can use something like:
Response<ServiceResponse> response = new Response<>();
Field field = response.getClass().getDeclaredField("response");
Type type = field.getGenericType();
which then tells me that response is of type T but what I actually need is ServiceResponse
Per this SO question I tried casting as ParameterizedType but that would actually seem to apply to a field of type Response<ServiceResponse> and not the actual field within (and it fails because type can't be cast as ParameterizedType)
Is there any way to determine (at run time) the raw type of response?
Eventually, I may wind up having to create an annotation providing more details about how to deserialize the field, probably by providing a function to do it, but would prefer a more transparent approach.
Another possibility might be to actually assign a void instance of T to response at initialization time and then I could grab the actual type from that...
Check out this post:
http://mydailyjava.blogspot.com/2013/06/advanced-java-generics-retreiving.html
It's actually exactly what you're looking for.
According to this, you'll just need to extend your Response class and then query the generic type of its super.
I'm building a data driven test system. I have done this before in XML but json is giving me some interesting issues.
For each request and response type json, I have a setting in my script where I specify a pojo type. This type is instantiated to a class object thats passed to jackson to marshal the json into a usable pojo. so its like this:
"responseType": "java.util.List",
eventually gets pumped to
Class<?> reponseType = null;
try {
if (d.shouldPass) {
reponseType = Class.forName(d.responseType);
}
} catch (ClassNotFoundException e) {
throw new RequestResponseTypeInvalid(testName);
}
and I have usable class info to use in jackson. My problem is I need to do this:
"responseType": "java.util.List<foo>",
otherwise complex json types parse as hashmaps instead of pojo's. I suppose I can get creative and put something in to go from hashmap to pojo if I need to but I was wondering if there was any straight forward way to do this.
I suppose another way is to implement a factory class where I could say list_foo in the property file and have the factory class map that to an actual class object. That wouldn't be very hard but not as easy as just using the property.
thanks
You can't do this in the way that you're hoping, I'm afraid. Generics are a compile-time thing only, and can't be used in this way at runtime, because of type erasure.
The best you could do would be to have some list_foo properties, and map these explicitly to List<Foo> and so in in your code. But you can't do it by reflection.