how to map JSON parsing related errors to json response in jersey
Asked Answered
S

4

6

I defined a datatype and its API:

public class DataType {
    @Column
    private String name;
}

// API is:
public class DataTypeAPI {
    @POST
    @Path("/")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    @ManagedAsync
    public void createDataType(
            final DataType dataType,
    ) {
        ...
        asyncResponse.resume(Response.status(Response.Status.CREATED)
                .entity(dataType)
                .build());
    }
}

Everything is fine if I posed { "name": "xxx" },

but when I posted { "name1": "xxx" }, I got the following text/plain response:

Unrecognized field "name1" (class com.xxx.datatypes.DataType), not marked as ignorable (1 known properties: "name"])
 at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream@526e34b1; line: 2, column: 15] (through reference chain: com.xxx.datatypes.DataType["name1"])

I prefer to convert the above error into JSON response. but event I added the following exception mapper, it is not returning JSON response.

@Provider

public class GenericExceptionMapper implements ExceptionMapper<Throwable> {
    public Response toResponse(Throwable ex) {
        System.out.println("GenericExceptionMapper");
        ex.printStackTrace();
        return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorBody(500, ex.getMessage()))
                .type(MediaType.APPLICATION_JSON)
                .build();
    }
}

Why my above exception mapper cannot catch the jersey parsing error. Could someone explain it to me? Thanks

UPDATE

I have two questions:

1, how to make the response to be application/json instead of text/plain?

2, why my exception mapper did not catch the exception raised and map it to a json response?

UPDATE

I added the following mapper:

public class JsonMappingExceptionMapper implements ExceptionMapper<JsonMappingException> {
    public Response toResponse(JsonMappingException ex) {
        return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorBody(500, ex.getMessage()))
                .type(MediaType.APPLICATION_JSON)
                .build();
    }
}

Now, I can get the json response: { "code": 500, "message": "Unrecognized field \"name1\" (class com.xxx.datatypes.DataType), not marked as ignorable (1 known properties: \"name\"])\n at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream@1d8cd12a; line: 2, column: 15] (through reference chain: class com.xxx.datatypes.DataType[\"name1\"])" }

I still have two questions:

1, why my generic exception mapper cannot catch it.

2, The json mapping exception mapper cannot map the following exception Unexpected character ('}' (code 125)): was expecting double-quote to start field name at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream@7615743c; line: 3, column: 2] when {"name": "xxxx",}posted (comma , added for testing)

Silica answered 30/1, 2018 at 16:44 Comment(0)
C
4

I met the same issue, and I have solved it. I try to explain your question:

Both JsonMappingException and JsonParseException extend JsonProcessingException. And Jersey has defined JsonMappingExceptionMapper for JsonMappingException and JsonParseExceptionMapper for JsonParseException.

  1. If you define your own mapper for JsonProcessingException (or more wide exception mapper), it cannot catch JsonMappingException and JsonParseException, because these two exceptions have their own mapper. If they have not, your mapper can catch.
  2. If you define your own mapper(say XxxJsonMappingExceptionMapper for JsonMappingException and YyyJsonParseExceptionMapper for JsonParseException), you may find sometimes you can catch these exceptions, sometimes cannot, it depends your own mapper is working or jersey's mapper is working. Now, you can add @Priority(1)(javax.annotation.Priority) to your mapper to make sure your mapper is working. see Jackson JsonParseExceptionMapper and JsonMappingExceptionMapper shadows custom mapper.

back to your question:

  1. Your generic exception mapper cannot catch it becasuse these exceptions has their own exception mapper(JsonMappingExceptionMapper and JsonParseExceptionMapper, they have a higher priority).
  2. You cannot catch the exception may because your own mapper is shadowed by the JsonParseExceptionMapper. Try to figure out by adding @Priority(1) to your own mapper
Champollion answered 11/12, 2020 at 10:17 Comment(0)
F
1

why my generic exception mapper cannot catch it.

Because the Jackson-JAXRS module already comes with ExceptionMappers for Jackson exceptions.

The json mapping exception mapper cannot map the following exception

I didn't test this, but it most likely because it is not a mapping exception, but a parsing exception. Jackson has both JsonParseException and JsonMappingException. You can see in the previous link that Jackson comes with an ExceptionMapper for both JsonMappingException and JsonParseException. You are only overriding the mapper for JsonMappingException.

Freightage answered 30/1, 2018 at 20:51 Comment(3)
but after I added my own JsonMappingExceptionMapper, I can get the JSON response. I tried to map JsonParseException in mapper, it is not working. so, I am confused.Silica
Yeah, I'm not sure about this.Freightage
@Silica may be the jersey's JsonParseExceptionMapper shadows yours. It's random at the program start. see my answer.Champollion
B
0

Unrecognized field "name1" (class com.xxx.datatypes.DataType), not marked as ignorable (1 known properties: "name"])

If you look at the exception raised, it is quite clear that, your POJO class has property with name name not name1.

And, you are trying to post your JSON with property name name1.

{ "name1": "xxx" }

And, it wasn't recognized as name1 wasn't defined instead name.

public class DataType {
    @Column
    private String name;
}

And that is the reason , it works fine for you in other case, when you post JSON as

{ "name": "xxx" }
Boutis answered 30/1, 2018 at 17:10 Comment(4)
Maybe I did not explain my questions clearly. I already updated my post. ThanksSilica
@Silica do you have try catch block, I don't see that in your code. If yes, then you need to build JSON response inside your catch block. If you are expecting this will done implicitly. Then you are wrong.Boutis
I think the parsing is done implicitly by jersey. and it did not reach my api codes.Silica
@Silica Yes, jersey takes care of (de)serialization, depending on your POJO. If you wanted to ignore above exception in all cases. Then you can mark Ignorable.Boutis
C
0

Im using org.glassfish.jersey-jackson-2.33. when you register JacksonFeature which is responsible for the default Jackson response with a "withoutExceptionMappers())". then if you have some Throwable exception mapper it will be catch instead of the default jackson mapper. you can also write your JsonParseException mapper if you dont use a Throwable mapper.

import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.process.internal.RequestScoped;
import org.glassfish.jersey.server.ResourceConfig;

public class JerseyApplication extends ResourceConfig {
    //register(JacksonFeature.class);
    register(JacksonFeature.withoutExceptionMappers());
}

import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

import com.fasterxml.jackson.core.JsonParseException;

@Provider
public class JsonExceptionsMapper implements ExceptionMapper<JsonParseException> {
    @Override
    public Response toResponse(JsonParseException exception) {
         //replace with your custom response
         return Response.status(Response.Status.BAD_REQUEST).entity(exception.getMessage()).type("text/plain").build();
    }
}
Centenarian answered 5/2, 2023 at 12:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.