I have a rest endpoint(A) that returns a large json object. I have to return it to another rest api(B) in a different format. Would it be beneficial to map it to an object, transform the data into the object the other REST API(B) is expecting?
Would it be better to use a json library and pull out the parts I need and transform the data that way?
Thanks
I use Spring for my web apps. in my context config, I have:
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jacksonMessageConverter"/>
</list>
</property>
</bean>
Then in my controller code, I use:
#RequestMapping(...)
public #ResponseBody MyPojo getMyPojo(#PathVariable("id") Integer id, #RequestBody MyOtherPojo) {
MyPojo myPojo = new MyPojo();
// do some business methods
return myPojo
}
The #ResponseBody MyPojo tells the jacksonMessageConverter to convert the MyPojo to a JSON object, and the #RequestBody tells the jacksonMessageConverter to convert a JSON variable into an instance of MyOtherPojo.
Related
What works
Suppose I have a spring bean definition of an ArrayList:
<bean id="availableLanguages" class="java.util.ArrayList">
<constructor-arg>
<bean class="java.util.Arrays" factory-method="asList">
<constructor-arg>
<list>
<value>de</value>
<value>en</value>
</list>
</constructor-arg>
</bean>
</constructor-arg>
</bean>
Now I can inject this into all kinds of beans, e.g. like this:
#Controller
class Controller {
#Autowired
public Controller(ArrayList<String> availableLanguages) {
// ...
}
}
This works great.
How it breaks
However if I change my controller a tiny bit and use the type List instead of ArrayList like this:
#Controller
class Controller {
#Autowired
public Controller(List<String> availableLanguages) {
// ...
}
}
Then instead I get a list of all beans of type String rather then the bean I defined. However I actually want to wrap my List into an unmodifiable List, but this will only be possible if I downgrade my dependency to a list.
So far discovered workaround
The following XML file:
<bean id="availableLanguages" class="java.util.Collections" factory-method="unmodifiableList">
<constructor-arg>
<bean class="java.util.Arrays" factory-method="asList">
<constructor-arg>
<list>
<value>de</value>
<value>en</value>
</list>
</constructor-arg>
</bean>
</constructor-arg>
</bean>
works together with this controller:
#Controller
class Controller {
#Autowired
public Controller(Object availableLanguages) {
List<String> theList = (List<String>)availableLanguages;
}
}
While this works the extra type cast is ugly.
Findings so far
I figured that there is a special handling for collections in Spring 4.2.5 (the currently most recent version) which seems to cause all the trouble. It creates special behaviour when a parameter is an interface that extends Collection. Thus I can workaround by using Object or a concrete implementation as parameter type.
Question
Is there any way to directly inject a list into a bean? How?
Using #Qualifier will inject the bean with the given qualifier. You can name the list which you want to be a bean and that will work fine.
Could you please help me to configure correctly Jackson mix-in annotation with Spring MVC to customize a JSON response.
This is what I have now :
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonMessageConverter"/>
</list>
</property>
</bean>
<bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
I don't know how to configure it to map the 2 classes ... : addMixInAnnotations(User.class, UserMixIn.class);
dd the mixin configuration to the ObjectMapper once initialized in your Controller constructor:
#Controller
public class MyController {
private ObjectMapper objectMapper = new ObjectMapper();
public MyController(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
objectMapper.addMixInAnnotations(User.class, UserMixIn.class);
}
#RequestMapping("/some-path")
#ResponseBody
public String someMethod() {
List<User> users = new ArrayList<User>(); // Mock List to hold your Users
users.add(new User()); // Keep adding some users
return objectMapper.writeValueAsString(users, new TypeReference<List<User>>() {});
}
}
And check out the output :)
The solution given by #tmarwen will work just fine if you only need to configure the Mixin for a single controller.
However if you want to use the mixin thoughout all Spring controllers you need implement an approach similar to the following:
Change your XML configuration to:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonMessageConverter"/>
</list>
</property>
</bean>
<bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="mapper"/>
</bean>
Next you need to configure the mapper bean that is referenced in the XML above. You could easily do that in XML with the use of SpEL and FactoryBean, but there is no good reason to do so when you have a great and super easy to use alternative in Java Config.
#Configuration
public class JacksonConfig {
#Bean
public ObjectMapper mapper() {
final ObjectMapper mapper = new ObjectMapper();
mapper.addMixInAnnotations(User.class, UserMixIn.class);
return mapper;
}
}
With the changes above in place, you need absolutely no reference to ObjectMapper in your controllers and can use Spring MVC's JSON features just like you are using them now.
I have inherited a spring MVC project that basically has several controllers. I came across a declaration in an xml file. Apparently, this declaration allows for writing rest-based services and clients. Could someone explain with an example the apparent magic these declarations allow for?
<bean id="restClient" class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<util:list>
<bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
<bean id="formMessageConverter" class="org.springframework.http.converter.FormHttpMessageConverter"/>
</util:list>
</property>
</bean>
Thank you in advance.
resttemplate by spring , provided to convert your java object to desired output(html string, xml, json etc) during the service call over the network, and inturn the received response from the service will be unmarshalled back to java object or desired datatype.
<property name="messageConverters">
<util:list>
<bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
<bean id="formMessageConverter" class="org.springframework.http.converter.FormHttpMessageConverter"/>
<bean id="messageConverter"
class="org.springframework.http.converter.StringHttpMessageConverter" />
</util:list>
//marhsaller and unmarshaller
<bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<property name="marshaller" ref="jaxbMarshaller"></property>
<property name="unmarshaller" ref="jaxbMarshaller"></property>
<property name="supportedMediaTypes">
<list>
<value>application/xml</value>
<value>text/xml</value>
<value>json</value>
</list>
</property>
through above configuration resttemplate will use those appropriate converters to handle different datatypes, as i said it may be html, json or application xml
all we are doing is, with out writing java code , we are configuring the resttemplate and will be in spring context , this configuration applies where ever your resttemplate been used.
i have example here
let us say we invoke a service to check an user is valid user or not
class User{
string userId;
string password;
}
and service response with some code as 1 is valid and 0 as invalid
class ValidUser{
int validCode;
}
first you need to marshall into any of the acceptable datatype , let us have application/xml
all i am doing here is through config file
to above configuration i am adding jaxb marshaller and unmarshaller (see above config)
i have configured both marshaller and unmarshaller, and i am telling the acceptable datatypes which both should use while marshalling and unmarshalling
and finally, the below configuration tells the java objects which are acceptable during marshalling (request.User will be converted to xml) and unmarshalling (xml to convert back to response.validUser)
<bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>com.request.user</value>
<value>com.response.validUser</value>
</list>
</property>
</bean>
on here comes the java code
here you directly pass your java object , and your resttemplate will marshall it without any hassle !!!
User user = new User();
user.setName('test');
user.setPassword('password');
// setting media type as xml, and telling convert my user java obj to xml
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
HttpEntity<User> entity = new HttpEntity<User> (user , headers);
ResponseEntity<validUser> responseEntity = rest.exchange(URL, HttpMethod.POST, entity, validUser.class);
// let us assume that service response for valid user <validCode>1<validCode>
//then validuserreponse obj will have code as 1, let us say valid user.
ValidUser validUserResponse = responseEntity.getBody();
like wise you can also handle plain html text
headers.setContentType(MediaType.TEXT_HTML);
HttpEntity<String> entity = new HttpEntity<String>(htmlString, headers);
ResponseEntity<String> htmlStringresponse = rest.postForEntity(URL, entity, String.class);
if you see, the above java code, dont have any message converter code, marshaller and unmarshaller logic, all done in one liner with use of spring configuration.
I am developing a web application using spring MVC, Hiberbate and MySQL database. I am trying to send data to server from a client, but I can not do it.
In detail,
I want to send my JSON data to http://localhost:8080/app/test . In my test controller I want to get the data which comes from the client and write it to screen or save it to db. I have been searching about 3 days and I have tried many strategies to do it, but I can not.
You must explicitly tell spring to use a json parser.
For instance, here is a piece of my DispatcherServlet config :
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonConverter" />
</list>
</property>
</bean>
<bean id="jsonConverter"
class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json" />
</bean>
Jackson Must be in your classpath.
Then you write a method like this one in your annotated controller :
#RequestMapping(value = "/test", method = RequestMethod.POST)
public void myMethod(#RequestBody MyObject object) {
//... do what you want with the object
}
You just have to send an JSON object with properties that match the ones in MyObject, via a POST request.
I've implemented a RESTful web service with Spring. The service responds in XML or JSON based on the Accept header. Here's the context.xml mapping:
<bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller"/>
<bean id="xmlMessageConverter"
class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<constructor-arg ref="xstreamMarshaller"/>
<property name="supportedMediaTypes" value="application/xml"/>
</bean>
<bean id="jsonHttpMessageConverter"
class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="prefixJson" value="false"/>
<property name="supportedMediaTypes" value="application/json"/>
</bean>
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<util:list id="beanList">
<ref bean="xmlMessageConverter"/>
<ref bean="jsonHttpMessageConverter"/>
</util:list>
</property>
</bean>
Here is my controller method:
#Controller
#RequestMapping(value = "/entityService")
class RestfulEntityService {
#Resource
private EntityService entityService;
#ResponseBody
#RequestMapping(value = "/getAllEntities", method = RequestMethod.GET)
public List<Entity> getAllEntities() {
return entityService.getAllEntities();
}
}
The XML response is valid, however, when the client sets the Accept header to application/json, the response is invalid JSON.
Here is the JSON response sample:
[{"id":3,"attributes":[{"id":18,"attributeValue":null,"attributeName":"mobile","attributeType":"varchar(40)","entity":{"id":3,"attributes":[{"id":18,"attributeValue":null,"attributeName":"mobile","attributeType":"varchar(40)","entity":{"id":3,"attributes":[{"id":18,"attributeValue":null,"attributeName":"mobile","attributeType":"varchar(40)","entity":{"id":3,"attributes": ..... repeats for a while and then stops..
You're using XStream to serialize XML responses and Jackson JSON to serialize JSON responses. Looking at the JSON output you posted, it seems like there's a circular reference issue at hand. I'm guessing Entity has a list of attributes, each pointing to their respective entity. XStream handles circular references transparently by using XPath, this allows to preserve references when deserializing back to objects. Jackson is able to handle circular references since v1.6, but you need to help it by annotating your serialized entities with #JsonManagedReference and #JsonBackReference. I think Jackson is unique in allowing back references in JSON serialization.
See Jackson's documentation on handling bi-directional references using declarative methods for reference.