Create JAX-RS Client Post Request with JSON String as Body - json

I am writing a REST Client for one of the Vendor REST Service. I use jersey 2.x and JSON-P, below are dependencies I add.
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-processing</artifactId>
<version>2.26</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.26</version>
</dependency>
</dependencies>
I successfully write code for GET request and received JSON output. I saved it to a file and used JSON-P to interpret and do my own logic without any issues.
But now I need to write a POST request. When I use CURL as below I am able do it. and want to implement the same using Jeresey 2.x and JSON-P.
curl -v -H "Accept:application/json" -H "Content-Type:application/json" -u user:password -X POST --databinary #./InputParameters.json https://<IP>:<PORT>/Configuration/server
InputParameters.json contents
{
"ip": "10.197.70.16",
"partNumber": 202067,
"model": "IBM P7"
}
When I tried to pass response body as String in JSON format ({"ip": "10.197.70.16", "partNumber": 202067, "model": "IBM P7"}), but didn't work. So tried as JsonObject as below still didn't work.
JsonObject jsonObj = Json.createObjectBuilder()
.add("ip", "10.197.70.16")
.add("partNumber", 202067)
.add("model", "IBM P7")
.build();
response = invocationBuilder.post(Entity.json(jsonObj));
I know core java, based on that experience I jumped into writing this program and got success with GET but not POST. I doubt I am doing something fundamentally wrong with POST.

Let's unpack what you're doing for a bit. First there's this part:
JsonObject jsonObj = Json.createObjectBuilder()
.add("ip", "10.197.70.16")
.add("partNumber", 202067)
.add("model", "IBM P7")
.build();
This creates a javax.json.JsonObject instance. JsonObject, which is part of the JSON-P Java API, is pretty much what it says: a class to represent a JSON object. A JsonObject instance contains a hierarchy of javax.json.JsonValue instances, which conform to more specific types like JsonArray, JsonString, other JsonObjects and so on. In this regard it's not unlike the classes of the DOM API for representing XML documents (let's hope Oracle keeps the API docs at that URL for a while). But JSON is fortunately a lot more straightforward than XML and DOM.
Your jsonObj instance would contain a JsonString instance with value "10.197.70.16" mapped to name "ip", a JsonNumber with value 202067 (probably represented as BigDecimal) mapped to name "partNumber" and so on.
Next your code executes this:
Entity.json(jsonObj)
javax.ws.rs.client.Entity.json(something) basically states that you want to create an entity that will provide the payload for a JAX-RS client invocation with as Content-Type application/json. In other words, the something you create it for must be transformed to a JSON representation when it's sent to the API, which should expect a JSON payload and know how to handle it. Note that Entity.json(...) has a generic type parameter. The method signature is static <T> Entity<t> json(T entity). So you're creating an instance of Entity<JsonObject> with the payload entity jsonObj.
When this is handed over to the post method of a javax.ws.rs.client.Invocation.Builder instance (the post method is actually defined in its parent interface SyncInvoker) the client implementation goes to work.
response = invocationBuilder.post(Entity.json(jsonObj));
It takes the provided Entity instance and its content (our jsonObj), checks what the desired output is of the Entity (this is application/json) and seeks a provider that can turn objects of the given type into that output. In other words, some component must be located that can be given a javax.json.JsonObject and write a representation of it as JSON to an OutputStream. The component handling this could be a javax.ws.rs.ext.MessageBodyWriter that claims it can perform this transformation and was registered to the JAX-RS runtime. Various libraries supply such providers and you can also write your own. This makes JAX-RS extensible to deal with various scenarios, handle non-standard input and output or lets you tune its behaviour. When multiple providers are capable of handling the given entity type and producing the desired output, there are rules to determine which one takes on the job. Note that this can depend on what is on your classpath. There are ways of forcing this explicitly.
The client puts together the invocation through its configuration, using the proper URL, query parameters, HTTP method, headers and so on. The payload is created by writing the entity to an OutputStream in the required format. In your example this results in a POST to the server. When the invocation has been completed you receive a javax.ws.rs.core.Response that you can use to determine the HTTP result code and retrieve a response payload, if any. The readEntity(Class<T> entityType) method of Response works like the reverse of turning an Entity into a payload. It searches for a MessageBodyReader that can interpret the response stream according to the value returned from response.getMediaType() and can create an instance of Class entityType from it.
So with all of that explained, what exactly is going wrong in your approach? Well, the issue is that the default implementations available to your JAX-RS runtime probably don't have a writer specifically for an input of type JsonObject and with expected output application/json. It may seem very logical if the server expects JSON, that you should be able to supply a JsonObject as payload. But if the JAX-RS implementation can't find something to handle that class, then at best it can just use some default approach. In that case it may try to interpret the object as a POJO and serialize it to JSON in a default manner, which could lead to weirdness like this:
{
"valueMap": {
"ip": {
"value": "10.197.70.16"
},
"partNumber": {
"num": 202067,
"integral": TRUE
},
...
}
}
That's what a literal interpretation of the JsonObject instance could look like, depending on which implementation it uses and what is used by JAX-RS to turn it into JSON output. Of course it's possible that the object can't be serialized to JSON at all, either because no suitable MessageBodyWriter can be found or it runs into an error when creating the output.
A first solution would be a very simple one. Just turn the JsonObject into a String and simply provide that as the entity:
StringWriter stringWriter = new StringWriter();
try (JsonWriter jsonWriter = Json.createWriter(stringWriter);) {
jsonWriter.writeObject(jsonObject);
} // some error handling would be needed here
String jsonPayload = stringWriter.toString();
response = invocationBuilder.post(Entity.json(jsonPayload));
It seems you had already tried that. A possible problem with this is that the MessageBodyWriter that gets used needs to just output the String's bytes in a suitable encoding (probably UTF-8) when presented with a String as output and application/json as the required content type. Some may not do that. You could try Entity.entity(jsonPayload, MediaType.TEXT_PLAIN_TYPE.withCharset("UTF-8")) but then the server might reject the call.
Other possible solutions are
Writing your own MessageBodyWriter for String objects with an #javax.ws.rs.Produces(MediaType.APPLICATION_JSON) annotation on it.
Better yet, writing such a class that accepts JsonObject instances.
Creating POJO classes for your JSON structure and letting those get used for generating JSON from instances or deserializing JSON responses to instances.
Finding an extension library that contains suitable providers for dealing with javax.json classes.
The addition of the com.owlike:genson dependency to your project is exactly the application of that last suggestion. The Genson library provides conversions between Java objects and JSON in both directions, as well as data binding. Part of its code base is dedicated to JAX-RS providers, among which a class suitable for accepting JsonObject as input.

Issue is resolved after adding below dependency. At this point I am not sure on what does it do.
Thanks to Swamy (TCS) for his support to resolve this.
<dependency>
<groupId>com.owlike</groupId>
<artifactId>genson</artifactId>
<version>1.4</version>
</dependency>

Example using genson
String serialize = new Genson().serialize(
Json.createObjectBuilder()
.add("ip", "10.197.70.16")
.add("partNumber", 202067)
.add("model", "IBM P7")
.build()
);
response = invocationBuilder.post(Entity.json(serialize));

Related

JAX-RS JSON Binding deserialization error when using multiple parameter in POST

I'm trying to develop a Jax-RS POST resource, reported here below:
#Path("testJson")
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response testJson(Float firstValue, Float secondValue, String thirdValue) {
LOG.info(" firstValue: " + firstValue);
LOG.info(" secondValue: " + secondValue);
LOG.info(" thirdValue: " + thirdValue);
return Response.ok().build();
}
However, i get the following error:
RESTEASY002305: Failed executing POST /aliments/testJson: org.jboss.resteasy.spi.ReaderException: javax.ws.rs.ProcessingException: RESTEASY008200: JSON Binding deserialization error
Searching around, I understood that for a POST method that accepts a JSON, you need to give to it only one parameter, which is in fact the entire JSON message.
My questions are:
Why can't I put two or more parameters? Is that because the Json represents the body part of the message and I can have only one body? Can you explain it better to me please?
I can create a DTO that contains my parameters and use this DTO as one and only parameter for my POST method, but is this the best practice? Doing so, I will have a DTO for each POST method, which actually acts as a Wrapper.
Is there anything I'm missing?
Thank you a lot for your time,
Have a nice day.
Why can't I put two or more parameters? Is that because the Json represents the body part of the message and I can have only one body? Can you explain it better to me please?
JAX-RS allows for one "entity" parameter. This parameter represents the entire request entity. It is determined to be the entity parameter by not having any annotations1. If you want the raw entity, you can use an InputStream parameter. If you want a POJO, you can do so. How the conversion works is with the use of MessageBodyReaders. The reader will be chosen based on the Content-Type header and the parameter type. The framwork comes with some standard readers for easiliy convertable types. For example String, InputStream, byte[]. The reader will get passed the entity stream and it will need to convert the stream to the parameter type. You can read more about "Entity Providers" here.
If you want to use a common media type like JSON, there are libraries that handle JSON/POJO conversion, and from that library, a reader can be made. For JSON, a common librabry is Jackson, and there is a Jackson MessageBodyReader that is provided by the Jackson team.
I can create a DTO that contains my parameters and use this DTO as one and only parameter for my POST method, but is this the best practice? Doing so, I will have a DTO for each POST method, which actually acts as a Wrapper.
Yes, this is very common practice. Get used to it with these type of frameworks.
Is there anything I'm missing?
I don't know, you tell me.
1. Some special annotation are allowed like #Valid for bean validation.

How to convert Model to JSON

When I naively use Jackson to convert to JSON i receive this exception:
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.apache.cayenne.access.DefaultDataRowStoreFactory and no properties discovered to create BeanSerializer
Edit: I'd like to do something like this:
ObjectContext context = cayenneRuntime.newContext();
List<User> users = ObjectSelect.query(User.class).select(context);
JsonObject json = Json.mapper.convertValue(obj, Map.class)
Are there any existing solutions? Thanks
Considering that in a general case Cayenne gives you not just objects, but a virtual graph of objects, serialization to JSON becomes a more quirky topic than it initially appears.
The short answer: you'd have manually build JSON for whatever subgraph of your object graph.
While not a direct answer, it may be worth mentioning that Agrest framework (ex. LinkRest) supports rule-based serialization of Cayenne object graphs to JSON. But it is not a standalone component. I.e. it will only work if you use it for your REST services.

Binding JSON request data to command objects

In a Grails 2.5.X app, I can directly bind request data to a command object field of type Map like so:
class MyController {
def myAction(Command command) {
Map requestData = command.data
}
}
class Command {
Map data
}
It seems that internally Grails uses Gson for the parsing of the JSON data. If for example, the request data is {"page": 6} the corresponding Map will be
[page: new LazilyParsedNumber(6)]
i.e. the value stored in the Map is an instance of com.google.gson.internal.LazilyParsedNumber.
This is problematic for me. I would prefer if the Map were equivalent to that which would be created by:
new groovy.json.JsonSlurper().parseText('{"page": 6}')
which is:
[page: new Integer(6)]
I've looked into the various options for customising the databinding, and none of them hook into the pipeline sufficiently early. In other words, no matter which of the options I choose, the request data has already been processed by Gson.
Is it possible to replace Gson with JsonSlurper as the default parser of JSON request data?
Is it possible to replace Gson with JsonSlurper as the default parser
of JSON request data?
Yes, but it isn't something that we document or provide any particular support hooks for.
The default JSON data binding source creator is at https://github.com/grails/grails-core/blob/v2.5.6/grails-web-databinding/src/main/groovy/org/codehaus/groovy/grails/web/binding/bindingsource/JsonDataBindingSourceCreator.groovy.
An instance of that class is added to the Spring application context by the data binding plugin at https://github.com/grails/grails-core/blob/bd7cc10e17d34f20cedce979724f0e3bacd4cdb4/grails-plugin-databinding/src/main/groovy/org/codehaus/groovy/grails/plugins/databinding/DataBindingGrailsPlugin.groovy#L97.
One thing you could do is write your own class which extends AbstractRequestBodyDataBindingSourceCreator (or just implements DataBindingSourceCreator) and register an instance of that class as a bean named jsonDataBindingSourceCreator and that will replace the default one with yours. Then you are on your own to use whatever techniques you like for parsing the body of the request and creating a Map.

Efficient way of parsing Jax-ws Restful Response

I need to parse the jax-ws rest response and I tried the following two ways of parsing the response.Both works good.But I am in need to know the best efficient way of implementation.Please provide me your view.
First Approach:
Use getEntity Object and get the response as Input Stream.
Using Jackson ObjectMapper readValue() -covert the inputstream to java
object.
Using getters and setters of nested java class get the response objects member values.
Second Approach:
Use getEntity Object and get the response as Input Stream and and
convert the Input Stream to String.
Using Google Json API,convert the string to json object.
Using Json parser and get the nested objects member values.
I would say the first approach is better for two reasons:
You don't go through the intermediate process of reading the response payload into String
The setter methods that are called during Jackson deserialization may perform validation on input and throw appropriate exceptions, so you do validation during deserialization.
Maybe not a general answer to this question but another variant of what you're describing under "First approach". I would start with a generic data structure and would only introduce an extra bean if necessary. I wouldn't use String to pass structured data around.
Use jackson to convert the JSON response to a
Map<String,Object> or JsonNode.
Advantage:
You don't need to implement a specialized bean class. Even a very simple bean can become unhandy over time (if format changes or new nested structures are added to the json response, etc.). It also introduces some kind of metaphor to your code which sometimes helps but also can be misleading.
Map<String,Object> is in the JDK and offers a good interface to access data. You don't have to change any interfaces even if the JSON format changes.
You can always pass your data in form of a Map<String,Object>
Disadvantage
Data Encapsulation. The map is a very close representation of the input data and therefore offers not same level of abstraction like a bean.

List<Object> to JSON using annotations

OK, here goes, hopefully this makes sense! I have a small project based off of the appfuse web service only archetype. I have a business facade which has a DAO injected, through this DAO I request some data, simple example:
PersonManager.java
#GET
#Path("{people}")
List<Person> getPeople(#QueryParam("surname") String surname);
PersonManagerImpl.java (implements PersonManager)
public List<Person> getPeople(String surname) {
return personDao.getPeople(String surname);
}
I can make a request to invoke this method through a URL configured to point to "getPeople", however, as the DAO returns the list of people as an array list, I get the following error
Error serializing the response, please check the server logs, response class : ArrayList.
I know I can wrap this method and use Jackson Object Mapper to change the list to a string, but I didn't want another layer in my code, just to marshal JSON requests/responses.
I also don't want to change the interface to return a string, because the interface may be used later to return other data types, thus, I don't want to lock it in to only returning a string representing JSON.
My dilemma is that, I don't quite get how keeping the above interface and implementation, I can have Jackson convert the list of people to a json list of people, with annotations only!
Any help is greatly appreciated.
Please help!
Upgrading Jackson from 1.7.1 to 1.9.5 resolved this issue.