Case is as follows:
I recieve a Json similar to this:
{
nodeProperties:
{
node: {
#id: "00:00:82:b6:ba:b6:5e:43"
#type: "OF"
},
properties: {
property: null,
tables: {
tablesValue: "-2"
},
actions: {
actionsValue: "4095"
},
...
}
}
Deserialization goes through Jackson v.1.9 & Java7
Properties are deserialized and stored into this "nodeProperties" attribute:
private java.util.List<Property> _properties;
And all "properties" fall into subclass polimorphism of Property abstract class. Annotations for this to happen shall they be correctly inserted in the parent class and all subclasses.
Problem is: the JSON includes almost always that "property : null" which should be in fact a pojo class (NullProperty or whatever), or just ignored (as when it is actually null, i don't care at all), but i can't seem to find a way of any of these 2 solutions to make it work.
When I define (for example) a NullProperty subclass (completly void, without anything):
Can not deserialize instance of ...properties.NullProperty out of VALUE_NULL token
Nor i can find a way to ignore the null value. Else when not defining it:
Could not resolve type id 'property' into a subtype of [simple type, class ...properties.Property]
Nor I find the way to ignore it.
Any help will be so greatly appreciated.
Related
I am working with the following structure:
class Family {
//...
Parent parent;
}
class Parent {
//...
}
class ChildA extends Parent {
//...
}
class ChildB extends Parent {
//...
}
I am trying to deserialize a JSON object of type Family (with a Child object and not Parent), but I need to tweak one of the Child so I tried to start by pulling the Parent property from the Family object and then set it equal to a Child. However, the compiler was saying that I needed to typecast it, so I tried but then I got an error basically saying that I couldn't typecast a superclass as a subclass, which makes sense.
I see now that the Family object gets deserialized with Parent only, and no subtypes. How can I deserialize Family with a Child property rather than a Parent type? I don't understand because the Family object gets posted to the server with a Child object, but it gets deserialized only with the Parent properties.
Thanks
After doing some research, I came across what I believe might be part of the solution (although now I have a new problem)
I have revised my classes in the following way:
class Family {
String address;
Parent parent;
}
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(value = ChildA.class, name = "A"),
#JsonSubTypes.Type(value = ChildB.class, name = "B")
})
class Parent {
String parentAttribute;
}
#JsonSubType("A")
class ChildA extends Parent {
String attributeA;
}
#JsonSubType("B")
class ChildB extends Parent {
String attributeB;
}
Now however, when I try to deserialize the Family class, I get the following error: com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve subtype of [simple type, class Family ]: missing type id property 'type' (for POJO property 'Parent')
There is no type field in any of my classes, I think this might have something to do with it.
Here is my JSON string that gets serialized:
{
"Address": "123 Main St",
"Parent": {
"parentAttribute": "Mom",
"attributeA": "Child A Type"
}
}
Ideally, when I perform deserialization, I would do it on the same JSON string above, but the string above doesn't include any type attributes. Is there a way I can sort of pre-process the deserialization and add an intermediary step (i.e. adding a type field?)
Before adding these annotations, I was able to serialize a POJO into a
JSON string like this:
{
"Address": "123 Main St",
"Parent": {
"parentAttribute": "Mom",
"attributeA": "Child A Type"
}
}
Ideally, when I perform deserialization, I would do it on the same
JSON string above, but the dto getting deserialized doesn't use any of
the Child types -- I suppose it doesn't know how to map which Child
object so it just uses Parent, but my knowledge is limited here.
As far as I know, this is not (easily) possible, I'm afraid. Not out-of-the-box and unlikely easily with custom deserialization code.
Try to think from a deserializer point of view: You need something to destinguish JSON objects to map them to a Java class. Currently I only see the presence and absence of some child properties. This would need quite some logic to do the mapping.
If you can change the JSON format, the easiest thing would be to add the property to the Parent which you specified in the following annotation (here: type):
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
You can e.g. pass the value for this property hardcoded to the parent constructor to make sure serialized object has always the correct value.
I was able to resolve this by taking advantage of the fact that one of the properties on the Parent class could be leveraged in the absence of a "type" field.
The problem was that the JSON string being serialized does not include a "type" field, so rather than using:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
I changed "type" to that property name:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "theOtherProperty")
This resolved my issues when attempting to deserialize the object. However, it is ultimately a workaround since it is not solving the original issue (doing some form of modification during the deserialization process to add an additional "type" field) but I am unsure of how to go about doing this anyhow.
In Java, using Jackson, I want to deserialize JSON that looks something like this:
{
"123_ABC": {
"XYZ": 768,
"123_DATA": {
"123_DEF": "",
"123_ACT": "ZAC",
"123_PAG": {
"123_PAG_A": 1,
"123_PAG_B": 1
}
}
}
}
You all know that identifiers starting with a number are invalid in Java (and every programming language I ever heard of.)
I already know how to use #JsonProperty to translate field names, but handling class names is outside my knowledge.
If I define classes corresponding to the structure of the JSON, but with valid class names, is there a way to use Jackson annotations to map the invalid class id in the JSON to my valid class names?
I think #JsonProperty should be good to deserialize this.
Let's create a wrapper class that will have 123_ABC as a property of class ValidClass.
class Wrapper {
#JsonProperty("123_ABC")
private ValidClass validName;
}
Now, when you serialize, it will create JSON like this (or can be deserialized using that)
{ "123_ABC":{ //PROPERTIES OF ValidClass HERE } }
Similarly, you can have different properties in further inner classes.
In case if you to support 123_ABC only for deserialization and serialize with correct field names, you can do like this
#JsonAlias("123_ABC")
private ValidClass validName;
it will serialize to following.
{"validName": {//properties}}
but deserialization can be done using both
{"validName": {//properties}}
{"123_ABC": {//properties}}
In case, if keys keep changing, I would suggest to deserialize them in Map.
i define this class
public class PostServerActionsRequest {
private ChangePassword changePassword;
private Reboot reboot;
private Rebuild rebuild;
private Resize resize;
private String confirmResize;
private String revertResize;
private CreateImage createImage;
//constructor + getter & setter
}
that i used to parse multiple json requests using jackson json processor. The situation is the following: i receive a json with this structure:
//Json A
{
"reboot": {
"type": "SOFT"
}
}
where i define class Reboot with a private String attribute whose name is type. Obviously the attributes that haven't the relative equivalent in json are set to null by jackson during deserialization. So serializing i obtain this json:
//Json B
{
"changePassword": null,
"reboot": {
"type": "SOFT"
},
"rebuild": null,
"resize": null,
"confirmResize": null,
"revertResize": null,
"createImage": null
}
Now i know how tell jackson to ignore null or empty values during serialization, for example with the annotation #JsonSerialize(include=JsonSerialize.Inclusion.NON_DEFAULT) above the class PostServerActionsRequest, but my question is, it's possible to tell jackson to don't set to null values that are not in json request (json A) during deserialization? This because i want to obtain a class has only the values present in the json request.
I hope i was clear to explain my issue and thanks you in advance for your help.
it's possible to tell jackson to don't set to null values that are not in json request (json A) during deserialization?
No, it is not possible.
Indeed, what you are asking doesn't make any sense. When you create a PostServerActionsRequest, by deserializing from JSON, or by any other means, each of the fields in the PostServerActionsRequest instance has to have a value.
It is simply meaningless to talk about a field of a Java object as having "no value" ... in the sense you are talking about. For fields that are reference typed, the value has to be either a reference to some object ... or null. There are simply no alternatives.
We have the following Json:
{
"type" : "1",
"otherStuff" : "2",
...
"items" : [
{
"commonItemAttribute" : "value",
"specificToType1" : "whatever"
...
}
]
}
We need to polymorphically deserialise the items into different sub classes based on the type attribute.
Is it possible in a custom Jackson deserialiser to get the type value?
Can we safely look back up the Json tree using the JsonParser given to the deserialize method?
I found this blog about polymorphic deserialisation but it seems to require a type attribute on the items themselves.
Thanks
This kind of JSON is not supported, since while "External" type ids of form:
{ "childType" : "SomeTypeId",
"child" : { .... }
}
are supported (with #JsonTypeInfo.As.EXTERNAL_PROPERTY), they only work for simple types, not for Collections or Maps.
So if you can't change JSON to be bit more standard (including type id for elements is the standard way), you will need to use custom serializers, deserializers.
I am writing a RESTful web service using Java and Jersey, where the service will accept either XML or JSON inputs. Jackson is used as the JSON deserializer, and integrated into the Jersey config.
One of the endpoints is a POST request to a URL, where the content can be one of several different Java classes, and there is a common base class. These classes - with XML annotations - are:
#XmlRootElement(name = "action")
#XmlAccessorType(XmlAccessType.NONE)
#XmlSeeAlso({ FirstAction.class, SecondAction.class, ThirdAction.class })
public abstract class BaseAction {
}
#XmlRootElement(name = "first-action")
#XmlAccessorType(XmlAccessType.NONE)
public class FirstAction extends BaseAction implements Serializable {
}
// Likewise for SecondAction, ThirdAction
In my resource I can declare a method like:
#POST
#Path("/{id}/action")
public Response invokeAction(#PathParam("id") String id, BaseAction action) {...}
Then I can POST an XML fragment that looks like <firstAction/> and my method will be invoked with a FirstAction instance. So far so good.
Where I'm struggling is getting the JSON deserialization to work as seamlessly as the XML deserialization. Where the #XmlSeeAlso annotation was critical to get the XML deserialization working properly, it seemed that the equivalent for JSON was #JsonSubTypes. So I annotated the classes like this:
// XML annotations removed for brevity, but they are present as in the previous code snippet
#JsonSubTypes({ #JsonSubTypes.Type(name = "first-action", value = FirstAction.class),
#JsonSubTypes.Type(name = "second-action", value = SecondAction.class),
#JsonSubTypes.Type(name = "third-action", value = ThirdAction.class) })
public abstract class BaseAction {
}
#JsonRootName("first-action")
public class FirstAction extends BaseAction implements Serializable {
}
// Likewise for SecondAction, ThirdAction
I then feed it my test input: { "first-action": null } but all I can get is:
"org.codehaus.jackson.map.JsonMappingException: Root name 'first-action' does not match expected ('action') for type [simple type, class com.alu.openstack.domain.compute.server.actions.BaseAction]"
Unfortunately since I'm trying to be compatible with someone else's API I can't change my sample input - { "first-action": null } has to work, and deliver to my method an object of class FirstAction. (The action doesn't have any fields, which is why null shouldn't be a problem - it's the type of the class that's important).
What's the correct way to have the JSON deserialization work in the same way as the XML deserialization already is?
If you are using Jackson, you are looking for #JsonTypeInfo and #Type . Please see here for more information
JSON does not work the way XML does, so the solution is not identical.
What you need to use is (like the other answer said), #JsonTypeInfo. That only triggers inclusion and use of the type identifier. If so, then '#JsonSubTypes` will be of use in deserialization.
The reason this indicator must be used is simple: if you have more than one alternative type to deserialize to, there must be something to differentiate.
Note, too, that this does NOT have to be a property -- while most users choose "As.PROPERTY" inclusion, it is not (IMO) the best way. "WRAPPER_OBJECT" may be what you are looking for, as it adds an extra intermediate JSON property, which is somewhat similar to what XML does.
I investigated the use of #JsonTypeInfo but ran into problems because I could not alter the input format. The parser absolutely had to be able to handle input { "first-action":null }. This ruled out the possibility of adding an #type or #class property. Using a wrapper object may have worked, but it choked on the null payload.
A crucial point was that I was using the UNWRAP_ROOT_PROPERTY configuration option. Jackson was absolutely insisting on finding an action property and I could not get it to consider anything else. So, I had to selectively disable UNWRAP_ROOT_PROPERTY for certain domain objects, so that Jackson would be open to parsing alternatives. I modified the project's ContextResolver.getContext(...) implementation to check for a #JsonRootName annotation - since this only has meaning if wrapping is enabled, I used the presence of this annotation to determine whether to return an object mapper configured with root property wrapping on, or off.
At this stage, I might have been able to use #JsonTypeInfo(include=JsonTypeInfo.As.WRAPPER_OBJECT, ...), except for the issue with the null payload mentioned above (this is used to indicate that the child object has no properties - if the spec I was working from had given an empty object {} instead then there would not be a problem). So to proceed I needed a custom type resolver.
I created a new class that extended org.codehaus.jackson.map.TypeDeserializer, with the purpose that whenever Jackson is called to deserialize a BaseAction instance, it will call this custom deserializer. The deserializer will be given a subtypes array, which for BaseAction maps first-action, second-action, etc. to FirstAction.class, etc. The deserializer reads the input stream for the field name, then matches the name to a class. If the next token is an object, then it finds and delegates to the appropriate deserializer for that class, or if it is null it finds the no-args constructor and invokes it to get an object.
A class that implements org.codehaus.jackson.map.jsontype.TypeResolverBuilder is needed that can build an instance of this previous class, and then the TypeResolverBuilder is given as a #JsonTypeResolver annotation on the BaseAction class.