4

I'm using Jackson 2.7.0. and latest Jersey for JSON on REST API that handles DB communication with Hibernate 5+.

I don't know how to verify incoming JSON if there are any missing properties in it. It is impossible to perform checking on primitive type if they are null. But also i would like to use primitive types because of performance hit. What is best practice to handle such problem?

When I receive JSON like below, everything is ok:

{"vehicle":{"id":1},"distance":1000,"quantity":2000} 

But when i receive JSON like:

{"vehicle":{"id":1},"quantity":2000}

then distance is set to default value 0.

My entity look like

public class Consumption{

private int id;
private double quantity;
private double distance;

@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public Consumption(
        @JsonProperty(value = "quantity", required = true)double quantity, 
        @JsonProperty(value = "distance", required = true)double distance,
        @JsonProperty(value = "vehicle", required = false)Vehicle vehicle) {...

And REST method:

@POST
@Path("/setConsumption/")
public Response setConsumption(@Valid Consumption consum){...

Tried solution #1

I have already tried, to set all values to default value of -1 and then check if it is less then 0, but problem remains with boolean values, where i can not set it to the "neutral" value.

How do you usually handle such problem with missing property in JSON?

Tried solution #2

As you can see i have used @JsonProperty(value = "quantity", required = true) in constructor. This is new feature for Jackson 2.7.0.+, that handles this type of problem. But i get this exception:

Missing required creator property 'distance' (index 1) at 
[Source:org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream@1b72cb6; line: 1, column: 181]

This exception is returned to user BEFORE JSON reaches my code. So i can not catch it as exception. I have custom responses with httpCode and errorMessage to inform user about them. Problem is that i don't know how to catch this exception and return it to user in my custom form with my text.

Searching for solution

I have either to catch exception of new Jackson feature or try to validate JSON. But i would not like to use class Integer instead of primitive data type int, because of performance. If it is possible not to write custom JSON deserializer for all 60+ classes i have in project.

If any other solution then Jackson or Jersey supports this types of handling of missing properties, feel free to comment.

TDLR - how to check if variable of primitive data type (int, double, boolean,...) was in incoming JSON or not, without manually writting deserializers for each of 60+ classes in project. Java does not allow to check those types with null.

Suggestion #1

Trying to register an ExceptionMapper for JsonMappingException and see if it overrides the one already registered by Jersey.

My code (still getting default "Missing property ..." exception - problem not solved):

@Provider
public class JacksonNoPropertyMapper implements ExceptionMapper<JsonMappingException> {
    @Override
    public Response toResponse(JsonMappingException e) {

    return Response.status(999).entity("OVERRIDE TEST").type(MediaType.APPLICATION_JSON).build();
}

}

AndroidTank
  • 324
  • 4
  • 11
  • Did you check if the performance loses was really so important ? – Julien Jan 29 '16 at 09:06
  • Integer takes 300% memory then int (in theory). What do you suggest how to test it? Also all math functions and comparisons with Integer are more complicated. – AndroidTank Jan 29 '16 at 09:35
  • You can do some benchmark like explained in [this question](http://stackoverflow.com/questions/4583175/benchmarking-java-programs). You can still convert an integer to a plain int by callinkg int value once you have made sure it's not null, which can solve your issue. – Julien Jan 29 '16 at 09:45
  • Just to expand on your problem, it's not just _this_ exception you need to deal with, but with any other unexpected Jackson exceptions that might occur, you're going to get the same error response. You can try to an register an ExceptionMapper for JsonMappingException and see if it overrides the one already registered by Jersey. if Not you can disable the JacksonFeature altogether, and just register all the Jackson components you need yourself. – Paul Samsotha Jan 29 '16 at 09:57
  • You can look at the source code for [JacksonFeature](https://github.com/jersey/jersey/blob/master/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/JacksonFeature.java) to get some ideas, if you need to go this route. – Paul Samsotha Jan 29 '16 at 09:57
  • @peeskillet , thanks for reply. But i'm pretty much in the dark. I have searched for example of your advice on the internet, but it does not work. I have updated question, please check code. Do you have any suggestions what to change, did i miss something? – AndroidTank Jan 29 '16 at 12:40
  • When I tried it (the ExceptionMapper), it worked. Add an s.o.p in the constructor of your mapper to see if it gets registered. – Paul Samsotha Jan 29 '16 at 13:13
  • No it does not =/ Do you have any other idea what to do with it? – AndroidTank Jan 29 '16 at 16:09
  • How are you registering all your resource classes? Through scanning? If so put the mapper in the same package as your resource classes – Paul Samsotha Jan 30 '16 at 00:52
  • I don't know how i'm registering my resource classes. How do i check this? I tried to move mapper to all of packages, but it did not catch exception. – AndroidTank Jan 30 '16 at 11:57

1 Answers1

2

The easiest way for you to approach this would be to use the Object counter parts on fields that are optional, it shouldn't affect you too much performance wise, and would allow you to null check the fields.

public class Consumption {

private int id;
private double quantity;
private Double distance;

public boolean isDistanceSet() {
    return distance != null;
}
DominicEU
  • 3,585
  • 2
  • 21
  • 32
  • 1
    I would add that if the field can be nullable, having an Object instead of a primitive type perfectly makes sense. Switching from primitive type to Object type won't change anything to performance, and I would even say that if you reached the point that you care using int or Integer, then you can be sure that this is not going to be the bottleneck in you app. – Louis F. Jan 29 '16 at 09:55