2014-11-17 2 views
5

я борюсь с этим:Spring MVC + Джексон + гуавы MultiMap

У нас есть класс таблицы с Multimap Гуава (упрощенный код, в основном 1 член, 2 конструкторами, геттер и сеттер для 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; 
    } 
} 

И я хочу сериализовать это с использованием Spring MVC 3.2.11 с использованием jackson 2.4.3.

POM соответствующих зависимостей:

<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> 

Мой spring.xml СЕЙЧАС выглядит следующим образом (после этого 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" /> 

Я также попробовал этот другой подход используя расширенный 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> 

И тогда FactoryBean выглядит следующим образом:

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; 
} 

}

У меня есть класс Test, который работает отлично и не используя Spring вообще (только тестирование Table.class + Джексон + гуавы) Упрощенная:

Table study = getTable(); 

ObjectMapper mapper = new ObjectMapper(); 
mapper.registerModule(new GuavaModule()); 

String tableString = mapper.writeValueAsString(table); 

Он serialises это правильно:

{ 
    "fields":{ 
    "Field1":[ 
     { 
     "index":0, 
     "header":"Field1", 
     "fieldType":"fieldtype", 
     "description":null, 
     "cleanHeader":null 
     } 
    ], 
    "Field2":[ 
     { 
     "index":1, 
     "header":"Field2", 
     "fieldType":"fieldtype", 
     "description":null, 
     "cleanHeader":null 
     } 
    ] 
    } 
} 

Использование пружины (любой из 2-х подходов) я получаю:

{ 
    "fields":{ 
    "empty": false 
    } 
} 

Мой контроллер имеет @ResponseBody аннотацию, и это возвращает таблицу.

Редакция: Я отладка глубоко в пружинных классы (пихта времени ;-)) и org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor обрабатывает запрос. Это связано с моей проблемой ... Является ли моя весна xml, так или иначе противоречащей аннотации @ResponseBody?

Любые идеи, что я делаю неправильно?

ПРИМЕЧАНИЕ. Мне нужна Multimap, не может быть стандартной сборкой Java.

ответ

0

В конце концов я узнал, что @ResponseBody аннотацию вынуждал использовать другой «viewResolver», которая использовала Джексон, но без модуля Гуавы.

Таким образом, чтобы исправить это я удалил @ResponseBody аннотаций в моем методе контроллера:

<!-- language: java --> 
@ResponseBody 
@RequestMapping("table") 
public Table getTable() { 

К сожалению, это возвращение: {Таблица: {...}} и был артефакт представлен ValueHandlder (ModelAttributeMethodProcessor).

В конце концов, что работает сейчас:

1.- Восстановление @ResponseBody 2.- Регистрация модуля Guava в MappingJackson2HttpMessageConverter, которые приходят по умолчанию с обработчиком @ResponseBody.

Это как пружина XML выглядит следующим образом: Много чисто и проще:

<!-- JSON parser configuration--> 
<bean id="guavaObjectMapper" class="com.fasterxml.jackson.databind.ObjectMapper"/> 

<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> 
    <property name="targetObject"><ref local="guavaObjectMapper" /></property> 
    <property name="targetMethod"><value>registerModule</value></property> 
    <property name="arguments"> 
     <list> 
      <bean id="guavaModule" class="com.fasterxml.jackson.datatype.guava.GuavaModule"/> 
     </list> 
    </property> 
</bean> 


<mvc:annotation-driven> 
    <mvc:message-converters register-defaults="true"> 
     <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> 
      <property name="objectMapper"> 
       <ref local="guavaObjectMapper"/> 
      </property> 
     </bean> 
    </mvc:message-converters> 
</mvc:annotation-driven> 
1

Я думаю, вам нужно добавить раздел mvc: messsage-converters в ваш spring.xml. Например

<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> 
+0

Thanks Katerina! Я добавил ваш код и попытался с обоими из этих подходов .... Я получаю те же «поля»: {«empty»: false} result. – Pablo

3

Незнайка, если решение ниже подходит проблему точно, но только в том случае, если вы хотите настроить SpringBootApplication также сериализовать гуавы, вы можете повысить свой класс @Configuration, используя @Bean следующим образом:

@Configuration 
public class Application extends SpringBootServletInitializer { 

    @Bean 
    ObjectMapper customizeJacksonConfiguration() { 
     ObjectMapper om = new ObjectMapper(); 
     om.registerModule(new GuavaModule()); 
     return om; 
    } 
} 
+0

Спасибо! не используя SpringBootApplication, но может быть полезен для тех, кто его использует. – Pablo

+0

Если у вас есть несколько модулей Jackson, импортированных в ваш файл pom, это будет загружать их все: 'ObjectMapper mapper = new ObjectMapper(); mapper.findAndRegisterModules(); ' – Mark

Смежные вопросы