I am struggling with this:
We have a Table class with a Guava multimap (simplified code, basically 1 member, 2 constructors, getter and setter for a 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 serialize this using Spring MVC 3.2.11 using jackson 2.4.3.
Corresponding POM dependencies:
<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 (after 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"> </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 extended Jackson2ObjectMapperFactoryBean:
<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 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 doesn't use 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 serializes it correctly:
{ "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 (either of the two approaches), I get:
{ "fields":{ "empty": false } }
My controller has the @ResponseBody annotation and returns a table.
EDITED: I am debugging the deep classes Spring (firs time, ;-)) and org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor processes the request. This is due to my problem ... Is my Spring xml somehow contradicting @ResponseBody annotations?
Any ideas what I'm doing wrong?
NOTE. I need a multimap, cannot be a standard Java build.