I'm struggling with this:
We have a Table class with a Guava multimap (simplified code, basically 1 member, 2 constructors, getter and setter for the multimap):
public class Table {
private LinkedHashMultimap<String,Field> fields;
public Table(){
this.fields = LinkedHashMultimap.create();
};
public Table (LinkedHashMultimap<String, Field> fields){
this.fields= fields;
}
public LinkedHashMultimap<String, Field> getFields() {
return fields;
}
public void setFields(LinkedHashMultimap<String, Field> fields) {
this.fields = fields;
}
}
And I want to serialise this using Spring MVC 3.2.11 using jackson 2.4.3.
POM relevant dependencies are:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.4.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-guava</artifactId>
<version>2.4.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.11.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>3.2.11.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
My spring.xml NOW looks like this (following this example)
<bean id="abstractJacksonObjectMapper"
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
p:targetMethod="registerModule">
<property name="targetObject">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"
p:indentOutput="true">
<!--<property name="featuresToDisable">-->
<!--<util:constant static-field="com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES" />-->
<!--</property>-->
</bean>
</property>
<property name="arguments">
<list>
<bean class="com.fasterxml.jackson.datatype.guava.GuavaModule" />
</list>
</property>
</bean>
<bean id="abstractMappingJacksonHttpMessageConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"
abstract="true"/>
<bean id="abstractMappingJacksonJsonView"
class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"
abstract="true"
p:extractValueFromSingleKeyModel="true"/>
<bean id="jacksonObjectMapper" parent="abstractJacksonObjectMapper" />
<bean id="mappingJacksonHttpMessageConverter"
parent="abstractMappingJacksonHttpMessageConverter"
p:objectMapper-ref="jacksonObjectMapper"
p:supportedMediaTypes="application/json" />
<bean id="mappingJacksonJsonView"
parent="abstractMappingJacksonJsonView"
p:objectMapper-ref="jacksonObjectMapper"
p:contentType="application/json" />
I also tried this other approach using an extended Jackson2ObjectMapperFactoryBean:
<!-- Json rendering configuration-->
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order" value="1" />
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
</map>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView">
<property name="objectMapper">
<bean class="my.package.Jackson2ObjectMapperFactoryBean"/>
</property>
</bean>
</list>
</property>
<property name="ignoreAcceptHeader" value="true" />
</bean>
And then the FactoryBean looks like this:
package my.package;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.guava.GuavaModule;
public class Jackson2ObjectMapperFactoryBean extends org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean{
public ObjectMapper getObject(){
ObjectMapper objectMapper =super.getObject();
objectMapper.registerModule(new GuavaModule());
return objectMapper;
}
}
I have a Test class that works fine and is not using Spring at all (just testing Table.class + Jackson + guava) Simplified:
Table study = getTable();
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new GuavaModule());
String tableString = mapper.writeValueAsString(table);
It serialises it properly:
{
"fields":{
"Field1":[
{
"index":0,
"header":"Field1",
"fieldType":"fieldtype",
"description":null,
"cleanHeader":null
}
],
"Field2":[
{
"index":1,
"header":"Field2",
"fieldType":"fieldtype",
"description":null,
"cleanHeader":null
}
]
}
}
Using spring (any of the 2 approaches) I'm getting:
{
"fields":{
"empty": false
}
}
My controller has a @ResponseBody annotation and it's returning a Table.
EDITED: I'm debugging deep into spring classes (firs time, ;-)) and org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor is handling the request. Is this related to my problem...Is my spring xml, somehow in contradiction with the @ResponseBody annotation?
Any ideas what I'm doing wrong?
NOTE: I need the Multimap, can't be a standard Java collection.
It uses only java annotation to config all beans. 2. Create The Example Project. Click File —> New —> Spring Legacy Project menu item to open the New Spring Legacy Project window, then select Spring MVC Project template in the Spring Legacy Project wizard dialog.
Multimap Implementation In the case of Guava Multimap, if we add two values for the same key, the second value will not override the first value. Instead, we will have two values in the resulting map.
Treat as the configuration file for Spring MVC-enabled applications. Also, we use the @Bean tag to register ViewResolver. We use InternalResourceViewResolver. Create another class, which will replace our traditional web.xml. We use Servlet 3.0 and extend the org.springframework.web.WebApplicationInitializer class.
@Controller annotation is used to mark that class as controller. Front Controller: It remains responsible for managing the flow of the web application. DispatcherServelet acts as a front controller in spring MVC. Eclipse (EE version). Tomcat Apache latest version. Step 1: Go to File menu and click on New -> Maven Project.
Dunno if the solution below fits your problem exactly, but just in case you want to configure your SpringBootApplication to also serialize guava, you could enhance your @Configuration
class using a @Bean
like this:
@Configuration
public class Application extends SpringBootServletInitializer {
@Bean
ObjectMapper customizeJacksonConfiguration() {
ObjectMapper om = new ObjectMapper();
om.registerModule(new GuavaModule());
return om;
}
}
I think you need to add an mvc:messsage-converters section to your spring.xml. For instance
<mvc:annotation-driven ignoreDefaultModelOnRedirect="true" >
<mvc:message-converters>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"/>
<bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"/>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With