Spring #RequestBody not mapping to custom Object - json

I have this custom Object called Example, that have a org.json.JSONObject as a mongo query.
public class ExampleObject {
private JSONObject query;
private String[] projections;
private Number limit;
// Constructors, getters and setters
}
I'm using a REST controller like this:
#RestController
#RequestMapping("/example")
public class ExampleRestController {
#RequestMapping(value = "/search", method = RequestMethod.POST)
#ResponseBody
public String example(#RequestBody ExampleObject example) {
return "This is an example";
}
And then, I do with Postman the following request:
POST to http://localhost:8080/example/search
with body as follows (I have checked the validity of the JSON with http://jsonlint.com):
{
"query":{
"field1":"value1",
"field2":"value2"
},
"projections":["field3, field4"],
"limit":3
}
The result is: projections and limit on object "example" have setted correctly but query is an empty JSONObject (not null). If I don't send the field query, the JSONObject variable on the object "example" is null.
I don't understand why the field query is not setted fine. I want to Spring maps the #RequestBody json to an ExampleObject.

Spring (particularly Jackson) does not know how to deserialize JSONObject. You can use com.fasterxml.jackson.core.JsonNode instead.

Related

Spring MVC preparing JSON but loading a page instead

I have a controller to send back a JSON Payload
#RequestMapping(value = "/MerchantMonitoringAPI", method = RequestMethod.GET,produces = "application/json")
public String MerchantMonitoring() {
ApplicationContext context =
new ClassPathXmlApplicationContext("Spring-Module.xml");
TopMerchantsDAO topMerchantsDAO = (TopMerchantsDAO) context.getBean("topMerchantsDAO");
TopMerchants topMerchants = topMerchantsDAO.retrieveMerchantList();
for(String temp:topMerchants.getMerchantList())
{
System.out.println(temp);
}
Gson gson = new Gson();
Type type = new TypeToken<TopMerchants>() {}.getType();
String jsonPayload = gson.toJson(topMerchants, type);
System.out.println(jsonPayload);
return jsonPayload;
}
It is trying to redirect me to a view with the page name as the JSON (localhost:8080/{"merchantList":["Apple","Google"]}.jsp)
How to stop this and return the JSON payload ??
Add #RestController on top of the #RequestMapping
#RestController
#RequestMapping(value = "/MerchantMonitoringAPI", method =
RequestMethod.GET,produces = "application/json")
public String MerchantMonitoring() {...}
Because the method is now annotated with #RestController, the objects returned from this methods will go through message conversion to produce a json resource representation for the client.
If you want to have several methods for returning JSON and page in the same class, you can still annotate your class with #Controller, and the method for JSON annotated with #ResponseBody
If you will annotated class with #RestController - all methods inside class will be working like #ResponseBody and class will be like #Controller annotated. Of course, this is a better approach (to not include page and JSON returning methods in one Controller).
Notice! You can use #RestController only for class (instead of #Controller), not for methods.
If you will open source of this annotation, you will see among other things the next:
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Controller
#ResponseBody
public #interface RestController {
String value() default "";
}
and ElementType.TYPE has comment:
Class, interface (including annotation type), or enum declaration

How do I get the JSON in a response body with Spring annotaion

I have a standard Spring 4 MVC application. I have REST endpoints that take the ResponseBody json and maps to my Java objects. This is working great.
But now I have a need to get the raw JSON, as I do not have a Java object to map it to. My endpoint looks like this:
#RequestMapping(value="", method = RequestMethod.POST)
#ResponseBody
public Object createObject(#RequestBody JsonObject objectJson) {
When I POST json to this endpoint I get an empty JSON string. The objectJson is not NULL, but when I debug like this:
System.out.println(objectJson.toString());
I get: {}
when I change the method signature to:
public Object createObject(#RequestBody String objectJson) {
I get a 400 "The request sent by the client was syntactically incorrect"
How do I get the JSON being sent in, either as a String that I can parse manually, or the JsonObject and I can use?
In order to receive a JSON object using #RequestBody, you will need to define a POJO class. Suppose your raw JSON looks like
{"id":"123", "name":"John"}
The POJO will look like
public class User {
private String id;
private String name;
...
// setters and getters
}
Your Controller method will be
#RequestMapping(value="", method = RequestMethod.POST)
#ResponseBody
public Object createObject(#RequestBody User user) {
String id = user.getId();
String name = user.getName();
...
}

Spring boot / Jackson deserializes JSON of wrong type

I'm a litte bit lost, I have to admit. I wrote a Spring Boot (1.3M2) application that receives a JSON object which it needs to store in a database:
#RequestMapping(value = "/fav", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity<String> setFavorite(#RequestBody List<Favorite> favorites) {
...
internally this method passes the JSON to another method which stores it line by line in a database:
jdbcTemplate.batchUpdate(INSERT_FAVORITE, new BatchPreparedStatementSetter() {
#Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
Favorit fav = favorites.get(i);
ps.setString( ...
}
#Override
public int getBatchSize() {
int size = favorites.size();
return size;
}
When I POST a JSON to the controller which does not match the structure of my Favorite-object I only see null values in my database. Obviously Jackson tries its best to convert my JSON into a Java object but fails and sets all values of the object it finds no value for to null.
Then this list of sort of empty objects is written to the database.
I use curl to POST the values
curl -vX POST https://localhost/fav -d #incorrectype.json
This can't be the source of error because it works with a favorite.json. How can I have my controller / Jackson detect if I use a JSON that does not match ?
One solution is to use annotations from javax.validation, and instead of accepting a List in the controller signature, use a custom wrapper along the lines of this (getters/setters omitted):
public class FavoriteList {
#Valid
#NotNull
#Size(min = 1)
private List<Favorite> favorites;
}
then for the Favorite class add the validation as needed, e. g.:
public class Favorite {
#NotNull
private String id;
}
with these changes in place, modify the controller method signature along these lines:
public ResponseEntity<String> setFavorite(#Valid #RequestBody FavoriteList favoritesList) {
This way, input failing validation will throw exceptions before anything in the controller method is executed.

JAX-RS - JSON List to Object with JaxB

I am using JAX-RS (CXF) with JaxB and Jackson to provide a REST-API.
Unfortunately, none of the found results helps me with following (simple) problem:
I implemented following method:
#POST
#Path(ApiStatics.ARMY_CREATE_ARMY)
public com.empires.web.dto.Army createArmy(#FormParam("locationid") long locationId, #FormParam("name") String name, #FormParam("troops") ArmyTroops troops) {
and here are is my model class:
#XmlRootElement
#XmlSeeAlso(ArmyTroop.class)
public class ArmyTroops {
public ArmyTroops() {
}
public ArmyTroops(List<ArmyTroop> troops) {
this.troops = troops;
}
#XmlElement(name = "troops")
private List<ArmyTroop> troops = new ArrayList<ArmyTroop>();
public List<ArmyTroop> getTroops() {
return troops;
}
public void setTroops(List<ArmyTroop> troops) {
this.troops = troops;
}
}
ArmyTroop
#XmlRootElement(name = "troops")
public class ArmyTroop {
#XmlElement
private long troopId;
#XmlElement
private String amount;
public long getTroopId() {
return troopId;
}
public void setTroopId(long troopId) {
this.troopId = troopId;
}
public String getAmount() {
return amount;
}
public void setAmount(String amount) {
this.amount = amount;
}
}
My json that i send looks like this:
locationid 1
name asdasd
troops {"troops":[{"troopId":4,"amount":"5"},{"troopId":6,"amount":"5"}]}
Unfortunately, the object gets not transformed. Instead I receive this error:
InjectionUtils #reportServerError - Parameter Class com.empires.web.dto.in.ArmyTroops has no constructor with single String parameter, static valueOf(String) or fromString(String) methods
If I provide the constructor with a single string parameter, I get passed the whole json string for "troops" as mentioned above.
Any ideas why JaxB does not work at this point?
You are passing all your parameters with #Form annotation.
But the Form part of the http message must be an xml data structure.
Your 3 parameters don't have a main xml datastructure so it wont work.
In short, form params are send as body.
Cxf use the MultivaluedMap to send params (cxf have an xml model for this structure).
As you can see it is not fit for parameters that can't be trivally serialized.
Here me solution would be to drop the #FormParam to avoid the problem:
1) Use #PathParam #CookieParam to send yours first 2 parameters, and the 'no tag' (body) only for the army compositions.
2) Define an uber object that take all parameters and can be serialized as xml datastructure and use the 'no tag' (body) sending.
3) Use soap, with cxf it is really easy to gets both Rest and Soap.

Parse unnamed mappings in JSON using Jackson

I have some JSON in the following format that I'm trying to parse with Jackson -
"response":{
"response_inner":{
"a":{"field1":2,"field2":0,"field3":5,"field4":0,"field5":[{"field5_1":"b","field5_2":1},{"field5_1":"c","field5_2":1}]},
"d":{"field1":2,"field2":6,"field3":11,"field4":0,"field5":[{"field5_1":"c","field5_2":1},{"field5_1":"b","field5_2":1}]},
"response_inner_bool":false
}
}
Here "a", "b" etc. are some Strings that can change in each response.
I've created a Java object to represent the 'response_inner' (let's call it ResponseInner) and another to represent the object containing the field?s (let's call this one FieldInfo) but I'm not sure how to parse this using the #JsonCreator and #JsonProperty annotations - ResponseInner objects can contain any number of String -> FieldInfo mappings.
I tried parsing it like this -
public class Response {
private ResponseInner responseInner;
#JsonCreator
public Response(#JsonProperty("response_inner") ResponseInner responseInner) {
this.reponseInner = responseInner;
}
}
public class ResponseInner {
private Map<String, FieldInfo> stringToFieldInfoMap;
private boolean responseInnerBool;
#JsonCreator
public ResponseInner(Map<String, FieldInfo> stringToFieldInfoMap, #JsonProperty("response_inner_bool") boolean responseInnerBool ) {
this.stringToFieldInfoMap = stringToFieldInfoMap;
this.responseInnerBool = responseInnerBool;
}
}
But it complains that Argument #0 of constructor has no property name annotation; must have name when multiple-paramater constructor annotated as Creator. Any suggestions for how to get around this?
You don't seem to be using the stringToFieldInfoMap within ResponseInner anyway. Why do you need to pass it as parameter?
If you do need it in that class, you can simply set it via a setter rather than passing it to constructor.
Alternatively, you could perhaps utilize a third class which deals with that actual mapping of the response, which consumes the Response object (which would in turn consume the ResponseInner object which has had the Map removed from it). This would actually allow you to decouple the mapping logic from the response logic perhaps.
public class MappedResponse {
private Map<String, FieldInfo> stringToFieldInfoMap;
private Response response;
public MappedResponse(Map<String, FieldInfo> stringToFieldInfoMap, Response response) {
this.stringToFieldInfoMap = stringToFieldInfoMap;
this.response = response;
}
}