Extract and unmarshal JSON payload from response - json

I'm trying to write Citrus tests for a RESTful endpoint producing and consuming application/json content, and I'm not sure how to get my responses unmarshalled to a Java POJO (using Jackson or whatever (un)marshaller Citrus supports).
E.g. in REST-assured, I can simply write
UploadResponse response = when().post("/file").as(UploadResponse.class);
Is there an equivalent in Citrus?
I can only find examples using validate() or extractFromPayload(), which don't really cover my use case, since I don't want to operate on scalar members but embed the entire response object in the request object for the subsequent test step.

You can do something like this:
http().server(testServer)
.post("/file")
.validationCallback(new JsonMappingValidationCallback<UploadResponse>(UploadResponse.class) {
#Override
public void validate(UploadResponse payload, Map<String, Object> headers, TestContext context) {
// do something with payload object
}
});
The JsonMappingValidationCallback automatically searches for a JSON ObjectMapper in Spring bean application context. You can also provide an ObjectMapper instance as constructor arg to the JsonMappingValidationCallback.

Related

Jersey/Genson: Unmarschalling single object array

Similar to Jersey: Json array with 1 element is serialized as object BUT on the client side. E.g. I recieve a JSON object where a field is an array regulary, but in case there is only one element, it is a single object.
{"fileInfo":[{"fileName":"weather.arff","id":"10"},"fileName":"supermarket.arff","id":"11"}]}
versus
{"fileInfo":{"fileName":"weather.arff","id":"10"}}
I'm parsing/unmarshalling the JSON using Jersey/Genson. Of course, if the JSON doesnt match the target class I recieve an error (such as expected [ but read '{' )
I've read a lot about this bug and how to avoid when creating JSON objects on the SERVER side, but I found nothing about how to handle this issus when dealing on the CLIENT side.
As always, I prefere the most codeless possibility if there are several solutions...
BTW: Moxy works but it does not marshal native Object-type objects which is another requirement...
Update
Starting with Genson 1.3 release you can achieve it by enabling permissiveParsing:
Genson genson = new GensonBuilder().usePermissiveParsing(true).create();
Answer
Uh, do you know what library produces this on server side? I am curious to see who is responsible for all those badly structured jsons out there...
It is not yet supported in Genson. Originally because IMO people should not produce such dynamic json. Anyway, I opened an issue - this can be easily done, you can expect it to be present in the release coming next week.
Otherwise here is a way to achieve it without breaking the existing mechanisms.
You need to register a Factory that will use Gensons collections factory to create an instance of its standard collection converter. Then you will wrap this converter in another one that will handle the object to array logic. Here is the code (not codeless..., but if you wait a bit you won't have to code :)).
import com.owlike.genson.convert.DefaultConverters.CollectionConverterFactory;
import com.owlike.genson.convert.DefaultConverters.CollectionConverterFactory;
class SingleObjectAsCollectionFactory implements Factory<Converter<Collection>> {
// get the default factory
Factory<Converter<Collection<?>>> defaultFactory = CollectionConverterFactory.instance;
#Override
public Converter<Collection> create(Type type, Genson genson) {
// obtain an instance of the correct default converter for this type
final CollectionConverter defaultConverter = (CollectionConverter) defaultFactory.create(type, genson);
// wrap it in your own converter
return new Converter<Collection>() {
#Override
public void serialize(Collection object, ObjectWriter writer, Context ctx) throws Exception {
defaultConverter.serialize(object, writer, ctx);
}
#Override
public Collection deserialize(ObjectReader reader, Context ctx) throws Exception {
if (reader.getValueType() == ValueType.OBJECT) {
Object object = defaultConverter.getElementConverter().deserialize(reader, ctx);
Collection result = defaultConverter.create();
result.add(object);
return result;
} else return defaultConverter.deserialize( reader, ctx );
}
};
}
}
And then register it
Genson genson = new GensonBuilder()
.withConverterFactory(new SingleObjectAsCollectionFactory())
.create();

Spring MVC : post request and json object with array : bad request

I'm trying to retrieve parameters from a http POST request with Spring MVC.
The request contains the following json object (content-type : application/json), which itself contains an array of customObjects :
{
"globalId":"338",
"lines":[
{
"id": "someId",
"lib":"blabla",
...
}
]
}
Here's the code I'm trying to use :
#RequestMapping(method = RequestMethod.POST, value = "/valider")
#ResponseBody
public void valider(final HttpServletRequest request, #RequestParam("globalId") final String globalId, #RequestParam("lines") final MyCustomObject[] lines) {
All I'm getting is a "bad request" error (http 400).
Is it possible to separately retrieve the two parameters "globalId" and "lines" ? Or since they are in the same json object, it has to be treated has a single parameter ? How do you proceed when you have more than one parameter in a Post request ?
I think you're looking for something like `#RequestBody. Create a class to represent your JSON data. In your case, this class will contain two member variables - globalId as a string and lines as an array of the object it represents. Then in your controller method, you will use the #RequestBody annotation on this class type so that Spring will be able to convert the JSON into object. Check the examples below.
http://www.leveluplunch.com/java/tutorials/014-post-json-to-spring-rest-webservice/
JQuery, Spring MVC #RequestBody and JSON - making it work together
http://www.techzoo.org/spring-framework/spring-mvc-requestbody-json-example.html
create model object to map your Json data
class DLibrary{
int id;
String lib;
//getters/setters
}
class GLibrary{
int globalId;
List<DLibrary> lines;
//getters/setters
}
Replace your controller code with below
#RequestMapping(method = RequestMethod.POST, value = "/valider")
#ResponseBody
public void valider(#RequestBody GLibrary gLibrary) {
#RequestBody annotation will map Json to Java Object implicitly.
To achieve this spring must require jackson-core and jackson-mapper library included in your application and your Java class should have getter and setters i.e it must follow bean standards.
Indeed, I have to use #RequestBody to get the JSON object.
Quick summary, depending on how the parameters are passed in the http POST body request :
one JSON object (Content-Type: application/json), use #RequestBody to map the json object to a java object
multiple parameters (Content-Type: application/x-www-form-urlencoded), use #RequestParam for each parameter

Is it possible to get jersey to read json variables our of a request body without using a bean?

In jersey a Java bean can be auto-deserialized from within a request body but what if I want to read a parameter without creating a special type. Is it possible to do this using annotations.
My current code is:
public class RequestData {
String param;
}
...
public Response readData(RequestData data) {
data.getParam();
...
}
I want it to be something like:
public Response readData(#RequestParam("param") String param) {
...
}
If its not already clear the input JSON is:
{
"param":"some value"
}
The type of your input JSON is Map<String, String> so if you want to have undifferentiated input you could use that as your request parameter and read the values that you require.
Note that #RequestParam looks at the request parameters and not the body, so it's a different beast.
You do this by letting Jersey pass you String as is (as per annotations), and then data-bind it using Jackson ObjectMapper (thing Jersey uses internally for JSON binding):
Map<String,Object> map = objectMapper.readValue(param, Map.class);
to get access to ObjectMapper, you can use JAX-RS injection annotation (#Context I think?) in the resource class:
#Context
private ObjectMapper objectMapper;

How to get a JSON object and map it into object in spring 3.0 controller using POST method?

I want to add request mapping my controller which gets a Json of Array of an object.
The JSON will be sent from javascript using post.
Thanks,
Kfir
You can do this using Spring's #RequestBody annotation, e.g.
#ResponseBody
public Map<String, Object> handleJsonPost(#RequestBody Map<String, Object> requestJson) {
...
return responseJson;
}
This will also send the return value back as JSON.
You'll also need to include the Jackson library in your application's classpath, and add
<mvc:annotation-driven/>
to your context (see docs)

Consuming JSON object in Jersey service

I've been Googling my butt off trying to find out how to do this: I have a Jersey REST service. The request that invokes the REST service contains a JSON object. My question is, from the Jersey POST method implementation, how can I get access to the JSON that is in the body of the HTTP request?
Any tips, tricks, pointers to sample code would be greatly appreciated.
Thanks...
--Steve
As already suggested, changing the #Consumes Content-Type to text/plain will work, but it doesn't seem right from an REST API point of view.
Imagine your customer having to POST JSON to your API but needing to specify the Content-Type header as text/plain. It's not clean in my opinion. In simple terms, if your API accepts JSON then the request header should specify Content-Type: application/json.
In order to accept JSON but serialize it into a String object rather than a POJO you can implement a custom MessageBodyReader. Doing it this way is just as easy, and you won't have to compromise on your API spec.
It's worth reading the docs for MessageBodyReader so you know exactly how it works. This is how I did it:
Step 1. Implement a custom MessageBodyReader
#Provider
#Consumes("application/json")
public class CustomJsonReader<T> implements MessageBodyReader<T> {
#Override
public boolean isReadable(Class<?> type, Type genericType,
Annotation[] annotations,MediaType mediaType) {
return true;
}
#Override
public T readFrom(Class<T> type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
InputStream entityStream) throws IOException, WebApplicationException {
/* Copy the input stream to String. Do this however you like.
* Here I use Commons IOUtils.
*/
StringWriter writer = new StringWriter();
IOUtils.copy(entityStream, writer, "UTF-8");
String json = writer.toString();
/* if the input stream is expected to be deserialized into a String,
* then just cast it
*/
if (String.class == genericType)
return type.cast(json);
/* Otherwise, deserialize the JSON into a POJO type.
* You can use whatever JSON library you want, here's
* a simply example using GSON.
*/
return new Gson().fromJson(json, genericType);
}
}
The basic concept above is to check if the input stream is expected to be converted to a String (specified by Type genericType). If so, then simply cast the JSON into the specified type (which will be a String). If the expected type is some sort of POJO, then use a JSON library (e.g. Jackson or GSON) to deserialize it to a POJO.
Step 2. Bind your MessageBodyReader
This depends on what framework you're using. I find that Guice and Jersey work well together. Here's how I bind my MessageBodyReader in Guice:
In my JerseyServletModule I bind the reader like so --
bind(CustomJsonReader.class).in(Scopes.SINGLETON);
The above CustomJsonReader will deserialize JSON payloads into POJOs as well as, if you simply want the raw JSON, String objects.
The benefit of doing it this way is that it will accept Content-Type: application/json. In other words, your request handler can be set to consume JSON, which seems proper:
#POST
#Path("/stuff")
#Consumes("application/json")
public void doStuff(String json) {
/* do stuff with the json string */
return;
}
Jersey supports low-level access to the parsed JSONObject using the Jettison types JSONObject and JSONArray.
<dependency>
<groupId>org.codehaus.jettison</groupId>
<artifactId>jettison</artifactId>
<version>1.3.8</version>
</dependency>
For example:
{
"A": "a value",
"B": "another value"
}
#POST
#Path("/")
#Consumes(MediaType.APPLICATION_JSON)
public void doStuff(JSONObject json) {
/* extract data values using DOM-like API */
String a = json.optString("A");
Strong b = json.optString("B");
return;
}
See the Jersey documentation for more examples.
I'm not sure how you would get at the JSON string itself, but you can certainly get at the data it contains as follows:
Define a JAXB annotated Java class (C) that has the same structure as the JSON object that is being passed on the request.
e.g. for a JSON message:
{
"A": "a value",
"B": "another value"
}
Use something like:
#XmlAccessorType(XmlAccessType.FIELD)
public class C
{
public String A;
public String B;
}
Then, you can define a method in your resource class with a parameter of type C. When Jersey invokes your method, the JAXB object will be created based on the POSTed JSON object.
#Path("/resource")
public class MyResource
{
#POST
public put(C c)
{
doSomething(c.A);
doSomethingElse(c.B);
}
}
This gives you access to the raw post.
#POST
#Path("/")
#Consumes("text/plain")
#Produces(MediaType.APPLICATION_JSON)
public String processRequset(String pData) {
// do some stuff,
return someJson;
}
Submit/POST the form/HTTP.POST with a parameter with the JSON as the value.
#QueryParam jsonString
public desolveJson(jsonString)
Some of the answers say a service function must use consumes=text/plain but my Jersey version is fine with application/json type. Jackson and Jersey version is
jackson-core=2.6.1, jersey-common=2.21.0.
#POST
#Path("/{name}/update/{code}")
#Consumes({ "application/json;charset=UTF-8" })
#Produces({ "application/json;charset=UTF-8" })
public Response doUpdate(#Context HttpServletRequest req, #PathParam("name") String name,
#PathParam("code") String code, String reqBody) {
System.out.println(reqBody);
StreamingOutput stream = new StreamingOutput() {
#Override public void write(OutputStream os) throws IOException, WebApplicationException {
..my fanzy custom json stream writer..
}
};
CacheControl cc = new CacheControl();
cc.setNoCache(true);
return Response.ok().type("application/json;charset=UTF-8")
.cacheControl(cc).entity(stream).build();
}
Client submits application/json request with a json request body. Servlet code may parse string to JSON object or save as-is to a database.
SIMPLE SOLUTION:
If you just have a simple JSON object coming to the server and you DON'T want to create a new POJO (java class) then just do this.
The JSON I am sending to the server
{
"studentId" : 1
}
The server code:
//just to show you the full name of JsonObject class
import javax.json.JsonObject;
#Path("/")
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response deleteStudent(JsonObject json) {
//Get studentId from body <-------- The relevant part
int studentId = json.getInt("studentId");
//Return something if necessery
return Response.ok().build();
}