2014-11-17 23 views
5

sto lottando con questo:Spring MVC + Jackson + Guava multimap

abbiamo una classe da tavolo con un multimap Guava (codice semplificato, in fondo 1 membro, 2 costruttori, getter e setter per la multimap):

E voglio serializzare questo utilizzando Spring MVC 3.2.11 utilizzando jackson 2.4.3.

POM dipendenze rilevanti sono:

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

mio spring.xml SOCIETÀ si presenta così (segue questo 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" /> 

ho anche provato questo altro approccio utilizzando un Jackson2ObjectMapperFactoryBean esteso:

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

E poi la FactoryBean assomiglia a questo:

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

}

ho una classe di prova che funziona bene e non sta usando Primavera a tutti (solo test Table.class + Jackson + guava) semplificato:

Table study = getTable(); 

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

String tableString = mapper.writeValueAsString(table); 

E serialises in modo corretto:

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

Utilizzando primavera (uno qualsiasi dei 2 approcci) sto ottenendo:

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

mio controller ha un'annotazione @ResponseBody ed è il ritorno di una tabella.

CURA: Sto debug in profondità in classi primavera (tempo di abeti, ;-)) e org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor sta gestendo la richiesta. Questo è legato al mio problema ... Il mio xml di primavera è in qualche modo in contraddizione con l'annotazione @ResponseBody?

Qualche idea su cosa sto facendo male?

NOTA: Ho bisogno del Multimap, non può essere una raccolta Java standard.

risposta

0

Dopo tutto quello che ho scoperto che l'annotazione @ResponseBody stava costringendo ad usare un diverso "viewResolver", che stava usando Jackson, ma senza il modulo di Guava.

Così, per risolvere questo problema ho rimosso l'annotazione @ResponseBody nel mio metodo di controllo:

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

Purtroppo, questo era di ritorno: {Tabella: {...}} ed è stato un artefatto introdotto dal ValueHandlder (ModelAttributeMethodProcessor).

Alla fine, ciò che sta lavorando ora è:

1.- Ripristinare il @ResponseBody 2.- Registrare il modulo Guava all'interno del MappingJackson2HttpMessageConverter che vengono di default con il gestore @ResponseBody.

è così che l'xml primavera si presenta come: Molto pulito e semplice:

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

Penso che sia necessario aggiungere una sezione mvc: messsage-converters al proprio spring.xml. Per esempio

<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

Grazie Katerina! Ho aggiunto il tuo codice e provato con entrambi gli approcci di cui sopra .... Sto ottenendo gli stessi "campi": {"vuoto": falso} risultato. – Pablo

3

so se la soluzione qui di seguito si adatta al vostro problema esattamente, ma solo nel caso in cui si desidera configurare lo SpringBootApplication per serializzare anche guava, potresti migliorare la tua classe @Configuration in questo modo:

@Configuration 
public class Application extends SpringBootServletInitializer { 

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

Grazie! non usando SpringBootApplication, ma potrebbe essere utile per chi lo usa. – Pablo

+0

Se nel tuo pom file sono stati importati più moduli Jackson, questi verranno caricati tutti: 'ObjectMapper mapper = new ObjectMapper(); mapper.findAndRegisterModules(); ' – Mark