I have several REST services defined that are currently returning JSON formatted objects as service response bodies. I'm trying to make this service also accept XML as a new requirement though it does not accept this.
I'm following the spring-mvc-showcase sample project and have setup my pom.xml dependencies almost identically, my service definitions likewise are very simple.
#Controller
#RequestMapping(value = "api/sales/*")
public class SalesController {
#RequestMapping(value = "/countries", method = RequestMethod.GET)
#ResponseBody
public List<NamedEntity> getCountries() {
NamedEntity has the appropriate #XmlRootElement annotation.
Could somebody explain the most basic requirements that I would need to get XML as a ResponseBody that the spring-mvc-showcase sample project is using.
EDIT: Added spring MVC sample.
The sample from the spring-mvc-showcase is as follows:
package org.springframework.samples.mvc.messageconverters;
#Controller
#RequestMapping("messageconverters/*")
public class MessageConvertersController {
#RequestMapping(value="/xml", method=RequestMethod.GET)
public #ResponseBody JavaBean writeXml() {
return new JavaBean("bar", "fruit");
Check the request header, client needs to have "application/xml" in the header, rather than "application/json "
Having said this make sure you have registered appropriate message converter for your object. If you are using Java 6 then Spring will auto detect JAXB in your classpath or else you can manually add the converter.
Add #Produces("application/xml") to getCountries()
You need to send "application/xml", not "application/application+xml". Also consider using:
#RequestMapping(value = "/countries", method = RequestMethod.GET, produces={"application/json", "application/xml"})
This ensures your method responds to those media types only and rejects others with 406 HTTP status code.
try this dispatcher servlet config.
<mvc:annotation-driven
content-negotiation-manager="contentManager" />
<bean id="contentManager"
class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="false" />
<property name="ignoreAcceptHeader" value="false" />
<property name="defaultContentType" value="application/json" />
<property name="useJaf" value="false" />
</bean>
and some dependency
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.4.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.4.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.4.3</version>
</dependency>
Related
Let's consider this fragment of code:
#ResponseBody
#RequestMapping(value = "/getFiltered", method = RequestMethod.POST)
public ResponseEntity<OraPhysicalGoodMiniDataWrapper> getFilteredProducts(#RequestBody OraSalesOfGoodsFilters filters) {
return oraProductListController.getFilteredProducts(filters);
}
filters parameter has String named searchValue.
The problem is: when I pass for example searchValue: "(2016)" in json, Spring controller receives searchValue=(2016).
I tried to decode it with Java URLDecoder but it didn't work.
And my question is:
is there any Java method which could decode all such characters in String? Or should I write my own method?
This is your problem solution. using javascript or spring settings.
Javascript
var param = {
"searchValue": "(2016)"
};
$.ajax({
type: 'POST',
dataType: 'JSON',
data: JSON.stringify(param),
contentType:"application/json; charset=UTF-8",
url: '/test/filter',
error: function() {
// error
},
success: function(returnJSON) {
// success
}
}):
Spring settings
Pom.xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.0.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.0.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.0.4</version>
</dependency>
servlet.xml
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</list>
</property>
</bean>
As Joop Eggen mentioned in comment - the StringEscapeUtils.unescapeXml(String value)method proved to be helpful.
It's kind of werid that it's unescapeXml(), not unescapeJavaScript() (which is not working), because I pass the value on frontend as JSON (not XML) from JavaScript, but I'm glad it works.
This is a follow up questions from this:
Spring Hibernate FetchType LazyInitializationException even when not calling association
I have tried to implement this solution but with no luck. I am wondering if I have made a mistake some where...
Avoid Jackson serialization on non fetched lazy objects
applicationContext.xml
<context:annotation-config />
<context:component-scan base-package="com.app"></context:component-scan>
<tx:annotation-driven />
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="com.app.service.HibernateAwareObjectMapper" />
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!-- Hibernate server settings -->
HibernateAwareObjectMapper
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module;
public class HibernateAwareObjectMapper extends ObjectMapper {
public HibernateAwareObjectMapper() {
Hibernate4Module hm = new Hibernate4Module();
registerModule(hm);
}
}
pom.xml
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.3.11.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.3.2.Final</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.8.6</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.8.6</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.6</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate4</artifactId>
<version>2.8.6</version>
</dependency>
And I am still getting the following error
Hibernate: select this_.person_id as person_i1_1_0_, this_.age as age2_1_0_, this_.name as name3_1_0_ from person this_
[Person [person_id=1, name=eric, age=11]]
Jun 20, 2017 12:37:29 AM org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver handleHttpMessageNotWritable
WARNING: Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: failed to lazily initialize a collection of role: com.app.person.Person.phones, could not initialize proxy - no Session (through reference chain: java.util.HashMap["results"]->java.util.ArrayList[0]->com.app.person.Person["phones"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException:
Please any insight or suggestions are much appreciated...
One last thing, I don't want to marke it as JsonIgnore, as it will never be serialized. There will be times where I will need to retrieve the Lazy object.
Thank you
to resolve this probleme there two ways. the first one is to ignore the phones if you don't need this information in this request by using #JsonIgnore.
the second way is the load the phones before closing the transaction. and to do that you can use Hibernate.initialize(person.getPhones()). Or you can fetch this collection using #OneToMany(fetch = FetchType.EAGER) on the collection.
I'm using WebLgic have the following saaj soap message factory. I've tried leaving off the messageFactory and just the soapVersion and vice Versa. I've also tried the different messageFactory Impl beans that are commented out. I'm still getting the error in the title. Why is t trying to use version 1.1?
<bean id="saajMessageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory">
<property name="messageFactory">
<bean class="com.sun.xml.messaging.saaj.soap.MessageFactoryImpl"
/>
<!-- <bean class="com.sun.xml.messaging.saaj.soap.ver1_2.SOAPMessageFactory1_2Impl"
/> -->
<!-- <bean class="weblogic.xml.saaj.MessageFactoryImpl"/> -->
</property>
<property name="soapVersion">
<util:constant static-field="org.springframework.ws.soap.SoapVersion.SOAP_12" />
</property>
</bean>
Error:
SAAJ MessageFactory: Unable to create message factory for SOAP: com.sun.xml.messaging.saaj.soap.ver1_1.SOAPMessageFactory1_1Impl cannot be cast to javax.xml.soap.MessageFactory; nested exception is javax.xml.soap.SOAPException: Unable to create message factory for SOAP: com.sun.xml.messaging.saaj.soap.ver1_1.SOAPMessageFactory1_1Impl cannot be cast to javax.xml.soap.MessageFactory
at org.springframework.ws.support.DefaultStrategiesHelper.instantiateBean(DefaultStrategiesHelper.java:188)
at org.springframework.ws.support.DefaultStrategiesHelper.getDefaultStrategies(DefaultStrategiesHelper.java:134)
at org.springframework.ws.support.DefaultStrategiesHelper.getDefaultStrategy(DefaultStrategiesHelper.java:219)
at org.springframework.ws.support.DefaultStrategiesHelper.getDefaultStrategy(DefaultStrategiesHelper.java:203)
at org.springframework.ws.client.core.WebServiceTemplate.initMessageFactory(WebServiceTemplate.java:310)
pom file:
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.messaging.saaj</groupId>
<artifactId>saaj-impl</artifactId>
<version>1.3.15</version>
</dependency>
<dependency>
<groupId>javax.xml.soap</groupId>
<artifactId>saaj-api</artifactId>
<version>1.3.5</version>
</dependency>
Weblogic.xml:
<prefer-application-packages>
<package-name>javax.xml.soap.*</package-name>
<package-name>javax.xml.ws.*</package-name>
<package-name>com.sun.xml.messsaging.saaj.*</package-name>
</prefer-application-packages>
I should also add that I tried setting various this JVM argument with various options: -Djavax.xml.soap.MessageFactory=weblogic.xml.saaj.MessageFactoryImpl
such as com.sun.xml.messaging.saaj.soap.ver1_2.SOAPMessageFactory1_2Impl. The error message now reflects the new Impl class, but I still can't get around the error.
I'm not sure exactly what ended up fixing this issue, but I was also getting some other errors as I tried different things, and I finally got everything working as specified in this post: java.lang.NoClassDefFoundError: org/apache/cxf/jaxrs/impl/UriBuilderImpl. I ended up removing those pom file entries above.
One thing I did have to do was to download a new version of Maven as the one that came in my VM was giving me problems.
I want to send JSON from my controller. I have the following configuration.
spring-servlet.xml :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<mvc:annotation-driven/>
<mvc:resources mapping="/resources/**" location="/resources/"/>
<context:component-scan base-package="com.castle.controllers"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
.js :
function testAjax() {
var data = {userName: "MyUsername", password:"Password"};
$.ajax({
url: 'ajax/test.htm',
dataType : 'json',
type : 'POST',
contentType: "application/json",
data: JSON.stringify(data),
success: function(response){
alert('Load was performed.');
}
});
}
UserTest.java:
public class UserTest {
private String userName;
private String password;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
TestAjaxController.java :
#Controller
#RequestMapping("/ajax")
public class TestAjaxController {
#RequestMapping(method = RequestMethod.POST, value = "/test.htm")
public #ResponseBody
UserTest testAjaxRequest(#RequestBody UserTest user) {
return user;
}
}
pom.xml :
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
</dependency>
When i do this request, i get in my Controller JSON represented as UserTest object. But on return :
HTTP Status 406 - The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers.
What i'm doing wrong? I know, there is a lot of questions about such cases, but i can't fix it for 2 days...
UPDATE
I Have found the solution!!
It's only need to return an Object. Not a User object or something.
But
return Object;
public #ResponseBody Object testAjaxRequest(#RequestBody UserTest user) {
List<UserTest> list = new ArrayList<>();
list.add(user);
list.add(user);
list.add(user);
return list;
The main issue here is that the path "/test.htm" is going to use content negotiation first before checking the value of an Accept header. With an extension like *.htm, Spring will use a org.springframework.web.accept.ServletPathExtensionContentNegotiationStrategy and resolve that the acceptable media type to return is text/html which does not match what MappingJacksonHttpMessageConverter produces, ie. application/json and therefore a 406 is returned.
The simple solution is to change the path to something like /test, in which content negotiation based on the path won't resolve any content type for the response. Instead, a different ContentNegotiationStrategy based on headers will resolve the value of the Accept header.
The complicated solution is to change the order of the ContentNegotiationStrategy objects registered with the RequestResponseBodyMethodProcessor which handles your #ResponseBody.
I had the same problem in the end it was the version of org.codehaus.jackson 1.9.x,
when I switched from 1.9.x jackson to 2.x (fasterxml) in my pom.xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.2.3</version>
</dependency>
also is necesary : <mvc:annotation-driven />
I ran into this issue when upgrading Spring in a legacy project. The .html suffix of the AJAX endpoints (which were trying to return JSON) were indeed forcibly made to try to produce HTML due to the suffix, and since the handlers were returning an object, the request ended in a 406 error since Spring couldn't figure out how to make HTML out of a plain Java object.
Instead of altering the endpoint suffixes, or doing a complete configuration of a custom ContentNegotiationStrategy, making this change was enough:
<mvc:annotation-driven />
changed to:
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="false" />
</bean>
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>
Adding these lines to context configuration solved the same issue for me:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
I used Spring 4.1.x and Jackson 2.4.2
Make sure to use produces = "application/json" in your annotations.
I've tried to have a controller in Spring return a JSON response to no avail using the Jackson classes as recommended with 3.0. I've got the jackson jar files(jackson-core-asl-1.5.5.jar & jackson-mapper-asl-1.5.5.jar) in my class path of course.
As for the appconfig.xml entries, I'm not sure I need these. I've put them in there as a last act of desperation before returning to ol' fashion non-json ajax.
In debug, I watch the controller get the request, return the foo and then, in firebug, get a 406.
The error messages are as follows:
From the logger when set to debug:
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
From the response:
(406) The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers ().
My appconfig.xml is here:
<!-- Configures support for #Controllers -->
<mvc:annotation-driven />
<!-- Resolves view names to protected .jsp resources within the /WEB-INF/views directory -->
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="html" value="text/html"/>
<entry key="json" value="application/json"/>
</map>
</property>
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
</list>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
</list>
</property>
</bean>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages"></property>
</bean>
My controller
#RequestMapping(value="foo/bar", method=RequestMethod.GET)
public #ResponseBody foo getFoo(#RequestParam String fooId) {
return new foo(fooId);
}
On the jsp, where the ajax call is made:
function addRow() {
$.getJSON("foo/bar", {
fooId: 1
}, function(data) {
alert("it worked.");
});
}
Let me know if there's any more info that is needed.
Get rid of all Jackson beans, and of the json mapping in the negotiating resolver. the mvc:annotation-driven should configure everything you need for the Jackson serialization to work.
Make sure the POJO you return has get()ers, one for each field.
Make sure the appserver (Tomcat) has the libraries even if you are sure your build system (Eclipse/Maven) does.
I've had this error twice now.
Just now I added getters to my pojo. The 406 error went away and I got JSON as expected.
I assume that because my fields were package-protected (the default access), it would grab them, but I guess not.
For the record, in case it matters, I also made the POJO implement Serializable, toString(), serialVersionUID, no-arg constructor, and explicit constructors.
The prior time I cleaned/cleared/refreshed my Tomcat cache and did whatever else to force it to reload. I believe when I added the Jackson dependencies, it fixed my compile time errors, but since tomcat missed them, at runtime Spring MVC did not discover the Jackson libraries, and produced the 406 error about unacceptable response type.
Also, make sure that you add two jackson related jar files.
jackson-core-asl-1.9.8.jar
jackson-mapper-asl-1.9.8.jar
The version can be different.
I know that this is an old thread, but maybe someone will encounter the same problem as me. I got that exception when migrating an application from Spring 4.0.3.RELEASE to Spring 4.1.0.RELEASE. In my case updating Jackson from 1.9.x to 2.4.x did the trick.
I have got same exception when migrating from spring 3.x to spring 4.x.
I solved it with updating jackson dependencies from
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
to
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.5.1</version>
</dependency>
Nothing else was needed for me.
Sending the Accept: application/xml header does not work. But sending Accept: application/json actually works, and jackson mapper kicks in. I got rid of my 406 and got my serialized java object in json format with no more config than #ResponseBody and return new MyObject()
:)
Thanks skaffman for this information, and thanks Bozho for the working header value :D
This question pops up everywhere on the net and I got bitten by it a couple of times. Spring 3.0.6 (and 5 possibly) has some issues in rendering json. Once I changed to 3.1.0.RELEASE version eveyrything worked AS IS. Without any config changes. Things to note is, the return method must have #ResponseBody (as in the example before) and must be in servlet-context.xml or your spring context configuration file.
May be my answer is a bit late, but it may help some one else visiting this question.
I got my problem resolved by adding
hibernate-validator-4.0.2
and gave me another exception (class not found exception: org.slf4j.LoggerFactory) which i resolved by adding
slf4j-api-1.5.6.jar
I hope it will help someone.