JSON parameter in spring MVC controller - java

I have
#RequestMapping(method = RequestMethod.GET)
#ResponseBody
SessionInfo register(UserProfile profileJson){
...
}
I pass profileJson this way:
http://server/url?profileJson={"email": "mymail#gmail.com"}
but my profileJson object has all null fields. What should I do to make spring parse my json?

The solution to this is so easy and simple it will practically make you laugh, but before I even get to it, let me first emphasize that no self-respecting Java developer would ever, and I mean EVER work with JSON without utilizing the Jackson high-performance JSON library.
Jackson is not only a work horse and a defacto JSON library for Java developers, but it also provides a whole suite of API calls that makes JSON integration with Java a piece of cake (you can download Jackson at http://jackson.codehaus.org/).
Now for the answer. Assuming that you have a UserProfile pojo that looks something like this:
public class UserProfile {
private String email;
// etc...
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
// more getters and setters...
}
...then your Spring MVC method to convert a GET parameter name "profileJson" with JSON value of {"email": "mymail#gmail.com"} would look like this in your controller:
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper; // this is your lifesaver right here
//.. your controller class, blah blah blah
#RequestMapping(value="/register", method = RequestMethod.GET)
public SessionInfo register(#RequestParam("profileJson") String profileJson)
throws JsonMappingException, JsonParseException, IOException {
// now simply convert your JSON string into your UserProfile POJO
// using Jackson's ObjectMapper.readValue() method, whose first
// parameter your JSON parameter as String, and the second
// parameter is the POJO class.
UserProfile profile =
new ObjectMapper().readValue(profileJson, UserProfile.class);
System.out.println(profile.getEmail());
// rest of your code goes here.
}
Bam! You're done. I would encourage you to look through the bulk of Jackson API because, as I said, it is a lifesaver. For example, are you returning JSON from your controller at all? If so, all you need to do is include JSON in your lib, and return your POJO and Jackson will AUTOMATICALLY convert it into JSON. You can't get much easier than that. Cheers! :-)

This could be done with a custom editor, that converts the JSON into a UserProfile object:
public class UserProfileEditor extends PropertyEditorSupport {
#Override
public void setAsText(String text) throws IllegalArgumentException {
ObjectMapper mapper = new ObjectMapper();
UserProfile value = null;
try {
value = new UserProfile();
JsonNode root = mapper.readTree(text);
value.setEmail(root.path("email").asText());
} catch (IOException e) {
// handle error
}
setValue(value);
}
}
This is for registering the editor in the controller class:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(UserProfile.class, new UserProfileEditor());
}
And this is how to use the editor, to unmarshall the JSONP parameter:
#RequestMapping(value = "/jsonp", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE})
#ResponseBody
SessionInfo register(#RequestParam("profileJson") UserProfile profileJson){
...
}

You can create your own Converter and let Spring use it automatically where appropriate:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
#Component
class JsonToUserProfileConverter implements Converter<String, UserProfile> {
private final ObjectMapper jsonMapper = new ObjectMapper();
public UserProfile convert(String source) {
return jsonMapper.readValue(source, UserProfile.class);
}
}
As you can see in the following controller method nothing special is needed:
#GetMapping
#ResponseBody
public SessionInfo register(#RequestParam UserProfile userProfile) {
...
}
Spring picks up the converter automatically if you're using component scanning and annotate the converter class with #Component.
Learn more about Spring Converter and type conversions in Spring MVC.

This does solve my immediate issue, but I'm still curious as to how you might pass in multiple JSON objects via an AJAX call.
The best way to do this is to have a wrapper object that contains the two (or multiple) objects you want to pass. You then construct your JSON object as an array of the two objects i.e.
[
{
"name" : "object1",
"prop1" : "foo",
"prop2" : "bar"
},
{
"name" : "object2",
"prop1" : "hello",
"prop2" : "world"
}
]
Then in your controller method you recieve the request body as a single object and extract the two contained objects. i.e:
#RequestMapping(value="/handlePost", method = RequestMethod.POST, consumes = { "application/json" })
public void doPost(#RequestBody WrapperObject wrapperObj) {
Object obj1 = wrapperObj.getObj1;
Object obj2 = wrapperObj.getObj2;
//Do what you want with the objects...
}
The wrapper object would look something like...
public class WrapperObject {
private Object obj1;
private Object obj2;
public Object getObj1() {
return obj1;
}
public void setObj1(Object obj1) {
this.obj1 = obj1;
}
public Object getObj2() {
return obj2;
}
public void setObj2(Object obj2) {
this.obj2 = obj2;
}
}

Just add #RequestBody annotation before this param

Related

How to get integer array from Java Object? [duplicate]

I have a sample RestController in Spring Boot:
#RestController
#RequestMapping("/api")
class MyRestController
{
#GetMapping(path = "/hello")
public JSONObject sayHello()
{
return new JSONObject("{'aa':'bb'}");
}
}
I am using the JSON library org.json
When I hit API /hello, I get an exception saying :
Servlet.service() for servlet [dispatcherServlet] in context with path
[] threw exception [Request processing failed; nested exception is
java.lang.IllegalArgumentException: No converter found for return
value of type: class org.json.JSONObject] with root cause
java.lang.IllegalArgumentException: No converter found for return
value of type: class org.json.JSONObject
What is the issue? Can someone explain what exactly is happening?
As you are using Spring Boot web, Jackson dependency is implicit and we do not have to define explicitly. You can check for Jackson dependency in your pom.xml in the dependency hierarchy tab if using eclipse.
And as you have annotated with #RestController there is no need to do explicit json conversion. Just return a POJO and jackson serializer will take care of converting to json. It is equivalent to using #ResponseBody when used with #Controller. Rather than placing #ResponseBody on every controller method we place #RestController instead of vanilla #Controller and #ResponseBody by default is applied on all resources in that controller. Refer this link: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-responsebody
The problem you are facing is because the returned object(JSONObject) does not have getter for certain properties. And your intention is not to serialize this JSONObject but instead to serialize a POJO. So just return the POJO.
Refer this link: https://stackoverflow.com/a/35822500/5039001
If you want to return a json serialized string then just return the string. Spring will use StringHttpMessageConverter instead of JSON converter in this case.
The reason why your current approach doesn't work is because Jackson is used by default to serialize and to deserialize objects. However, it doesn't know how to serialize the JSONObject. If you want to create a dynamic JSON structure, you can use a Map, for example:
#GetMapping
public Map<String, String> sayHello() {
HashMap<String, String> map = new HashMap<>();
map.put("key", "value");
map.put("foo", "bar");
map.put("aa", "bb");
return map;
}
This will lead to the following JSON response:
{ "key": "value", "foo": "bar", "aa": "bb" }
This is a bit limited, since it may become a bit more difficult to add child objects. Jackson has its own mechanism though, using ObjectNode and ArrayNode. To use it, you have to autowire ObjectMapper in your service/controller. Then you can use:
#GetMapping
public ObjectNode sayHello() {
ObjectNode objectNode = mapper.createObjectNode();
objectNode.put("key", "value");
objectNode.put("foo", "bar");
objectNode.put("number", 42);
return objectNode;
}
This approach allows you to add child objects, arrays, and use all various types.
You can either return a response as String as suggested by #vagaasen or you can use ResponseEntity Object provided by Spring as below. By this way you can also return Http status code which is more helpful in webservice call.
#RestController
#RequestMapping("/api")
public class MyRestController
{
#GetMapping(path = "/hello", produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> sayHello()
{
//Get data from service layer into entityList.
List<JSONObject> entities = new ArrayList<JSONObject>();
for (Entity n : entityList) {
JSONObject entity = new JSONObject();
entity.put("aa", "bb");
entities.add(entity);
}
return new ResponseEntity<Object>(entities, HttpStatus.OK);
}
}
you can also use a hashmap for this
#GetMapping
public Map<String, Object> get() {
Map<String, Object> map = new HashMap<>();
map.put("key1", "value1");
map.put("results", somePOJO);
return map;
}
More correct create DTO for API queries, for example entityDTO:
Default response OK with list of entities:
#GetMapping(produces=MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.OK)
public List<EntityDto> getAll() {
return entityService.getAllEntities();
}
But if you need return different Map parameters you can use next two examples
2. For return one parameter like map:
#GetMapping(produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> getOneParameterMap() {
return ResponseEntity.status(HttpStatus.CREATED).body(
Collections.singletonMap("key", "value"));
}
And if you need return map of some parameters(since Java 9):
#GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> getSomeParameters() {
return ResponseEntity.status(HttpStatus.OK).body(Map.of(
"key-1", "value-1",
"key-2", "value-2",
"key-3", "value-3"));
}
#RequestMapping("/api/status")
public Map doSomething()
{
return Collections.singletonMap("status", myService.doSomething());
}
PS. Works only for 1 value
If you need to return a JSON object using a String, then the following should work:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.ResponseEntity;
...
#RestController
#RequestMapping("/student")
public class StudentController {
#GetMapping
#RequestMapping("/")
public ResponseEntity<JsonNode> get() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
JsonNode json = mapper.readTree("{\"id\": \"132\", \"name\": \"Alice\"}");
return ResponseEntity.ok(json);
}
...
}
use ResponseEntity<ResponseBean>
Here you can use ResponseBean or Any java bean as you like to return your api response and it is the best practice. I have used Enum for response. it will return status code and status message of API.
#GetMapping(path = "/login")
public ResponseEntity<ServiceStatus> restApiExample(HttpServletRequest request,
HttpServletResponse response) {
String username = request.getParameter("username");
String password = request.getParameter("password");
loginService.login(username, password, request);
return new ResponseEntity<ServiceStatus>(ServiceStatus.LOGIN_SUCCESS,
HttpStatus.ACCEPTED);
}
for response ServiceStatus or(ResponseBody)
public enum ServiceStatus {
LOGIN_SUCCESS(0, "Login success"),
private final int id;
private final String message;
//Enum constructor
ServiceStatus(int id, String message) {
this.id = id;
this.message = message;
}
public int getId() {
return id;
}
public String getMessage() {
return message;
}
}
Spring REST API should have below key in response
Status Code
Message
you will get final response below
{
"StatusCode" : "0",
"Message":"Login success"
}
you can use ResponseBody(java POJO, ENUM,etc..) as per your requirement.
I use to return Map<String,Object> in the Controller by using the toMap() method of org.json.JSONObject as follows.
#GetMapping("/json")
public Map<String, Object> getJsonOutput() {
JSONObject jsonObject = new JSONObject();
//construct jsonObject here
return jsonObject.toMap();
}
you can do this :
#RestController
#RequestMapping("/api")
class MyRestController
{
#GetMapping(path = "/hello")
public JSONObject sayHello()
{
return new JSONObject("{'aa':'bb'}").toMap();;
}
}

Spring Boot using Json as request parameters instead of an entity/model

Our company is planning to switch our microservice technology to Spring Boot. As an initiative I did some advanced reading and noting down its potential impact and syntax equivalents. I also started porting the smallest service we had as a side project.
One issue that blocked my progress was trying to convert our Json request/response exchange to Spring Boot.
Here's an example of the code: (This is Nutz framework for those who don't recognize this)
#POST
#At // These two lines are equivalent to #PostMapping("/create")
#AdaptBy(type=JsonAdapter.class)
public Object create(#Param("param_1") String param1, #Param("param_2) int param2) {
MyModel1 myModel1 = new MyModel1(param1);
MyModel2 myModel2 = new MyModel2(param2);
myRepository1.create(myMode12);
myRepository2.create(myModel2);
return new MyJsonResponse();
}
On PostMan or any other REST client I simply pass POST:
{
"param_1" : "test",
"param_2" : 1
}
I got as far as doing this in Spring Boot:
#PostMapping("/create")
public Object create(#RequestParam("param_1") String param1, #RequestParam("param_2) int param2) {
MyModel1 myModel1 = new MyModel1(param1);
MyModel2 myModel2 = new MyModel2(param2);
myRepository1.create(myMode12);
myRepository2.create(myModel2);
return new MyJsonResponse();
}
I am not sure how to do something similar as JsonAdapter here. Spring doesn't recognize the data I passed.
I tried this but based on the examples it expects the Json paramters to be of an Entity's form.
#RequestMapping(path="/wallet", consumes="application/json", produces="application/json")
But I only got it to work if I do something like this:
public Object (#RequestBody MyModel1 model1) {}
My issue with this is that MyModel1 may not necessarily contain the fields/parameters that my json data has.
The very useful thing about Nutz is that if I removed JsonAdapter it behaves like a regular form request endpoint in spring.
I couldn't find an answer here in Stack or if possible I'm calling it differently than what existing spring devs call it.
Our bosses expect us (unrealistically) to implement these changes without forcing front-end developers to adjust to these changes. (Autonomy and all that jazz). If this is unavoidable what would be the sensible explanation for this?
In that case you can use Map class to read input json, like
#PostMapping("/create")
public Object create(#RequestBody Map<String, ?> input) {
sout(input.get("param1")) // cast to String, int, ..
}
I actually figured out a more straightforward solution.
Apparently this works:
#PostMapping("/endpoint")
public Object endpoint(#RequestBody MyWebRequestObject request) {
String value1 = request.getValue_1();
String value2 = request.getValue_2();
}
The json payload is this:
{
"value_1" : "hello",
"value_2" : "world"
}
This works if MyRequestObject is mapped like the json request object like so. Example:
public class MyWebRequestObject {
String value_1;
String value_2
}
Unmapped values are ignored. Spring is smart like that.
I know this is right back where I started but since we introduced a service layer for the rest control to interact with, it made sense to create our own request model object (DTOs) that is separate from the persistence model.
You can use #RequestBody Map as a parameter for #PostMapping, #PutMapping and #PatchMapping. For #GetMapping and #DeleteMapping, you can write a class which implements Converter to convert from json-formed request parameters to Map. And you would register that class as a bean with #Component annotation. Then you can bind your parameters to #RequestParameter Map.
Here is an example of Converter below.
#Component
public class StringToMapConverter implements Converter<String, Map<String, Object>> {
private final ObjectMapper objectMapper;
#Autowired
public StringToMapConverter(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
#Override
public Map<String, Object> convert(String source) {
try {
return objectMapper.readValue(source, new TypeReference<Map<String, Object>>(){});
} catch (IOException e) {
return new HashMap<>();
}
}
}
If you want to exclude specific field of your MyModel1 class, use #JsonIgnore annotation onto the field like below.
class MyModel1 {
private field1;
#JsonIgnore field2;
}
Then, I guess you can just use what you have done.(I'm not sure.)
public Object (#RequestBody MyModel1 model1) {}
i think that you can use a strategy that involve dto
https://auth0.com/blog/automatically-mapping-dto-to-entity-on-spring-boot-apis/
you send a json to your rest api that is map like a dto object, after you can map like an entity or use it for your needs
try this:
Add new annotation JsonParam and implement HandlerMethodArgumentResolver of this, Parse json to map and get data in HandlerMethodArgumentResolver
{
"aaabbcc": "aaa"
}
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.RUNTIME)
public #interface JsonParam {
String value();
}
#Component
public class JsonParamMethodResolver implements HandlerMethodArgumentResolver {
#Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(JsonParam.class);
}
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
RepeatedlyRequestWrapper nativeRequest = webRequest.getNativeRequest(RepeatedlyRequestWrapper.class);
if (nativeRequest == null) {
return null;
}
Gson gson = new Gson();
Map<String, Object> response = gson.fromJson(nativeRequest.getReader(), new TypeToken<Map<String, Object>>() {
}.getType());
if (response == null) {
return null;
}
JsonParam parameterAnnotation = parameter.getParameterAnnotation(JsonParam.class);
String value = parameterAnnotation.value();
Class<?> parameterType = parameter.getParameterType();
return response.get(value);
}
}
#Configuration
public class JsonParamConfig extends WebMvcConfigurerAdapter {
#Autowired
JsonParamMethodResolver jsonParamMethodResolver;
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(jsonParamMethodResolver);
}
}
#PostMapping("/methodName")
public void methodName(#JsonParam("aaabbcc") String ddeeff) {
System.out.println(username);
}

Jaxb annotation #xmlelement(name="ElementName") not binding with xml element name

My Spring MVC Web Service code is as follows.
Model Class
#XmlRootElement(name="wrappedSecretData")
public class VendorData {
private long lKeyId;
#XmlElement(name="keyId")
public long getlKeyId() {
return lKeyId;
}
public void setlKeyId(long lKeyId) {
this.lKeyId = lKeyId;
}
}
Controller Method
#RequestMapping(value = "/vendor", method = RequestMethod.POST)
public String addVendor(#RequestBody VendorData vendorData) {
/*Checking recieved value*/
System.out.println(vendorData.getlKeyId());//**Returning 0 value **
return "Success";
}
Xml request body for web service
<wrappedVendorSecretsMetadata>
<keyId>1</keyId>
</wrappedVendorSecretsMetadata>
I am getting "0" value in lKeyId(Bold comment).
Where am I doing wrong.
Please provide the correct way to bind the xml element to object member using #XmlElement(name="keyId") annotation.
I think you need the #XmlElement only over the variable declaration.
try this:
#XmlRootElement(name="wrappedVendorSecretsMetadata")
#XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
public class VendorData {
private long lKeyId;
public VendorData(){
}
#XmlElement(name="keyId")
public long getlKeyId() {
return lKeyId;
}
public void setlKeyId(long lKeyId) {
this.lKeyId = lKeyId;
}
}
By default, annotations doesn't work with XmlMapper in jaxb. You have to register the annotation module for this purpose as I have done in the following code block:
String xmlData = getMyXmlData();
ObjectMapper objectMapper = new XmlMapper();
objectMapper.registerModule(new JaxbAnnotationModule());
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
MyClass myObj= objectMapper.readValue(xmlData , MyClass.class);
In your case, you have to overwrite the Xml to Object binding process. To do that, you can receive the the HttpRequest in your controller and then convert the xml data to VendorData using your own java code.

Jersey, Jackson and JAX-RS POST multiple JSON formats

I am trying to be able to define the following code:
public class MyObject {
private String name;
... // Other attributes
}
#Path(...)
#Stateless
public class MyRestResource {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response create(List<MyObject> myObjects) {
// Do some stuff there
}
}
I know that I need to use:
DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true
to setup correctly my object mapper to be able to accept single value as array on my rest resources. I succeed to setup that part.
My problem with this approach is that the following content is not differentiable:
{
"name": "a name",
... // other attributes
}
and
[{
"name": "a name",
... // other attributes
}]
will result into a list (List) of size one. Then, in the method create(List myObjects), I will not be able to do the difference between the List and the Single Object sent to the Rest Resource.
Then, my question is how to do something like that. The idea is to have only one #POST that accepts both Arrays and Single values?
Ideally, I will get rid of the configuration of the ObjectMapper to avoid letting the possibility to set Single Object into the other level of the JSON document. For example, I do not want to allow that:
{
...
"attributes": {
...
}
}
where normally this format should be mandatory:
{
...
"attributes": [{
...
}]
}
Based on that, I tried to put in place an object wrapper of my List to set if I am able to the difference between the list and the object. With something like that:
public class ObjectWrapper<T> {
private List<T> list;
private T object;
public boolean isObject() {
return list == null;
}
}
with the resource that becomes:
#Path(...)
#Stateless
public class MyRestResource {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response create(ObjectWrapper myObjects) {
// Do some stuff there
}
}
and trying to put in place the deserialization of my content through the JAX-RS/Jersey/Jackson mechanisms. If I let the solution as it is now, the deserialization fails due to the fact that the JSON format expected is the following:
{
"list": [{
"name": "a name",
... // other attributes
}]
}
Then I tried to write a custom deserializer but I am a bit lost in this task. I have something like that:
public class ObjectWrapperDeserializer<T> extends JsonDeserializer<T> {
#Override
public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
... // What to put there to deserialize Array or Object
}
}
I just want to deserialize the root level to set the content deserialized into the object wrapper. I also want to keep the feature configured in a class annotated with #ApplicationPath when the configuraiton of the different #Provider are done.
I hope that all the info will give a sufficient picture of what I want to do and what I already tested.
Waiting for suggestion on how to do a resource that accept Arrays or Objects on the same path.
Thanks a lot in advance.
Ok, finally I succeed to put in place a mechanism that do exactly what I am looking for. But, I am not sure if there are negative consequences such the performance or such things.
First, I defined a class that can accept both List or Single Object:
public class RootWrapper<T> {
private List<T> list;
private T object;
}
Then, I need a custom deserializer that is able to know which kind of T type to deserialize and to handle the collection or the single object.
public class RootWrapperDeserializer extends JsonDeserializer<CollectionWrapper<?>> {
private Class contentType;
public RootWrapperDeserializer(Class contentType) {
this.contentType = contentType;
}
#Override
public RootWrapper deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
// Retrieve the object mapper and read the tree.
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
JsonNode root = mapper.readTree(jp);
RootWrapper wrapper = new RootWrapper();
// Check if the root received is an array.
if (root.isArray()) {
List list = new LinkedList();
// Deserialize each node of the array using the type expected.
Iterator<JsonNode> rootIterator = root.getElements();
while (rootIterator.hasNext()) {
list.add(mapper.readValue(rootIterator.next(), contentType));
}
wrapper.setList(list);
}
// Deserialize the single object.
else {
wrapper.setObject(mapper.readValue(root, contentType));
}
return wrapper;
}
}
As far as I know, I try to only deserialize the root level manually and then let Jackson take the next operations in charge. I only have to know which real type I expect to be present in the Wrapper.
At this stage, I need a way to tell Jersey/Jackson which deserializer to use. One way I found for that is to create a sort of deserializer registry where are stored the type to deserialize with the right deserializer. I extended the Deserializers.Base class for that.
public class CustomDeserializers extends Deserializers.Base {
// Deserializers caching
private Map<Class, RootWrapperDeserializer> deserializers = new HashMap<>();
#Override
public JsonDeserializer<?> findBeanDeserializer(JavaType type,
DeserializationConfig config, DeserializerProvider provider,
BeanDescription beanDesc, BeanProperty property) throws JsonMappingException {
// Check if we have to provide a deserializer
if (type.getRawClass() == RootWrapper.class) {
// Check the deserializer cache
if (deserializers.containsKey(type.getRawClass())) {
return deserializers.get(type.getRawClass());
}
else {
// Create the new deserializer and cache it.
RootWrapperDeserializer deserializer =
new RootWrapperDeserializer(type.containedType(0).getRawClass());
deserializers.put(type.getRawClass(), deserializer);
return deserializer;
}
}
return null;
}
}
Ok, then I have my deserializers registry that create new deserializer only on demand and keep them once created. What I am not sure about that approach is if there is any concurrency issue. I know that Jackson do a lot of caching and do not call every time the findBeanDeserializer once it was called a first time on a specific deserialization context.
Now I have created my different classes, I need to do some plumbing to combine everything together. In a provider where I create the ObjectMapper, I can setup the deserializers registry to the created object mapper like below:
#Provider
#Produces(MediaType.APPLICATION_JSON)
public class JsonObjectMapper implements ContextResolver<ObjectMapper> {
private ObjectMapper jacksonObjectMapper;
public JsonObjectMapper() {
jacksonObjectMapper = new ObjectMapper();
// Do some custom configuration...
// Configure a new deserializer registry
jacksonObjectMapper.setDeserializerProvider(
jacksonObjectMapper.getDeserializerProvider().withAdditionalDeserializers(
new RootArrayObjectDeserializers()
)
);
}
#Override
public ObjectMapper getContext(Class<?> arg0) {
return jacksonObjectMapper;
}
}
Then, I can also define my #ApplicationPath that is my REST application like following:
public abstract class AbstractRestApplication extends Application {
private Set<Class<?>> classes = new HashSet<>();
public AbstractRestApplication() {
classes.add(JacksonFeature.class);
classes.add(JsonObjectMapper.class);
addResources(classes);
}
#Override
public Set<Class<?>> getClasses() {
return classes;
}
#Override
public Set<Object> getSingletons() {
final Set<Object> singletons = new HashSet<>(1);
singletons.add(new JacksonJsonProvider());
return singletons;
}
private void addResources(Set<Class<?>> classes) {
classes.add(SomeRestResource.class);
// ...
}
}
Now, everything is in place and I can write a REST resource method like that:
#POST
#Path("somePath")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response create(RootWrapper<SpecificClass> wrapper) {
if (wrapper.isObject()) {
// Do something for one single object
SpecificClass sc = wrapper.getObject();
// ...
return Response.ok(resultSingleObject).build();
}
else {
// Do something for list of objects
for (SpecificClass sc = wrapper.getList()) {
// ...
}
return Response.ok(resultList).build();
}
}
That's all. Do not hesitate to comment the solution. Feedbacks are really welcome especially around the way of deserialization process where I am really not sure that it is safe for performance and concurrency.

Parsing JSON object using jQuery and GSON returned by Spring controller

I was looking for some solution around here and I didnt find any correct answer to my question so I would like to ask you.
I have POJO with some simple attribs. and one List of another POJOs.
public class Standard implements Serializable {
private String id;
private String title;
private String description;
private Set<Interpretation> interpretations = new LinkedHashSet<Interpretation>();
}
public class Interpretation implements Serializable {
private String id;
private String title;
private String description;
}
In my controller class, I am returning Standard POJO with GSON.
#RequestMapping(value="/fillStandard", method= RequestMethod.GET)
public #ResponseBody String getStandard(#RequestParam String id) {
Standard s = DAOFactory.getInstance().getStandardDAO().findById(id);
return new Gson().toJson(s);
}
The question is, am I able to get the list of interpretations in my Standard POJO using jQuery ? Something like :
function newStandard() {
$.get("standard/fillStandard.htm", {id:"fe86742b2024"}, function(data) {
alert(data.interpretations[0].title);
});
}
Thanks a lot !
EDIT:
Well, thanks to #Atticus, there is solution of my problem. Hope that it will help somebody.
#RequestMapping(value="/fillStandard", method= RequestMethod.GET, produces="application/json")
public #ResponseBody Standard getStandard(#RequestParam String id) {
Standard s = DAOFactory.getInstance().getStandardDAO().findById(id);
return s;
}
Using #ResponseBody allows you to return the whole POJO, but you need to add produces="application/json" to your #RequestMapping annotation. Then you will be able to catch a returning object as JSON in jQuery like as I supposed.
function newStandard() {
$.get("standard/fillStandard.htm", {id:"idOfStandard"}, function(data) {
alert(data.id); //Standard id
alert(data.interpretations[0].title); //id of Interpretation on first place in array
});
Well you have to create and register your custom serializer.
It goes like this:
//You create your builder that registers your custom serializer with the class you want to serialize
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Standard.class, new StandardSerializer());
//Then you create your Gson object
Gson gson = builder.create();
//Then you pass your object to the gson like
Standard s = DAOFactory.getInstance().getStandardDAO().findById(id);
gson.toJson(s);
Your serializer looks like this:
public class StandardSerializer implements JsonSerializer<Standard>{
#Override
public JsonElement serialize(Standard src, Type typeOfSrc,
JsonSerializationContext context) {
JsonObject obj = new JsonObject();
//You put your simple objects in like this
obj.add("id",new JsonPrimitive(src.getId()));
//You put your complex objects in like this
JsonObject interpretations = new JsonObject();
//Here you need to parse your LinkedHashSet object and set up the values.
//For the sake of simplicity I just access the properties (even though I know this would not compile)
interpretations.add("title", src.getInterpretation().getTitle());
obj.add("interpretations", interpretations);
return obj;
}
}
In this case your Json would look something like:
{"id":"idValue", "title":"titleValue", "description":"descriptionValue", "interpretations":["id":"interpretationIdValue"]}
Now, you can access your data with jQuery like this:
function newStandard() {
$.get("standard/fillStandard.htm", {id:"fe86742b2024"}, function(data) {
alert(data.interpretations.title);
});
}
I hope this helps.
EDIT:
I see that your response gets converted to the declared method argument type which is String (as stated here: 16.3.3.2 Supported method return types). But what you really want is your Standrad POJO converted to JSON. I am not very familiar with Spring but as I have read here (16.3.2.6 Producible Media Types) there is another, maybe easier solution. If you want to return a JSON object, then change the return type of the
getStandard method to Standard instead of String and add produces="application/json" to your #RequestMapping annotation. As far as I have read this should tell Spring that the return type should be converted to JSON. In this case you do not need to use Gson.

Categories

Resources