Configurer ObjectMapper au printemps

91

mon objectif est de configurer le objectMapperde manière à ne sérialiser que les éléments annotés avec @JsonProperty.

Pour ce faire, j'ai suivi cette explication qui explique comment configurer le objectmapper.

J'ai inclus le objectmapper personnalisé comme décrit ici .

Cependant, lorsque la classe NumbersOfNewEventsest sérialisée, elle contient toujours tous les attributs du fichier json.

Quelqu'un a-t-il un indice? Merci d'avance

Jackson 1.8.0 ressort 3.0.5

CustomObjectMapper

public class CompanyObjectMapper extends ObjectMapper {
    public CompanyObjectMapper() {
        super();
        setVisibilityChecker(getSerializationConfig()
                .getDefaultVisibilityChecker()
                .withCreatorVisibility(JsonAutoDetect.Visibility.NONE)
                .withFieldVisibility(JsonAutoDetect.Visibility.NONE)
                .withGetterVisibility(JsonAutoDetect.Visibility.NONE)
                .withIsGetterVisibility(JsonAutoDetect.Visibility.NONE)
                .withSetterVisibility(JsonAutoDetect.Visibility.DEFAULT));
    }
}

servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

    <context:component-scan base-package="de.Company.backend.web" />

    <mvc:annotation-driven />

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

    <bean id="jacksonObjectMapper" class="de.Company.backend.web.CompanyObjectMapper" />
</beans>

NumbersOfNewEvents

public class NumbersOfNewEvents implements StatusAttribute {

    public Integer newAccepts;
    public Integer openRequests;

    public NumbersOfNewEvents() {
        super();
    }
}
Steve Eastwood
la source

Réponses:

136

En utilisant Spring Boot (1.2.4) et Jackson (2.4.6), la configuration basée sur les annotations suivante a fonctionné pour moi.

@Configuration
public class JacksonConfiguration {

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true);

        return mapper;
    }
}
Alex
la source
3
Spring prend en charge la personnalisation de certaines propriétés de Jackson dans application.properties: docs.spring.io/spring-boot/docs/current/reference/html/…
NangSaigon
10
Salut les gars. J'ai rencontré un problème: lorsque vous déclarez le bean avec le nom objectMapper dans la configuration java, comme il est montré dans l'exemple, ce bean est ignoré par Spring Boot, car le bean avec un tel nom est déjà enregistré dans le système, même si vous placez \ @ Annotation principale dessus. Pour résoudre ce problème, vous devez enregistrer le bean avec un nom différent, par exemple jsonObjectMapper, et le marquer avec l'annotation \ @Primary
Demwis
Veuillez vous référer à ma réponse si vous n'utilisez pas Spring Boot.
Dormouse
Une question: vous devez nommer la classe JacksonConfiguration? C'est un moyen automatique de demander à Spring Boot d'utiliser cette classe comme classe de configuration pour Jackson?
Marco Sulla
Cela fonctionne-t-il vraiment lors de l'utilisation également @EnableWebMvc? Je doute parce que ... voir dans ma réponse : Pourquoi l'utilisation de @EnableWebMvc est un problème?
adrhc
25

C'est peut-être parce que j'utilise Spring 3.1 (au lieu de Spring 3.0.5 comme votre question l'a spécifié), mais la réponse de Steve Eastwood n'a pas fonctionné pour moi. Cette solution fonctionne pour Spring 3.1:

Dans votre contexte printemps xml:

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
        <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
        <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
            <property name="objectMapper" ref="jacksonObjectMapper" />
        </bean>        
    </mvc:message-converters>
</mvc:annotation-driven>

<bean id="jacksonObjectMapper" class="de.Company.backend.web.CompanyObjectMapper" />
Andrew Newdigate
la source
Cela a aidé à utiliser Jackson 2.x avec Spring 3.1: stackoverflow.com/questions/10420040/…
Aram Kocharyan
Utile! J'ai dû adopter cette approche pour enregistrer mon ObjectMapper personnalisé lors de la mise à niveau de Spring 3.0 vers 3.2. Auparavant défini une propriété objectMapper lors de la définition d'un bean MappingJacksonJsonView, mais cela ne fonctionnait plus. C'est avec Jackson 1.7
David Easley
20

Il y en a org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBeandepuis longtemps. À partir de la version 1.2 de Spring Boot, il existe org.springframework.http.converter.json.Jackson2ObjectMapperBuilderpour Java Config.

Dans String, la configuration peut être aussi simple que:

spring.jackson.deserialization.<feature_name>=true|false
spring.jackson.generator.<feature_name>=true|false
spring.jackson.mapper.<feature_name>=true|false
spring.jackson.parser.<feature_name>=true|false
spring.jackson.serialization.<feature_name>=true|false
spring.jackson.default-property-inclusion=always|non_null|non_absent|non_default|non_empty

dans classpath:application.propertiesou du code Java dans la @Configurationclasse:

@Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.indentOutput(true).dateFormat(new SimpleDateFormat("yyyy-MM-dd"));
    return builder;
}

Voir:

gavenkoa
la source
2
Cela fonctionne pour la plupart des cas d'utilisation, mais la configuration jackson de Spring Boot est ignorée si vous utilisez l'annotation @EnableWebMvc (testée dans Spring Boot 2.0.3).
John
1
Bien que je n'ai pas testé, je suis sûr que cela ne fonctionnera pas avec @EnableWebMvc (malgré l'utilisation ou non du démarrage à ressort). Pourquoi? voir WebMvcConfigurationSupport (DelegatingWebMvcConfiguration étend WebMvcConfigurationSupport): Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json().
adrhc
16

J'ai utilisé ceci avec Jackson 2.x et Spring 3.1.2+

servlet-context.xml:

Notez que l'élément racine est <beans:beans>, donc vous devrez peut-être supprimer beanset ajouter mvccertains de ces éléments en fonction de votre configuration.

    <annotation-driven>
        <message-converters>
            <beans:bean
                class="org.springframework.http.converter.StringHttpMessageConverter" />
            <beans:bean
                class="org.springframework.http.converter.ResourceHttpMessageConverter" />
            <beans:bean
                class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <beans:property name="objectMapper" ref="jacksonObjectMapper" />
            </beans:bean>
        </message-converters>
    </annotation-driven>

    <beans:bean id="jacksonObjectMapper"
        class="au.edu.unimelb.atcom.transfer.json.mappers.JSONMapper" />

au.edu.unimelb.atcom.transfer.json.mappers.JSONMapper.java:

public class JSONMapper extends ObjectMapper {

    public JSONMapper() {
        SimpleModule module = new SimpleModule("JSONModule", new Version(2, 0, 0, null, null, null));
        module.addSerializer(Date.class, new DateSerializer());
        module.addDeserializer(Date.class, new DateDeserializer());
        // Add more here ...
        registerModule(module);
    }

}

DateSerializer.java:

public class DateSerializer extends StdSerializer<Date> {

    public DateSerializer() {
        super(Date.class);
    }

    @Override
    public void serialize(Date date, JsonGenerator json,
            SerializerProvider provider) throws IOException,
            JsonGenerationException {
        // The client side will handle presentation, we just want it accurate
        DateFormat df = StdDateFormat.getBlueprintISO8601Format();
        String out = df.format(date);
        json.writeString(out);
    }

}

DateDeserializer.java:

public class DateDeserializer extends StdDeserializer<Date> {

    public DateDeserializer() {
        super(Date.class);
    }

    @Override
    public Date deserialize(JsonParser json, DeserializationContext context)
            throws IOException, JsonProcessingException {
        try {
            DateFormat df = StdDateFormat.getBlueprintISO8601Format();
            return df.parse(json.getText());
        } catch (ParseException e) {
            return null;
        }
    }

}
Aram Kocharyan
la source
10

J'ai trouvé la solution maintenant basée sur https://github.com/FasterXML/jackson-module-hibernate

J'ai étendu le mappeur d'objets et ajouté les attributs dans le constructeur hérité.

Ensuite, le nouveau mappeur d'objets est enregistré en tant que bean.

<!-- https://github.com/FasterXML/jackson-module-hibernate -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="messageConverters">
        <array>
            <bean id="jsonConverter"
            class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
                <property name="objectMapper">
                    <bean class="de.company.backend.spring.PtxObjectMapper"/>
                </property>
            </bean>
        </array>
    </property>
</bean>   
Steve Eastwood
la source
Merci beaucoup pour la solution! J'ai passé plus de quatre heures à creuser autour de <annotation-driven> , il n'a tout simplement pas ramassé les convertisseurs. J'ai essayé votre réponse, enfin ça marche!
snowindy
1
Pour utiliser des annotations, vous pouvez simplement annoter votre mappeur personnalisé qui s'étend en ObjectMappertant que @Componentet @Primary. C'est l'approche recommandée dans le Spring Reference Guide et cela a bien fonctionné pour moi.
OlgaMaciaszek
9

Si vous souhaitez ajouter un ObjectMapper personnalisé pour enregistrer des sérialiseurs personnalisés, essayez ma réponse.

Dans mon cas (Spring 3.2.4 et Jackson 2.3.1), configuration XML pour un sérialiseur personnalisé:

<mvc:annotation-driven>
    <mvc:message-converters register-defaults="false">
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper">
                <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                    <property name="serializers">
                        <array>
                            <bean class="com.example.business.serializer.json.CustomObjectSerializer"/>
                        </array>
                    </property>
                </bean>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

a été de manière inexpliquée réécrit par défaut par quelque chose.

Cela a fonctionné pour moi:

CustomObject.java

@JsonSerialize(using = CustomObjectSerializer.class)
public class CustomObject {

    private Long value;

    public Long getValue() {
        return value;
    }

    public void setValue(Long value) {
        this.value = value;
    }
}

CustomObjectSerializer.java

public class CustomObjectSerializer extends JsonSerializer<CustomObject> {

    @Override
    public void serialize(CustomObject value, JsonGenerator jgen,
        SerializerProvider provider) throws IOException,JsonProcessingException {
        jgen.writeStartObject();
        jgen.writeNumberField("y", value.getValue());
        jgen.writeEndObject();
    }

    @Override
    public Class<CustomObject> handledType() {
        return CustomObject.class;
    }
}

Aucune configuration XML ( <mvc:message-converters>(...)</mvc:message-converters>) n'est nécessaire dans ma solution.

user11153
la source
6

J'utilise Spring 4.1.6 et Jackson FasterXML 2.1.4.

    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="com.fasterxml.jackson.databind.ObjectMapper">
                        <!-- 设置不输出null字段-->
                        <property name="serializationInclusion" value="NON_NULL"/>
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

cela fonctionne à ma configuration applicationContext.xml

jecyhw
la source
6

SOLUTION 1

Première solution de travail (testée) utile surtout lors de l'utilisation de @EnableWebMvc:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private ObjectMapper objectMapper;// created elsewhere
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        // this won't add a 2nd MappingJackson2HttpMessageConverter 
        // as the SOLUTION 2 is doing but also might seem complicated
        converters.stream().filter(c -> c instanceof MappingJackson2HttpMessageConverter).forEach(c -> {
            // check default included objectMapper._registeredModuleTypes,
            // e.g. Jdk8Module, JavaTimeModule when creating the ObjectMapper
            // without Jackson2ObjectMapperBuilder
            ((MappingJackson2HttpMessageConverter) c).setObjectMapper(this.objectMapper);
        });
    }

SOLUTION 2

Bien sûr, l'approche commune ci-dessous fonctionne également (fonctionne également avec @EnableWebMvc):

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private ObjectMapper objectMapper;// created elsewhere
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        // this will add a 2nd MappingJackson2HttpMessageConverter 
        // (additional to the default one) but will work and you 
        // won't lose the default converters as you'll do when overwriting
        // configureMessageConverters(List<HttpMessageConverter<?>> converters)
        // 
        // you still have to check default included
        // objectMapper._registeredModuleTypes, e.g.
        // Jdk8Module, JavaTimeModule when creating the ObjectMapper
        // without Jackson2ObjectMapperBuilder
        converters.add(new MappingJackson2HttpMessageConverter(this.objectMapper));
    }

Pourquoi l'utilisation de @EnableWebMvc est-elle un problème?

@EnableWebMvc utilise DelegatingWebMvcConfigurationquelle extension WebMvcConfigurationSupportfait ceci:

if (jackson2Present) {
    Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();
    if (this.applicationContext != null) {
        builder.applicationContext(this.applicationContext);
    }
    messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));
}

ce qui signifie qu'il n'y a aucun moyen d'injecter le vôtre ObjectMapperdans le but de le préparer à être utilisé pour créer la valeur par défaut MappingJackson2HttpMessageConverterlors de l'utilisation de @EnableWebMvc.

adrhc
la source
Génial! En fait, RepositoryRestMvcConfiguration, qui fait partie de spring-data-rest-webmvc: 3.2.3 n'utilise pas ObjectMapper créé par Spring et seul objectMapper personnalisé avec des modules ne fonctionne pas directement. La première approche est actuelle et remplace ObjectMapper personnalisé par spring-data-rest-webmvc. Merci!
Valtoni Boaventura le
J'ai eu un problème où je ne pouvais pas personnaliser mon ObjectMapper qui était misérable b / c de EnableWebMvc. Je suis tellement content que je suis tombé dessus.
Evan White
3

Dans Spring Boot, 2.2.xvous devez le configurer comme ceci:

@Bean
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
    return builder.build()
}

Kotlin:

@Bean
fun objectMapper(builder: Jackson2ObjectMapperBuilder) = builder.build()
CorayThan
la source
2

Au-dessus de Spring 4, il n'est pas nécessaire de configurer MappingJacksonHttpMessageConvertersi vous avez uniquement l'intention de configurer ObjectMapper.

(configurer vous MappingJacksonHttpMessageConverterfera perdre un autre MessageConverter)

Il vous suffit de faire:

public class MyObjectMapper extends ObjectMapper {

    private static final long serialVersionUID = 4219938065516862637L;

    public MyObjectMapper() {
        super();
        enable(SerializationFeature.INDENT_OUTPUT);
    }       
}

Et dans votre configuration Spring, créez ce bean:

@Bean 
public MyObjectMapper myObjectMapper() {        
    return new MyObjectMapper();
}
Sam YC
la source
Ne fonctionne pas - - customObjectMapper: défini par la méthode 'customObjectMapper' dans la ressource de chemin de classe [com / Config.class] -_halObjectMapper: defined in null
Angshuman Agarwal
1
Et cela ne fonctionnera qu'avec Spring Boot. Pas simple Spring Web.
Dormouse
2

Pour configurer un convertisseur de messages en plain spring-web, dans ce cas pour activer le Java 8 JSR-310 JavaTimeModule, vous devez d'abord implémenter WebMvcConfigurerdans votre @Configurationclasse, puis remplacer la configureMessageConvertersméthode:

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().modules(new JavaTimeModule(), new Jdk8Module()).build()
            .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    converters.add(new MappingJackson2HttpMessageConverter(objectMapper));
}

De cette manière, vous pouvez enregistrer toute personnalisation définie ObjectMapperdans une configuration Spring basée sur Java.

Loir
la source
Je préférerais que l'ObjectMapper soit injecté et configuré via Spring Boot.
Constantino Cronemberger
1
Bien sûr, si vous utilisez Spring Boot. Ce n'était pas une question Spring Boot.
Dormouse
Oui, il est intéressant de noter que lors de l'utilisation de Spring Web avec Spring Boot, il n'utilise pas l'ObjectMapper fourni par Spring Boot et c'est pourquoi je me suis retrouvé dans cette question qui m'a aidé.
Constantino Cronemberger
Cela effacerait tous les convertisseurs de messages par défaut! Je suis sûr que personne ne veut ça. Voir ma réponse pour la solution.
adrhc
Eh bien, si vous souhaitez étendre les convertisseurs de messages, la solution correcte serait de remplacer extendMessageConverters, selon la documentation Spring . Mais je n'en ai pas eu besoin, car j'ai juste besoin d'un personnalisé MappingJackson2HttpMessageConverter.
Loir
1

J'utilise Spring 3.2.4 et Jackson FasterXML 2.1.1.

J'ai créé un JacksonObjectMapper personnalisé qui fonctionne avec des annotations explicites pour chaque attribut des objets mappés:

package com.test;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

public class MyJaxbJacksonObjectMapper extends ObjectMapper {

public MyJaxbJacksonObjectMapper() {

    this.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
            .setVisibility(PropertyAccessor.CREATOR, JsonAutoDetect.Visibility.ANY)
            .setVisibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.NONE)
            .setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE)
            .setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE);

    this.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
    }
}

Ensuite, ceci est instancié dans la configuration du contexte ( servlet-context.xml ):

<mvc:annotation-driven>
    <mvc:message-converters>

        <beans:bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <beans:property name="objectMapper">
                <beans:bean class="com.test.MyJaxbJacksonObjectMapper" />
            </beans:property>
        </beans:bean>

    </mvc:message-converters>
</mvc:annotation-driven>

Cela fonctionne très bien!

Carlos AG
la source