Object value as null in Jackson Parsing - java

I am using Jackson library and trying to achieve that is mentioned here
BaseOperationRequest.java
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "command"
)
#JsonSubTypes({
#JsonSubTypes.Type(name = "ZADD", value = ZAddBaseOperationRequest.class)
})
public class BaseOperationRequest {
public short operationId;
public Command command;
public String gameId;
public String key;
}
ZAddBaseOperationRequest.java
public class ZAddBaseOperationRequest extends BaseOperationRequest{
public Map<String, Double> members;
}
Command.java
public enum Command{
ZADD,
HSET
}
The problem is here when I try to pass the Object from REST call which is something like this:
#RestController
public class MyController{
//keeping just for now as GET, will change it to POST and take it in RequesBody later on
#RequestMapping(value = "/process/{object}", method = RequestMethod.GET, produces = "application/json")
public #ResponseBody ResponseEntity process(#Pathvariable String object){
System.out.println(object);//I am getting this as correct--->(A)
BaseOperationRequest[] baseOperationRequestArray = new ObjectMapper().readValue(object, BaseOperationRequest[].class);//getting exception --->(B)
System.out.println(baseOperationRequestArray);
}
}
Now, I am calling this as follows:
1st scenario
CALLING WITHOUT MEMBERS MAP:
<server>:<port>/.../process/[{"operationId":1,"command":"ZADD","gameId":"t5","key":"abc"}]
The process method is getting called and since Jackson is told to create the Object of ZAddBaseOperationRequest when getting ZADD in command, it is doing it but the value of command itself is assigned as null in the resultant object.
Please explain why? Where did the value of command went?
2nd scenario
CALLING WITH MEMBERS MAP:
:/.../process/[{"members":{"a":1.0},"operationId":1,"command":"ZADD","gameId":"t5","key":"abc"}]
then in this case, the equation (A) is showing [{"members":{"a":1.0,b that's it, where did the other part of the GET went.
This is making me mad. :).
Thanks in advance..
Please help.

It is not a good practice to send json as path parameter.
To fix your problem add visible=true in JsonTypeInfo annotation. Your declaration will become:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "command",
visible = true
)
As per jackson documentation for visible:
Property that defines whether type identifier value will be passed as part of JSON stream to deserializer (true), or handled and removed by TypeDeserializer (false).
Default value is false, meaning that Jackson handles and removes the type identifier from JSON content that is passed to JsonDeserializer.

Related

Make POJO dynamic in nature to accept any amount of JSON array variables

I have a Bean class which I have constructed according to the response I have got. Below is my Postman response.
{
"EU": [
{
"calId": "EU",
"calDate": "2022-11-01",
"prevBusinessDay": "2022-11-01",
"nextBusinessDay": "2022-11-01",
"businessDay": true,
"monthEndBusinessDay": false
}
],
"AU": [
{
"calId": "AU",
"calDate": "2022-11-01",
"prevBusinessDay": "2022-11-01",
"nextBusinessDay": "2022-11-01",
"businessDay": true,
"monthEndBusinessDay": false
}
]
}
According to this I have constructed the Bean as follows
#Data
#AllArgsConstructor
#NoArgsConstructor
public class IndexCalendarDateResponseBean {
#JsonProperty("EU")
private List<IndexCalendarDateResponseWrapper> EU;
#JsonProperty("AU")
private List<IndexCalendarDateResponseWrapper> AU;
}
IndexCalendarDateResponseWrapper is another POJO which contains all the variables calId,calDate etc which are inside the two JSON arrays.
My issue here is every time this array names cannot be EU and AU. This depends on whatever I pass in the API request URI path parameters. If I pass something else, then that will be the JSON array variable names. So my bean will not work for that. So I wanted to construct a bean that should be dynamic in nature. What I am doing with this bean is I am using this bean to fetch an API response as follows.
IndexCalendarDateResponseBean actualRIOutput = JsonPath.from(response.extract().asInputStream()).getObject("", IndexCalendarDateResponseBean.class);
So how to construct a bean such that even if I pass say something else as path parameters say JU, BU, and SU or JU and BU anything, it will fetch the response for me? Also the JSON array variables I am passing in path parameters can also vary in quantity i.e. it can be two, three or any number of paramaters I can pass. So that also the bean should accept? Is any other bean needed for this idea to support?
So how to construct a bean such that even if I pass say something else as path parameters say JU, BU, and SU or JU and BU anything, it will fetch the response for me?
You can create a field of type Map<String, List<IndexCalendarDateResponseWrapper>> and make use of the method annotated with #JsonAnySetter to populate it.
If you need the ability to serialize this POJO into the same JSON, then you can add another method exposing the data of the Map annotated with #JsonAnyGetter.
public class IndexCalendarDateResponseBean {
private Map<String, List<IndexCalendarDateResponseWrapper>> responseWrappers = new HashMap<>();
#JsonAnySetter
public void readResponseWrappers(String key, List<IndexCalendarDateResponseWrapper> value) {
responseWrappers.put(key,value);
}
#JsonAnyGetter
public Map<String, List<IndexCalendarDateResponseWrapper>> writeResponseWrappers() {
return responseWrappers;
}
}
Also the JSON array variables I am passing in path parameters can also vary in quantity i.e. it can be two, three or any number of paramaters
When you're deserializing JSON into a POJO missing properties would be set to default values of the corresponding type (for instance, null for reference types).
When you're serializing a POJO into JSON if you don't want to include null fields you can make use of the annotation #JsonInclude providing the value of JsonInclude.Include.NON_NULL or JsonInclude.Include.NON_EMPTY (depending on your requirements).
Example:
#JsonInclude(JsonInclude.Include.NON_NULL)
public class IndexCalendarDateResponseWrapper {
// field, constructors, etc.
}
Why not replace IndexCalendarDateResponseBean with Map<Stirg,List< IndexCalendarDateResponseWrapper>>。
private static ObjectMapper mapper = new ObjectMapper();
static {
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
}
public static <T> T parseObject(String json, TypeReference<T> typeRef) throws Exception {
return mapper.readValue(json, typeRef);
}
....
return JsonUtils.parseObject(jsonStr, new TypeReference<Map<String, List<IndexCalendarDateResponseWrapper>>>() {
});

Spring REST any way to set the JSON type field automatically?

I'm using Spring 5.3.20 to build a REST/JSON API, which is based on automatic mapping of Java POJOs to JSON objects. For instance:
#RestController
#RequestMapping ( path = "/{ds}/dataset-info", method = { RequestMethod.GET, RequestMethod.POST } )
#CrossOrigin
public class DatasetInfoService
{
#RequestMapping ( path = "" )
public DatasetInfo datasetInfo ()
{
// TODO: mockup data that need to be replaced with a real fetch from config
return new DatasetInfo () {
{
this.setTitle ( "AraTiny Dataset" );
this.setOrganization ( "Rothamsted Research" );
this.setSpecies ( List.of (
new SpecieInfo ( "3702", "Thale cress", "Arabidopsis Thaliana" ),
new SpecieInfo ( "4565", "Bread Wheat", "Triticum aestivum" ),
new SpecieInfo ( "4577", "Maize", "Zea mays" )
));
}
};
}
...
}
This returns what expected, something like:
{
"title": "AraTiny Dataset",
"organization": "Rothamsted Research",
"species": [
{
"taxId": "3702",
"commonName": "Thale cress",
"scientificName": "Arabidopsis Thaliana"
},
...
}
Now, my question is: I'd like to add something like "#type": "DatasetInfo" at the root level of every JSON object, and I'd like to do it automatically. The value could be taken from the class name, so, in principle, it is automatable.
Is there a simple way to do it in Spring? Like, setting a flag? Or setting a decorator for a JSON serialiser?
The intuitive solution would be defining getType() for each POJO class (or, as suggested in a comment, in a root class), but I don't think that's the most seamless one, some kind of annotation (eg, #JsonType) would already be better, and it would be great if some helper like that already exists, so I don't need to implement one on my own.
I am sure there is a question about this somewhere, but i can't find it. You are looking for this annotation - JsonTypeInfo.
Annotation used for configuring details of if and how type information is used with JSON serialization and deserialization, to preserve information about actual class of Object instances. This is necessarily for polymorphic types, and may also be needed to link abstract declared types and matching concrete implementation.
Example usage
package temp;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "#whatever_property_you_desire")
public class DatasetInfo {
private String name;
//getters and setters
}
property specifies the name of the property, use = JsonTypeInfo.Id.CLASS specifies that the fully qualified name will be used as type identifier.
Test
public class Temp {
public static void main(String[] args) throws Exception {
DatasetInfo info = new DatasetInfo();
info.setName("test");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(info);
System.out.println(json);
}
}
DatasetInfo will be serialized as:
{
"#whatever_property_you_desire": "temp.DatasetInfo",
"name": "test"
}
Edit: Since you seem to want the property to be named #type and to use simple class name, you can simplify to
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
Id.NAME serializes type info as simple class name, and default property name for it is #type.
Did you look at annotating your classes with #JsonTypeInfo ? You could even make this available as a marker interface:
#JsonTypeInfo(use=JsonTypeInfo.Id.MINIMAL_CLASS, property="type")
public interface JsonTyped {
}
Then your response classes can implement this marker interface in case you want them to include the basic class name.
public class DataSetInto implements JsonTyped {
...
}

Validating with Spring that a Boolean element comes explicitly defined

We got this REST endpoint in which one of the field is mapped to a Boolean (The wrapper class) instance. We are using a Boolean instead of a boolean because design decision, so this is non-negotiable.
This Boolean value is mandatory and it must be specified by the sender ("whateverValue":"" should return a 400 error), but when arriving to the endpoint, the value is automatically converted to a correct false value.
So, the question is: Can this be done? Are we not understanding the contract of using a "Boolean" object instead of the primitive?
EDIT: Just to clarify, we are already validating "whateverValue":null, and the value can be either true or false, so, as far as I know, neither #NotNull or #AssertTrue/False can be used here.
If you want to validate the Object Type Boolean you should use #NotNull
Here is a question where this has been asked before.
I use #NotNull if a boolean is mendatory to be set ans #AssertTrue/false to verify the incoming value has a specific state.
Hope this helps
I coded your scenario as follows and it was ok!
Please correct me if my understanding (from your scenario) is incorrect.
Create DTO
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class TestDto {
#NotNull
Boolean testValue;
//getter and setter
}
Create Endpoint
#RestController
public class testController {
#RequestMapping(value = "/test", method = RequestMethod.POST)
public void accountTrades(#RequestBody #Valid TestDto testDto) {
System.out.println(testDto.getTestValue());
}
}
Using Postman, when I send testValue:"", it throws a notnull exception, that means the server has received null, and there is no null to false conversion. (It worked correctly!)
Server response is as follows

Picketlink: How to get annotation parameters and the name of the function decorated when using #Secures?

I'm trying to define and use a custom security binding type called BasicRolesAllowed, as has been demonstrated in the Picketlink quickstarts here.
The only different between my type the ones in the quickstart, is that my annotation has to accept an array of strings (we want to secure methods using not just one but possibly combinations of roles), and thus my annotation is defined thus:
public #interface BasicRolesAllowed {
String[] value() default {};
}
Following the quickstart, I've tried to define how this decorator authenticates as such:
#Secures
#BasicRolesAllowed
public boolean doAdminCheck(Identity identity, IdentityManager identityManager, RelationshipManager relationshipManager) throws Exception {
/*
Sample usage of #BasicRolesAllowed is like:
#BasicRolesAllowed(value = RoleConstants.CREATE_USER)
TODO: need to get these from the #BasicRolesAllowed annotation instance/usage
*/
String[] requiredRoles = {};// get these from annotation
boolean isAuthorized = true;
for (String role : requiredRoles)
isAuthorized = isAuthorized && hasRole(relationshipManager, identity.getAccount(), getRole(identityManager, role));
return isAuthorized;
}
And as can be seen in the snippet, the trick part is:
String[] requiredRoles = {};// get these from annotation
How do I get the string constants passed to the annotation on the decorated method so I can use them in looking up roles?
Some Hints:
There's an answer to a similar question here, but the problem is that in that solution; one needs to know the name of the decorated function or class - which in my case is impossible given that the decorator will be used just about anywhere, and I don't know how to get these via the method shown in the Picketlink quickstart.
Also, the solution only shows how to obtain the value passed to an annotation expecting only 1 string - maybe I could try using values(), but the above limitation still stands in my way.
Thanks in advance to anyone who can help.
Thanks to #pedroigor over at #picketlink (freenode), the solution can be gleaned from an example of such a use-case in the picketlink quickstart here. In that file, a method getAnnotation() is defined, which has the signature:
private <T extends Annotation> T getAnnotation(InvocationContext invocationContext, Class<T> annotationType)
So, using this method, I'm able to introspect and obtain the values passed to my annotation as can be seen in my new implementation of the roles checking method here:
#Secures
#BasicRolesAllowed
public boolean hasBasicRolesCheck(InvocationContext invocationContext, Identity identity, IdentityManager identityManager, RelationshipManager relationshipManager) throws Exception {
BasicRolesAllowed basicRolesAllowed = getAnnotation(invocationContext,BasicRolesAllowed.class);
String[] requiredRoles = basicRolesAllowed.value();// get these from annotation
boolean isAuthorized = true;
for (String role : requiredRoles)
isAuthorized = isAuthorized && hasRole(relationshipManager, identity.getAccount(), getRole(identityManager, role));
return isAuthorized;
}
The essential modifications being:
I had to pass an instance of the invocation context InvocationContext invocationContext by adding this as a parameter to my method definition (CDI magic takes care of all else I hear).
I then obtain the annotation instance by calling:
BasicRolesAllowed basicRolesAllowed = getAnnotation(invocationContext,BasicRolesAllowed.class);
And then get the values/parameters passed to the annotation thus:
String[] requiredRoles = basicRolesAllowed.value();// get these from annotation
This solves my problem :-)

Jackson: What happens if a property is missing?

What happens if I annotate a constructor parameter using #JsonProperty but the Json doesn't specify that property. What value does the constructor get?
How do I differentiate between a property having a null value versus a property that is not present in the JSON?
Summarizing excellent answers by Programmer Bruce and StaxMan:
Missing properties referenced by the constructor are assigned a default value as defined by Java.
You can use setter methods to differentiate between properties that are implicitly or explicitly set. Setter methods are only invoked for properties with explicit values. Setter methods can keep track of whether a property was explicitly set using a boolean flag (e.g. isValueSet).
What happens if I annotate a constructor parameter using #JsonProperty but the Json doesn't specify that property. What value does the constructor get?
For questions such as this, I like to just write a sample program and see what happens.
Following is such a sample program.
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.ObjectMapper;
public class JacksonFoo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
// {"name":"Fred","id":42}
String jsonInput1 = "{\"name\":\"Fred\",\"id\":42}";
Bar bar1 = mapper.readValue(jsonInput1, Bar.class);
System.out.println(bar1);
// output:
// Bar: name=Fred, id=42
// {"name":"James"}
String jsonInput2 = "{\"name\":\"James\"}";
Bar bar2 = mapper.readValue(jsonInput2, Bar.class);
System.out.println(bar2);
// output:
// Bar: name=James, id=0
// {"id":7}
String jsonInput3 = "{\"id\":7}";
Bar bar3 = mapper.readValue(jsonInput3, Bar.class);
System.out.println(bar3);
// output:
// Bar: name=null, id=7
}
}
class Bar
{
private String name = "BLANK";
private int id = -1;
Bar(#JsonProperty("name") String n, #JsonProperty("id") int i)
{
name = n;
id = i;
}
#Override
public String toString()
{
return String.format("Bar: name=%s, id=%d", name, id);
}
}
The result is that the constructor is passed the default value for the data type.
How do I differentiate between a property having a null value versus a property that is not present in the JSON?
One simple approach would be to check for a default value post deserialization processing, since if the element were present in the JSON but had a null value, then the null value would be used to replace any default value given the corresponding Java field. For example:
import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
import org.codehaus.jackson.annotate.JsonMethod;
import org.codehaus.jackson.map.ObjectMapper;
public class JacksonFooToo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper().setVisibility(JsonMethod.FIELD, Visibility.ANY);
// {"name":null,"id":99}
String jsonInput1 = "{\"name\":null,\"id\":99}";
BarToo barToo1 = mapper.readValue(jsonInput1, BarToo.class);
System.out.println(barToo1);
// output:
// BarToo: name=null, id=99
// {"id":99}
String jsonInput2 = "{\"id\":99}";
BarToo barToo2 = mapper.readValue(jsonInput2, BarToo.class);
System.out.println(barToo2);
// output:
// BarToo: name=BLANK, id=99
// Interrogate barToo1 and barToo2 for
// the current value of the name field.
// If it's null, then it was null in the JSON.
// If it's BLANK, then it was missing in the JSON.
}
}
class BarToo
{
String name = "BLANK";
int id = -1;
#Override
public String toString()
{
return String.format("BarToo: name=%s, id=%d", name, id);
}
}
Another approach would be to implement a custom deserializer that checks for the required JSON elements. And yet another approach would be to log an enhancement request with the Jackson project at http://jira.codehaus.org/browse/JACKSON
In addition to constructor behavior explained in #Programmer_Bruce's answer, one way to differentiate between null value and missing value is to define a setter: setter is only called with explicit null value.
Custom setter can then set a private boolean flag ("isValueSet" or whatever) if you want to keep track of values set.
Setters have precedence over fields, in case both field and setter exist, so you can "override" behavior this way as well.
I'm thinking of using something in the style of an Option class, where a Nothing object would tell me if there is such a value or not. Has anyone done something like this with Jackson (in Java, not Scala, et al)?
(My answer might be useful to some people finding this thread via google, even if it doesn't answer OPs question)
If you are dealing with primitive types which are omittable, and you do not want to use a setter like described in the other answers (for example if you want your field to be final), you can use box objects:
public class Foo {
private final int number;
public Foo(#JsonProperty Integer number) {
if (number == null) {
this.number = 42; // some default value
} else {
this.number = number;
}
}
}
this doesn't work if the JSON actually contains null, but it can be sufficient if you know it will only contain primitives or be absent
another option is to validate the object after deserialization either manually or via frameworks such java bean validation or, if you are using spring, the spring validation support.

Categories