Deserializing null into a specific object in gson - java

I have this old implementation of awebservice class, which works off of retrofit1 and rxjava1 and is just a pain to work with, so wrote a new one from scratch.
Every response to an API-request is enveloped with the following object
{
"status":200,
"payload": {}
}
I bypassed the envelope-problem by simply wrapping every response into a generic object and when accessing the payload, I can do that easily by calling the getter
class BaseResponse<T> {
int status;
T payload;
public T getPayload(){
return payload;
}
}
The idea is to turn this BaseResponse<T> into Observable<T> and then call map {it.payload} to work with in some other place
interface API {
#Body("blablabla")
Observable<BaseResponse<SomeResponse>> someCall(#Body SomeRequest request);
}
public Observable<SomeResponse> someCall(SomeRequest request){
return api.someCall(request)
.map( response -> response.payload)
}
Now I've run into a problem. There are some API-requests, where the responsebody is defined as empty. I defined these empty responsebodies as class EmptyResponse {}
The problem is that I get those empty bodies like this
{
"status":200,
"payload":null
}
Now the problem is that payload is null and rxjava2 doesn't want null values.
Is there some way to have GSON deserialize null into an object of type EmptyResponse? Note that this ONLY occurs for calls that are documented to return empty responsebodies, there is no way that a call would return one, if it had a documented body

Related

Is is possible to take accept JSON objects in the body of a POST request without having getter and setter methods?

I need to accept JSON data in the body of a POST request. Is is possible to accept all the input JSON data that is present in the body of POST method without defining the getter and setter methods for all the keys in the JSON objects ?
#POST
#Path("/post")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public postData GiveData(final postData output) {
return output;
}
My Get and Set methods ::
public class TestClass {
#JsonProperty("Type")
public String getType() {
return Type;
}
public void setType(String Type) {
this.Type = Type;
}
}
If JSON input is
{
Type : "Test"
}
Returns 200 :
But If the JSON input is
{
"Type" : "Test" ,
"Random-KeY" : "Value"
}
Returns 400 : Unable to Process JSON data
My problem is I need to accept data where we cannot expect the incoming JSON keys , so I cannot write get and set methods for all the keys, So how can I accept all the JSON objects in the body of the POST Method.
Any Suggestion could help me ?
You can mark your Class with
#JsonIgnoreProperties(ignoreUnknown = true)
This will Map only existing fields in the class and ignores remaining.
As you have unknown key/values in your json data, I suggest to use HashMap for requestbody which will server what you are wanting(i.e. no getter,setter and accept unknown values) like this:
#POST
#Path("/post")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public postData GiveData(#RequestBody HashMap<String, Object> dataHashMap) {
//access the values by their keynames. You know how to get keySet of hashMap and
// iterate over them(I guess)
return output;
}
Hope this will work.

Using Jackson to implement toString() without annotations

I want to use Jackson to implement toString() to return the JSON representation of an object, but I do not want to use any Jackson annotation in my code.
I tried an implementation along the lines of:
public String toString()
{
Map<String,Object> ordered = ImmutableMap.<String, Object>builder().
put("createdAt", createdAt.toString()).
put("address", address.toString()).
build();
ObjectMapper om = new ObjectMapper();
om.enable(SerializationFeature.INDENT_OUTPUT);
try
{
return om.writeValueAsString(object);
}
catch (JsonProcessingException e)
{
// Unexpected
throw new AssertionError(e);
}
}
This works well for simple fields but if "address" has its own fields then instead of getting this:
{
"address" : {
"value" : "AZ4RPBb1kSkH4RNewi4NXNkBu7BX9DmecJ",
"tag" : null
}
I get this output instead:
{
"address" : "{\n\"value\" : \"AZ4RPBb1kSkH4RNewi4NXNkBu7BX9DmecJ\",\n \"tag\" : null"
}
In other words, the address value is being treated like a String as opposed to a JsonNode.
To clarify:
On the one hand, I want to control how simple class fields are converted to String. I don't want to use Jackson's built-in converter.
On the other hand, for complex fields, returning a String value to Jackson leads to the wrong behavior.
I believe that I could solve this problem by adding a public toJson() method to all my classes. That method would return a Map<String, JsonNode>, where the value is a string node for simple fields and the output of toJson() for complex fields. Unfortunately, this would pollute my public API with implementation details.
How can I achieve the desired behavior without polluting the class's public API?
UPDATE: I just saw an interesting answer at https://stackoverflow.com/a/9599585/14731 ... Perhaps I could convert the String value of complex fields back to JsonNode before passing them on to Jackson.
I think you should implement two methods in each class - one to dump data, second to build JSON out of raw data structure. You need to separate this, otherwise you will nest it deeper and deeper every time you encapsulate nested toString() calls.
An example:
class Address {
private BigDecimal yourField;
/* …cut… */
public Map<String, Object> toMap() {
Map<String, Object> raw = new HashMap<>();
raw.put("yourField", this.yourField.toPlainString());
/* more fields */
return raw;
}
#Override
public String toString() {
// add JSON processing exception handling, dropped for readability
return new ObjectMapper().writeValueAsString(this.toMap());
}
}
class Employee {
private Address address;
/* …cut… */
public Map<String, Object> toMap() {
Map<String, Object> raw = new HashMap<>();
raw.put("address", this.address.toMap());
/* more fields */
return raw;
}
#Override
public String toString() {
// add JSON processing exception handling, dropped for readability
return new ObjectMapper().writeValueAsString(this.toMap());
}
}

JAX-RS via Jersey -> No Content Returned In HTTP Body (Method Completes Without Errors)

I am using JAX-RS via Jersey and I have hit a "bump in the road". I have a method that is supposed to return a JSON object following an HTTP POST. It does execute successfully, but does not return the JSON Object (Unless I do a work around). I am hoping someone can tell me why this does not work as I expect it to. See the following code:
#Path("chatroom")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public class ChatroomResource {
ChatroomService service = new ChatroomService();
//this works properly and returns the object as json
#GET
public List<Chatroom> getChatrooms() {
return service.getChatrooms();
}
/**********
* This works, but does not return any content in response body
*******/
#POST
public Chatroom addRoom(Chatroom room) {
return service.addChatroom(room);
/*
* This one does produce content body
* service.addChatroom(room);
* return room;
*/
}
}
The following Method is in the service object:
public Chatroom addChatroom(Chatroom room) {
return Cache.getChatrooms().put(room.getRoomName(), room);
}
What might be wrong and how to fix it
Based on the superficial details you've provided, I believe the following instruction is returning null:
return Cache.getChatrooms().put(room.getRoomName(), room);
In the put(String, Chatroom) method, I guess you are adding the Chatroom instance to the cache, but you are returning null instead of the Chatroom instance.
The following should work:
public Chatroom addChatroom(Chatroom room) {
Cache.getChatrooms().put(room.getRoomName(), room);
return room;
}
Update 1
As you mentioned in the comments, you are using a Hashtable to implement your cache.
Be aware the put(K, V) method returns the previous value of the specified key in the hashtable, or null if it did not have one. For more details, consider reading the documentation.
Update 2
Have you ever consider using a HashMap instead of a Hashtable?
If synchronization becomes an issue, you might be interested in a ConcurrentHashMap.

Hibernate validation with strong typing in Jersey with Jackson

I am implementing a REST API using Jersey. I want to validate all of the inputs to my service (query params, path params, DTOs) and am looking into some options - one that looks like it does the job is Jersey Bean Validation. I also want to have everything in the service strongly typed - for example, instead of using String to represent all of the bits of data, where you'd have a function like this:
public Order getOrder(String customerId);
Instead define types for each bit of data (the benefit of this is to let the compiler catch incorrect data being passed to functions, being able to obfuscate the underlying value in the toString method for logging, knowing that the value is definitely valid if you have an instance and so on), so you end up with functions like this:
public Order getOrder(CustomerId customerId);
And types like this:
public class CustomerId {
private final String value;
public CustomerId(String value) {
this.value = validate(value);
}
public String getValue() {
return value;
}
private String validate(String value) {
// Do some validation here
}
}
The Jersey Bean Validation examples do not use strong types like above. For example:
#Path("/")
class MyResourceClass {
#POST
#Consumes("application/x-www-form-urlencoded")
public void registerUser(
#Pattern(regexp="[a-zA-Z -]{1,50}") #FormParam("name") String name) {
...
}
}
The build in validation is nice in that you get some features for free:
400 bad request exception returned on any validation error
Optionally include the validation error in the response
None of the code in your function gets executed if validation fails
However, there are a few problems:
You have to remember to include the annotations everywhere the data
can be input to your system, so it's hard to apply consistently
You may end up with different definitions of what is valid for a type
You don't get the strong typing benefits mentioned above
Does anyone know of a way to get all of these benefits. I tried defining a type like this:
public class CustomerId {
private final String value;
public CustomerId(String value) {
this.value = validate(value);
}
public String getValue() {
return value;
}
private String validate(String value) {
if (!Pattern.matches("[a-zA-Z -]{1,50}", value)) {
throw new ConstraintViolationException(new HashSet<ConstraintViolation<?>>());
}
return value;
}
}
But it seems the exception doesn't get handled the same way by Jersey, and the response code you get if the validation fails is 404 instead of 400.
Does anyone know of a way to get the best of both worlds?
This is from the spec, in regards to how errors are handle when constructing #XxxParams
... if the [..] is annotated with #MatrixParam, #QueryParam or #PathParam then an implementation MUST generate an instance of NotFoundException (404 status) that wraps the thrown exception and no entity; if the field or property is annotated with #HeaderParam or #CookieParam then an implementation MUST generate an instance of
BadRequestException (400 status) that wraps the thrown exception and no entity.
Though not listed here, #FormParam falls under the 400 bracket.
"Does anyone know of a way to get the best of both worlds?"
We can override this behavior by throwing a WebApplicationException. We could then create an ExceptionMapper for the exception, and then just delegate to the ExceptionMapper that normally handles ConstraintViolationException. I couldn't find any clear detail on this behavior. I mean you would expect that the ExceptionMapper should get called anyway, but it doesn't if it is isn't an instance of WebApplicationException. So you can make your exception extend WebApplicationException.
public static class MyException extends WebApplicationException {
private final ConstraintViolationException cve;
public MyException(ConstraintViolationException cve) {
this.cve = cve;
}
public ConstraintViolationException getConstraintViolationException() {
return cve;
}
}
Then create an ExceptionMapper for it. In the mapper, we simply delegate to the original mapper that handles ConstraintViolationException
public static class MyExceptionMapper implements ExceptionMapper<MyException> {
#Context
private Providers providers;
#Override
public Response toResponse(MyException exception) {
ExceptionMapper<ValidationException> mapper
= providers.getExceptionMapper(ValidationException.class);
return mapper.toResponse(exception.getConstraintViolationException());
}
}
Then you can just throw MyException. If you don't care for an error response body, and all you want is a 400 status, you can forget everything above and simply throw a BadRequestException. Or if you don't care for the response entity that the ConstraintViolationException mapper sends out, you can create your own response in the MyExceptionMapper, or create a Response inside the CustomerId class and pass it the BadRequestException constructor. So you have some options.
A headache from this approach I could see is that you need to create your own ConstraintViolation. That can get old really quick.
The other approach I could see is to use #BeanParam and #Valid
public static class CustomerId {
#FormParam("cust")
#Pattern(regexp="[a-zA-Z -]{1,50}")
private String value;
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
#POST
#Path("form")
#Consumes("application/x-www-form-urlencoded")
public String postForm(#BeanParam #Valid CustomerId custId) {
The problem with this approach is that your bean is now stuck with #FormParam and is not reusable with other #XxxParams.
So you have some trade-offs. Hope this gives you some good information to work with.
UPDATE
Oh and the last option I can think of, is similar to second one above, but you aren't tied to the #XxxParam in the bean
public static class CustomerId {
//#FormParam("cust")
#javax.validation.constraints.Pattern(regexp="[a-zA-Z -]{1,50}")
private String value;
public CustomerId(String value) {
//this.value = validate(value);
this.value = value;
}
...
}
#POST
#Path("form")
#Consumes("application/x-www-form-urlencoded")
public String postForm(#FormParam("cust") #Valid CustomerId custId) {
Think the last option might be the way to go, but you still need to remember to always annotate with #Valid, which sounds like something you were trying to avoid.

Spring JSON serialization, Gson deserialization

I'm currently having an issue with the deserialization of certain inner-objects, in spring, I initialize all of my objects before outputting them using #ResponseBody.
As an example, this is a response:
[{id:1, location:{id:1, ... extra location data}},
{id:2, location:1}
]
Now, GSON throws an error as it is not able to understand that location:1 refers to the location object already deserialized in the previous object.
Deserialization is done in the following method:
#Override
public void handleReader(Reader reader) {
try {
String json = readerToString(reader);
T object = getGson().fromJson(json, returnType);
handleObject(object);
} catch (Exception e) {
Sentry.captureException(e);
}
}
As an example, this is called through a regular generic class, I'd use the type Event[] as the T generic in order to return an array.
How can I either fix this using Gson or make spring output the full data every time? Ideally I'd like to fix with Gson as it would allow for seriously reduced bandwidth but I'm not too fussed at this point.
My Spring returning method is as follows:
#Override
public List<T> list() {
return service.findAll();
}
with the initialization like so:
#Override
#Transactional
public List<Event> findAll() {
List<Event> list = eventRepository.findByArchivedFalse();
for (Event event : list) {
this.initialize(event);
}
return list;
}
#Override
public Event initialize(Event obj) {
Hibernate.initialize(obj.getLocation());
Hibernate.initialize(obj.getLocation().get... inner data here);
return obj;
}
I imagine this is going to require a real structure review but, if I can help it, I'd like to keep the structure roughly the same.
You're going to have to write a custom deserializer, if you're not willing to change the JSon. However, changing the JSon is exactly what I would recommend.
Option 1: Changing the JSon
I think the right thing to do is to have two separate messages, e.g.
{
"uniqueLocations":
[
{"id":1, ... extra location details} ,
],
"locationMap":
[
{"id":1,"location":1},
{"id":2,"location":1}
... etc.
]
}
This is clearer; this separates your json so that you always have the same types of data in the same places.
Option 2: Making Gson able to do more complicated deserializations
However, if you're not willing to do that, you could write a custom deserializer. The most straightforward way to do that, extending TypeAdapter, only uses specific, concrete classes, not parameterized types. However, if you want to use a parameterized type, you must use a TypeAdapterFactory.
You can read more about how to do this here: How do I implement TypeAdapterFactory in Gson?

Categories