web-dev-qa-db-fra.com

Spring configure le format JSON @ResponseBody

Imaginez que j'ai cette méthode annotée dans un Spring 3 @Controller

@RequestMapping("")
public @ResponseBody MyObject index(@RequestBody OtherObject obj) {
    MyObject result = ...;
    return result;
}

Mais je dois configurer le format de sortie JSON, comme si je le faisais:

ObjectMapper om = new ObjectMapper();
om.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, true);
om.getSerializationConfig()
        .setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
om.getSerializationConfig()
        .set(SerializationConfig.Feature.INDENT_OUTPUT, false);

Est-il possible de configurer ce comportement?

J'ai trouvé quelques questions connexes, mais je ne sais pas trop comment les adapter à mon cas particulier:

  1. prefixjson de printemps avec le corps de réponse
  2. Qui définit le type de contenu de la réponse dans Spring MVC (@ResponseBody)

Je vous remercie !

39
Guido

AngerClown m'a dirigé dans la bonne direction.

C'est ce que j'ai finalement fait, juste au cas où quelqu'un le trouverait utile.

<bean
    class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="messageConverters">
        <list>
            <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
                <property name="objectMapper" ref="jacksonObjectMapper" />
            </bean>
        </list>
    </property>
</bean>

<!-- jackson configuration : https://stackoverflow.com/questions/3661769 -->
<bean id="jacksonObjectMapper" class="org.codehaus.jackson.map.ObjectMapper" />
<bean id="jacksonSerializationConfig" class="org.codehaus.jackson.map.SerializationConfig"
    factory-bean="jacksonObjectMapper" factory-method="getSerializationConfig" />
<bean
    class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetObject" ref="jacksonSerializationConfig" />
    <property name="targetMethod" value="setSerializationInclusion" />
    <property name="arguments">
        <list>
            <value type="org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion">NON_DEFAULT</value>
        </list>
    </property>
</bean>

Je dois encore comprendre comment configurer les autres propriétés telles que:

om.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, true);
15
Guido

Pour les personnes qui utilisent Configuration Spring basée sur Java :

@Configuration
@ComponentScan(basePackages = "com.domain.sample")
@EnableWebMvc
public class SpringConfig extends WebMvcConfigurerAdapter {
....

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        final ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        converter.setObjectMapper(objectMapper);
        converters.add(converter);
        super.configureMessageConverters(converters);
    }

....

}

J'utilise MappingJackson2HttpMessageConverter - qui vient de plus rapide.

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.3.1</version>
</dependency>

Si vous voulez utiliser le mappeur codehaus-jackson, utilisez plutôt celui-ci MappingJacksonHttpMessageConverter

 <dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-mapper-asl</artifactId>
    <version>${codehaus.jackson.version}</version>
 </dependency>
31
Õzbek

J'ai besoin de résoudre un problème très similaire, qui consiste à configurer Jackson Mapper pour "Ne pas sérialiser les valeurs nulles pour l'amour du Christ !!!".

Je ne voulais pas quitter mvc: balise pilotée par annotation, alors j'ai découvert comment configurer ObjectMapper de Jackson sans supprimer mvc: piloté par annotation et sans ajouter ContentNegotiatedViewResolver.

La belle chose est que vous n'avez pas à écrire de code Java vous-même!

Et voici la configuration XML (à ne pas confondre avec les différents espaces de noms des classes de Jackson, j'ai simplement utilisé la nouvelle bibliothèque Jakson 2.x ... la même chose devrait également fonctionner avec les bibliothèques Jackson 1.x):

<mvc:annotation-driven>
    <mvc:message-converters register-defaults="true">
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper">
                <bean class="com.fasterxml.jackson.databind.ObjectMapper">
                    <property name="serializationInclusion">
                        <value type="com.fasterxml.jackson.annotation.JsonInclude.Include">NON_NULL</value>
                    </property>
                </bean>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>
28
tkuty

Pour Spring version 4.1.3+

J'ai essayé la solution de Jama, mais toutes les réponses ont ensuite été renvoyées avec le type de contenu 'application/json', y compris la page HTML générée principale.

Remplacer configureMessageConverters(...)empêche le printemps de configurer les convertisseurs par défaut. Spring 4.1.3 permet de modifier les convertisseurs déjà configurés en remplaçantextendMessageConverters(...):

@Configuration
public class ConverterConfig extends WebMvcConfigurerAdapter {
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        for (HttpMessageConverter<?> converter : converters) {
            if (converter instanceof AbstractJackson2HttpMessageConverter) {
                AbstractJackson2HttpMessageConverter c = (AbstractJackson2HttpMessageConverter) converter;
                ObjectMapper objectMapper = c.getObjectMapper();
                objectMapper.setSerializationInclusion(Include.NON_NULL);
            }
        }

        super.extendMessageConverters(converters);
    }
}

voir org.springframework..WebMvcConfigurationSupport#getMessageConverters()

voir org.springframework..WebMvcConfigurationSupport#addDefaultHttpMessageConverters(...)

10
Melv

En spring3.2, une nouvelle solution est introduite par: http://static.springsource.org/spring/docs/3.2.0.BUILD-SNAPSHOT/api/org/springframework/http/converter/json/Jackson2ObjectMapperFactoryBean.html , ci-dessous est mon exemple:

 <mvc:annotation-driven>
   ​<mvc:message-converters>
     ​​<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
       ​​​<property name="objectMapper">
         ​​​​<bean
 class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
           ​​​​​<property name="featuresToEnable">
             ​​​​​​<array>
               ​​​​​​​<util:constant static-field="com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_SINGLE_QUOTES" />
             ​​​​​​</array>
           ​​​​​</property>
         ​​​​</bean>
       ​​​</property>
     ​​</bean>
   ​</mvc:message-converters>
 </mvc:annotation-driven>
10
Tody.Lu

J'ai écrit mon propre FactoryBean qui instancie un ObjectMapper (version simplifiée): 

 public class ObjectMapperFactoryBean implements FactoryBean<ObjectMapper>{

        @Override
        public ObjectMapper getObject() throws Exception {
                ObjectMapper mapper = new ObjectMapper();
                mapper.getSerializationConfig().setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
                return mapper;
        }

        @Override
        public Class<?> getObjectType() {
                return ObjectMapper.class;
        }

        @Override
        public boolean isSingleton() {
                return true;
        }

}

Et l'utilisation dans la configuration de printemps:

<bean
    class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="messageConverters">
        <list>
            <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
                <property name="objectMapper" ref="jacksonObjectMapper" />
            </bean>
        </list>
    </property>
</bean>

4
oehmiche

Ne répond pas à la question, mais voici le meilleur résultat de Google.

Si quelqu'un vient ici et veut le faire pour le printemps 4 (comme cela m'est arrivé), vous pouvez utiliser l'annotation

@JsonInclude(Include.NON_NULL)

sur la classe de retour.

4
Mário Fernandes

Jetez un coup d'œil à l'approche de Rick Hightower. Son approche évite de configurer ObjectMapper en tant que singleton et vous permet de filtrer la réponse JSON pour le même objet de différentes manières pour chaque méthode de requête.

http://www.jroller.com/RickHigh/entry/filtering_json_feeds_from_spring

3
Greg Sheremeta

Vous pouvez configurer ObjectMapper en tant que bean dans votre fichier XML Spring. Ce qui contient une référence à ObjectMapper est la classe MappingJacksonJsonView. Vous devez ensuite attacher la vue à un ViewResolver.

Quelque chose comme ça devrait marcher:

<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
      <property name="mediaTypes">
      <map>
        <entry key="json" value="application/json" />
        <entry key="html" value="text/html" />
      </map>
    </property>
    <property name="viewResolvers">
      <list>
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
          <property name="prefix" value="/WEB-INF/jsp/" />
          <property name="suffix" value=".jsp" />
        </bean>
      </list>
    </property>
    <property name="defaultViews">
      <list>
        <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
          <property name="prefixJson" value="false" />
          <property name="objectMapper" value="customObjectMapper" />
        </bean>
      </list>
    </property>
  </bean>

customObjectMapper est défini ailleurs dans le fichier xml. Notez que vous pouvez directement définir les valeurs de la propriété Spring avec les définitions Enums Jackson; voir cette question .

En outre, ContentNegotiatedViewResolver n'est probablement pas requis, c'est simplement le code que j'utilise dans un projet existant.

2
AngerClown

Oui, mais que se passe-t-il si vous commencez à utiliser mixins par exemple, vous ne pouvez pas utiliser ObjectMapper en tant que singleton, car vous appliquerez la configuration globalement. Vous allez donc ajouter ou définir les classes mixin sur la même instance ObjectMapper?

1
serge

Vous pouvez effectuer les opérations suivantes (version jackson <2):

Classe de mappeur personnalisé:

import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonSerialize;

public class CustomObjectMapper extends ObjectMapper {
    public CustomObjectMapper() {
        super.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, true);
        super.getSerializationConfig()
                .setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
        super.getSerializationConfig()
                .set(SerializationConfig.Feature.INDENT_OUTPUT, false);
    }
}

Configuration de printemps:

<mvc:annotation-driven>
    <mvc:message-converters register-defaults="false">
        <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
            <property name="objectMapper">
                <bean class="package.CustomObjectMapper"/>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>
1
I V