I have the following json:
{"resourceWithType":
{"parentId":0,
"pluginId":0,
"pluginName":"Platforms",
"resourceId":10001,
"resourceName":"snert",
"typeId":10057,
"typeName":"Mac OS X"
}
}
And a class
public class ResourceWithType {
String resourceName;
int resourceId;
String typeName;
with all the getters and setters and so on.
The above JSON was actually created via RESTeasy and the Jettison provider where the class was marked with #XmlRootElement.
When I try to deserialize the above JSON via
ObjectMapper mapper=new ObjectMapper();
ResourceWithType rwt = mapper.readValue(json,ResourceWithType.class);
It fails with
06-13 11:07:55.360: WARN/System.err(26040):
org.codehaus.jackson.map.exc.UnrecognizedPropertyException:
Unrecognized field "resourceWithType"
(Class org.rhq.core.domain.rest.ResourceWithType),
not marked as ignorable
Which is sort of understandable.
How can I tell Jackson, that the embedded 'resourceWithType' is actually the class to deserialize into?
Other option would be to tell jettison not to include that type - how?
Tree model is a possibility; or just a simple wrapper like:
class {
public ResourceWithType resourceWithType;
}
to let you unwrap it. But often framework itself should handle unwrapping, since they are ones adding extra wrapping (Jackson does not add 'resourceWithType' in there by default).
Perhaps use the TreeModel API to unwrap the first (tag name) level, then deserialize the inner contents as usual (using the binding API)?
Related
I've searched and found Jackson ObjectMapper throwing NullPointerException even with NON_NULL, but I don't have control of the class to change my setter.
I have am being given
{... "fieldNames": null,...}
and am supposed to deserialize it to
Collection<String> fieldNames
I don't have control of the class or the json I'm getting.
Is there some setting I can use to handle for this? I've looked at DeserializationFeature, but could not find it
You can use mix-ins when you don't control the class you are deserializing. You don't mention the name of the class containing Collection<String> fieldNames so lets assume it's called Fields. Then create a new class:
class FieldsMixin {
#JsonSetter(nulls = Nulls.SKIP)
Collection<String> fieldNames;
}
and add the mixin class to your ObjectMapper associating it with the original unmodified class:
mapper.addMixIn(Fields.class, FieldsMixin.class);
This is a new feature in Jackson 2.9 and as you guess it will skip calling a setter method or otherwise set a field if the value in JSON is null. Documentation
I am new to JACKSON serialization, and writing Test cases for model classes.
So when i serialise an another object initialized in this model class following anomaly is seen::
Example::
class ToTest{
ABC abc;
//getter setter
}
class Test{
//everything that is needed
#Test
public void serialize() throws Exception{
ToTest toTest = new ToTest();
ABC abc = new ABC();
toTest.setABC(abc);
}
Now when I serilize this toTest object: the json string is missing the "ABC" class name. So i am not able to equalise them. Please help.
Jackson does not explicitly write out the class names when doing serialization do JSON. This is by design as the POJO objects used in serialization are intended for describing the contents of the JSON data, not necessarily preserving the class.
With that said, there are a few things you can do. If you want to preserve the original class, you can use annotations to add a class field, which might solve your issue. A quick search resulted in this as an example:
include class name in all objects serialized by jackson
I have a JAX-RS WebService with the following method:
#Path("/myrest")
public class MyRestResource {
...
#GET
#Path("/getInteger")
#Produces(APPLICATION_JSON)
public Integer getInteger() {
return 42;
}
When accessed using this snipped:
#Test
public void testGetPrimitiveWrapers() throws IOException {
// this works:
assertEquals(new Integer(42), new ObjectMapper().readValue("42", Integer.class));
// that fails:
assertEquals(new Integer(42), resource().path("/myrest/getInteger").get(Integer.class));
}
I get the following exception:
com.sun.jersey.api.client.ClientResponse getEntity
SEVERE: A message body reader for Java class java.lang.Integer, and Java type class java.lang.Integer, and MIME media type application/json was not found
com.sun.jersey.api.client.ClientResponse getEntity
SEVERE: The registered message body readers compatible with the MIME media type are: application/json
...
The problem is just with returning single primitive values (int/boolean) or their wrapper classes. Returning other POJO classes is not the problemen so I guess all the answers regarding JSONConfiguration.FEATURE_POJO_MAPPING and JAXB annotations do not apply here.
Or which annotation should I use to describe the return type if I don't have access to its
class source?
Using ngrep I can verify that just the String "42" is returned by the webservice. Thats a valid JSON "value" but not a valid JSON "text" according to the spec. So is my problem on the client or the server side?
I tried activating JSONConfiguration natural/badgerfish according to http://tugdualgrall.blogspot.de/2011/09/jax-rs-jersey-and-single-element-arrays.html but with no success (ngrep still shows just "42"). Would that be the right path?
Any ideas are appreciated!
This is a recognized bug in Jackson, which has been touted (incorrectly in my opinion) as a feature. Why do I consider it a bug? Because while serialization works, deserialization definitely does not.
In any case, valid JSON cannot be generated from your current return type, so I would recommend creating a wrapper class:
class Result<T> {
private T data;
// constructors, getters, setters
}
#GET
#Path("/getInteger")
#Produces(APPLICATION_JSON)
public Result<Integer> getInteger() {
return new Result<Integer)(42);
}
Alternatively, you can elect to wrap root values, which will automatically encapsulate your data in a top level JSON object, keyed by the objects simple type name - but note that if this option is used that all generated JSON will be wrapped (not just for primitives):
final ObjectMapper mapper = new ObjectMapper()
.configure(SerializationFeature.WRAP_ROOT_VALUE, true)
.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
final String serializedJson = mapper.writeValueAsString(42);
final Integer deserializedVal = mapper.readValue(serializedJson,
Integer.class);
System.out.println(serializedJson);
System.out.println("Deserialized Value: " + deserializedVal);
Output:
{"Integer":42}
Deserialized Value: 42
See this answer for details on how to retrieve and configure your ObjectMapper instance in a JAX-RS environment.
Using this abstract class:
#JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type")
#JsonSubTypes({ #JsonSubTypes.Type(value = PostingTaskInstanceDto.class, name = "TI") })
public abstract class BasePostingDto {}
and this inherited class:
public class PostingTaskInstanceDto extends BasePostingDto {}
I get correct serialization for a single object. This works, using Spring-MVC:
#RequestMapping("/{id}")
#ResponseBody
public BasePostingDto findById(#PathVariable("id") Long id) {
return createDto(postingService.findById(id));
}
But if I retrieve a List of BasePostingDto from the remote controller, the type property is missing:
#RequestMapping("/by-user/all")
#ResponseBody
public List<BasePostingDto> findByUser() {
return createDtoList(postingService.findByUser(AuthUtils.getUser()));
}
Why is this and how can I force the type property?
Update: the type property is also included if I change List<BasePostingDto> to BasePostingDto[], however I would prefer to go with the List.
It sounds like the framework you are using (and which uses Jackson under the hood) is not passing full generics-aware type information.
I don't know how that can be fixed (it is problem with integration by framework, and not something Jackson can address), but the usual work around is for you to use sub-class of List:
public class PostingDtoList extends List<BasePostingDto> { }
and use that in signature, instead of generic type. This solves the issue because then the generic type signature is retained (since it is stored in super type declaration, and accessible via type-erased PostingDtoList class!).
In generally I think it is best to avoid using generic List and Map types as root type (and instead use POJO); partly because of problems issued (there are bigger problems when using XML for example). But it can be made to work if need be.
I'm having issues using Jackson to map a Javascript posted JSON array of hashes (Tag).
Here is the data received by the controller #RequestBody (It is send with correct json requestheader):
[{name=tag1}, {name=tag2}, {name=tag3}]
Here is the controller:
#RequestMapping(value = "purchases/{purchaseId}/tags", method = RequestMethod.POST, params = "manyTags")
#ResponseStatus(HttpStatus.CREATED)
public void createAll(#PathVariable("purchaseId") final Long purchaseId, #RequestBody final List<Tag> entities)
{
Purchase purchase = purchaseService.getById(purchaseId);
Set<Tag> tags = purchase.getTags();
purchaseService.updatePurchase(purchase);
}
When I debug and view the 'entities' value it shows as an ArrayList of generic objects, not as a list of objects of type 'Tag' as I would expect.
How can I get jackson to map a passed array of objects to a list of obejcts of type 'Tag'?
Thanks
It sounds like Spring is not passing full type information for some reason, but rather a type-erased version, as if declaration was something like List<?> tag. I don't know what can be done to fully resolve this (may need something from Spring integration team), but one work-around is to define your own type like:
static class TagList extends ArrayList<Tag> { }
and use that instead. This will retain generic parameterization through super-type declarations so that even if Spring only passes equivalent of TagList.class, Jackson can figure out the Tag parameter.
Another way to do this is to rather obtain an array than a List, as follows:
#RequestBody Tag[] entities
Jackson requires a default constructor with no parameters on custom Objects, so you'll need to simply add a default constructor to your Tag class.
In your case simply add to your Tag class:
public Tag(){}