Binding a JSON to a Java class using JAXB - java

I have the following JSON, where can be either true or false:
{"flag1":<boolean value>, "flag2":<boolean value>}
And I have tried to bind it to a Java class using Jersey and the following JAXB annotations:
#XmlRootElement
public class MyClass {
#XmlElement(name = "flag1", type = Boolean.class)
private Boolean flag1;
#XmlElement(name = "flag2", type = Boolean.class)
private Boolean flag2;
...
}
The problem is that when I assign a non-boolean value to 'flag1' or 'flag2', like in the example below, JAXB automatically assigns a false value to the 'flag1' and 'flag2' fields of MyClass.
{"flag1":"foo", "flag2":"bar"}
Is there a way to annotate 'MyClass' so that when JSON's 'flag1' and 'flag2' are not boolean I get an exception?

It looks like Jersey is simply using Boolean.valueOf, which treats everything other than a literal "true" as false. Since JavaScript doesn't have a notion of variable type, this is an arguably valid behavior.
An XML mapping, by comparison, is based on a schema definition, which does have a very specific notion of boolean values.
Not having used Jersey (or JAXB since the 1.x days), I'm wondering if you have to annotate the actual variables, or if you could annotate the setters. Or perhaps you could provide a setter that takes a String and parses it, instead of / along with a setter that takes a boolean.

What you showed would work the way you want (throw an exception) if you used pure Jackson JAX-RS provider. It does accept some variations (1 and 0, since some languages do not have native boolean type), but not things that have no meaningful equivalent.
Alternatively, as suggested, a setter method with type String would make sense, since then you could manually control conversions.

Related

How to make deserialization fail with Jackson when JSON is incomplete? [duplicate]

I'm using Jackson's readValue() method on an object mapper to read from a JSON file and convert it into my java object.
eg.
mapperObject.readValue( node, MyTargetClass.class )
Are there any annotations that I can set on MyTargetClass to enforce required attributes? For example, if I have a JSON object with properties ABC,DEF and GHI, and my Json is the following
{
"ABC" : "somevalue"
"DEF" : "someothervalue"
}
I want it to fail somehow, and only succeed on the readValue if it contained ABC, DEF and GHI.
You can mark a property as required with the #JsonProperty(required = true) annotation, and it will throw a JsonMappingException during deserialization if the property is missing or null.
Edit: I received a downvote for this without comment. I'd love to know why, since it does exactly the right thing.
Jackson does not include validation functionality, and this is by design (i.e. that is considered out-of-scope). But what is usually used is Bean Validation API implementation.
The nice thing about this is decoupling between data format handling, and validation logic.
This is what frameworks like DropWizard use; and it's the direction JAX-RS (like Jersey) are taking things for JAX-RS 2.0.
If you want to make sure a json field is provided, you have to use the #JsonProperty(value = "fieldName", required = true) annotation as a parameter to the constructor. But this is not enough, also the Constructor should have #JsonCreator annotation.
For example, if you have a field named 'endPoint' and you want o make sure it is provided in the JSON file, then the following code will throw an exception if it is not provided.
#JsonCreator
public QuerySettings(#JsonProperty(value = "endPoint", required = true) String endPoint) {
this.endPoint = endPoint;
}
I found this link helpful to understand the Jackson annotations. It also well explains why required=true is not enough and counter-intuitive to its name.
If you are neither satisfied with using #JsonProperty(required = true) as it works only with #JsonCreator nor with the use of bean validation then one more way of tackling it would be to catch this in your setter methods for the relevant variables.
You can simply check if the variable is null before setting it and throw an IllegalArgumentException or NullPointerException (as preferred by few people)
Note: It depends on how your POJO is defined too, so please make sure that it is going the setter method route for this solution to work.

Can I ignore a getter-based write-only property during deserialization in Java Jackson?

I have a legacy JSON API class that I'm evolving to remove a certain property. It's currently at a point where the property value is always the same constant, so I would like my Java code to be just a simple getter with no underlying field for it. I want to continue serializing the value until I know that all my clients have migrated off of using the value. The object is only read by my clients, so I don't have to worry about them sending other values across.
public class MyType {
private String value;
public boolean isLegacyValue() {
return true;
}
}
That said, I don't want any test code or the like to fail if I deserialize a full value with the now-constant property. Is there a way I can tell Jackson to serialize a setter method-only property, but ignore it on deserialization? I tried a few different things, but I get a UnrecognizedPropertyException on deserialization. I'd rather not change the global DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES just for this one property.
{"value": "ABC", "legacyValue": true}
Also acceptable would be a way to tell Jackson to include the value without including a Java field for it.
I'm aware I can add a getter in addition to my setter, or make it a field, but both those options feel like they're confusing the Java API, as it's not actually matching the constant constraint:
public void setLegacyValue(boolean legacyValue) {
// No-op; only exists for Jackson deserialization
}
One thing I've found to work through trial and error is making it a final field. For whatever reason, Jackson knows to handle that as a write-only constant in a way that doesn't work with the getter without matching setter. This will be my solution if there's no way to do it with just a getter.
private final boolean legacyValue = true;
public boolean isLegacyValue() {
return legacyValue;
}
Jackson supports "one-way" properties using the access parameter of #JsonProperty. Annotate your property like this:
#JsonProperty(access = READ_ONLY)
public boolean isLegacyValue() {
return true;
}

Dynamodb - Convert enumerator collection

Is there a way to convert a collection of enums? I've tried the following without success:
#DynamoDBTypeConvertedEnum
#DynamoDBAttribute(attributeName="myEnums")
private Collection<MyEnum> myEnums;
In order to save it as collection (i.e. set of strings), please use #DynamoDBTyped annotation with SS attribute type.
#DynamoDBTyped(DynamoDBAttributeType.SS)
#DynamoDBAttribute(attributeName = "myEnums")
public Set<MyEnum> getMyEnums() {
return myEnums;
}
myEnums is a Set of enums, not an enum itself.
DynamoDBTyped
The enum type is only supported by override or custom converter
Because you have a set of enums, you will need to use a customer converter. So remove the #DynamoDBTypeConvertedEnum annotation and use a custom converter. You can see an example I have posted before here. Its a generic Set example but it will work for your enum Set.
EDIT: i.e. use DynamoDBTypeConverted

Jackson ObjectMapper: specify required fields [duplicate]

I'm using Jackson's readValue() method on an object mapper to read from a JSON file and convert it into my java object.
eg.
mapperObject.readValue( node, MyTargetClass.class )
Are there any annotations that I can set on MyTargetClass to enforce required attributes? For example, if I have a JSON object with properties ABC,DEF and GHI, and my Json is the following
{
"ABC" : "somevalue"
"DEF" : "someothervalue"
}
I want it to fail somehow, and only succeed on the readValue if it contained ABC, DEF and GHI.
You can mark a property as required with the #JsonProperty(required = true) annotation, and it will throw a JsonMappingException during deserialization if the property is missing or null.
Edit: I received a downvote for this without comment. I'd love to know why, since it does exactly the right thing.
Jackson does not include validation functionality, and this is by design (i.e. that is considered out-of-scope). But what is usually used is Bean Validation API implementation.
The nice thing about this is decoupling between data format handling, and validation logic.
This is what frameworks like DropWizard use; and it's the direction JAX-RS (like Jersey) are taking things for JAX-RS 2.0.
If you want to make sure a json field is provided, you have to use the #JsonProperty(value = "fieldName", required = true) annotation as a parameter to the constructor. But this is not enough, also the Constructor should have #JsonCreator annotation.
For example, if you have a field named 'endPoint' and you want o make sure it is provided in the JSON file, then the following code will throw an exception if it is not provided.
#JsonCreator
public QuerySettings(#JsonProperty(value = "endPoint", required = true) String endPoint) {
this.endPoint = endPoint;
}
I found this link helpful to understand the Jackson annotations. It also well explains why required=true is not enough and counter-intuitive to its name.
If you are neither satisfied with using #JsonProperty(required = true) as it works only with #JsonCreator nor with the use of bean validation then one more way of tackling it would be to catch this in your setter methods for the relevant variables.
You can simply check if the variable is null before setting it and throw an IllegalArgumentException or NullPointerException (as preferred by few people)
Note: It depends on how your POJO is defined too, so please make sure that it is going the setter method route for this solution to work.

Jackson equivalent to #XmlSeeAlso

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.

Categories

Resources