Configurazione di ObjectMapper in primavera


91

il mio obiettivo è configurare il objectMapperin modo che serializzi solo gli elementi annotati con @JsonProperty.

Per farlo ho seguito questa spiegazione che dice come configurare l'objectmapper.

Ho incluso il mappatore di oggetti personalizzato come descritto qui .

Tuttavia, quando la classe NumbersOfNewEventsviene serializzata, contiene ancora tutti gli attributi nel json.

Qualcuno ha un suggerimento? Grazie in anticipo

Jackson 1.8.0 molla 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();
    }
}

Risposte:


136

Utilizzando Spring Boot (1.2.4) e Jackson (2.4.6), la seguente configurazione basata sull'annotazione ha funzionato per me.

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

3
Spring supporta la personalizzazione di alcune proprietà di Jackson in application.properties: docs.spring.io/spring-boot/docs/current/reference/html/…
NangSaigon

10
Hey ragazzi. Ho riscontrato un problema che quando dichiari un bean con nome objectMapper in java config, proprio come mostrato nell'esempio, questo bean viene ignorato da Spring Boot, perché il bean con tale nome è già registrato nel sistema, anche se inserisci \ @ Annotazione primaria su di essa. Per risolvere questo problema è necessario registrare un bean con un nome diverso, ad esempio jsonObjectMapper, e contrassegnarlo con \ @Primary annotation
Demwis

Si prega di fare riferimento alla mia risposta se non si utilizza Spring Boot.
Dormouse

Una domanda: devi nominare la classe JacksonConfiguration? È un modo automatico per istruire Spring Boot a usare questa classe come classe di configurazione per Jackson?
Marco Sulla

Funziona davvero anche quando si usa @EnableWebMvc? Dubito perché ... vedi nella mia risposta : Perché l'utilizzo di @EnableWebMvc è un problema?
adrhc

25

Potrebbe essere perché sto usando Spring 3.1 (invece di Spring 3.0.5 come specificato dalla tua domanda), ma la risposta di Steve Eastwood non ha funzionato per me. Questa soluzione funziona per Spring 3.1:

Nel tuo contesto xml primaverile:

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

Ciò ha aiutato a utilizzare Jackson 2.x con Spring 3.1: stackoverflow.com/questions/10420040/…
Aram Kocharyan

Utile! Ho dovuto adottare questo approccio per registrare il mio ObjectMapper personalizzato durante l'aggiornamento dalla Spring 3.0 alla 3.2. In precedenza era stata impostata una proprietà objectMapper durante la definizione di un bean MappingJacksonJsonView, ma non funzionava più. Questo è con Jackson 1.7
David Easley

20

C'è org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBeanda molto tempo. A partire dalla release 1.2 di Spring Boot c'è org.springframework.http.converter.json.Jackson2ObjectMapperBuilderper Java Config.

In String Boot la configurazione può essere semplice come:

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

in classpath:application.propertieso un po 'di codice Java in @Configurationclasse:

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

Vedere:


2
Funziona per la maggior parte dei casi d'uso, tuttavia la configurazione jackson di Spring Boot viene ignorata se si utilizza l'annotazione @EnableWebMvc (testata in Spring Boot 2.0.3).
John

1
Anche se non ho provato, sono sicuro che non funzionerà con @EnableWebMvc (nonostante utilizzi o meno l'avvio a molla). Perché? vedi WebMvcConfigurationSupport (DelegatingWebMvcConfiguration estende WebMvcConfigurationSupport): Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json().
adrhc

16

L'ho usato con Jackson 2.xe Spring 3.1.2+

servlet-context.xml:

Nota che l'elemento radice è <beans:beans>, quindi potrebbe essere necessario rimuovere beanse aggiungere mvcalcuni di questi elementi a seconda della configurazione.

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

}

10

Ho trovato la soluzione ora basata su https://github.com/FasterXML/jackson-module-hibernate

Ho esteso il mappatore di oggetti e aggiunto gli attributi nel costruttore ereditato.

Quindi il nuovo mappatore oggetti viene registrato come 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>   

Grazie mille per la soluzione! Ho trascorso più di quattro ore a scavare intorno a <annotation-driven> , semplicemente non ha raccolto i convertitori. Ho provato la tua risposta, finalmente funziona!
snowindy

1
Per utilizzare le annotazioni, puoi semplicemente annotare il tuo Mapper personalizzato che si estende ObjectMappercome @Componente @Primary. Questo è l'approccio consigliato nella Guida di riferimento di primavera e ha funzionato bene per me.
OlgaMaciaszek

9

Se desideri aggiungere ObjectMapper personalizzato per la registrazione di serializzatori personalizzati, prova la mia risposta.

Nel mio caso (Spring 3.2.4 e Jackson 2.3.1), configurazione XML per serializzatore personalizzato:

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

è stato in un modo inspiegabile sovrascritto di default da qualcosa.

Questo ha funzionato per me:

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

Nessuna configurazione XML ( <mvc:message-converters>(...)</mvc:message-converters>) è necessaria nella mia soluzione.


6

Sto usando Spring 4.1.6 e 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>

funziona nella mia configurazione applicationContext.xml


6

SOLUZIONE 1

Prima soluzione funzionante (testata) utile soprattutto quando si utilizza @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);
        });
    }

SOLUZIONE 2

Ovviamente anche l'approccio comune di seguito funziona (funziona anche con @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));
    }

Perché l'utilizzo di @EnableWebMvc è un problema?

@EnableWebMvc sta usando DelegatingWebMvcConfigurationwhich extends WebMvcConfigurationSupportche fa questo:

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

il che significa che non c'è modo di iniettare il tuo ObjectMappercon lo scopo di prepararlo per essere utilizzato per la creazione del valore predefinito MappingJackson2HttpMessageConverterquando si utilizza @EnableWebMvc.


Grande! In realtà RepositoryRestMvcConfiguration, parte di spring-data-rest-webmvc: 3.2.3 non utilizza ObjectMapper creato da Spring e solo objectMapper personalizzato con moduli non funziona immediatamente. Il primo approccio è effettivo e sostituisce ObjectMapper personalizzato da spring-data-rest-webmvc. Grazie!
Valtoni Boaventura

Ho avuto un problema in cui non riuscivo a personalizzare il mio ObjectMapper che era miserabile b / c di EnableWebMvc. Sono così felice di essermi imbattuto in questo.
Evan White il

3

In Spring Boot 2.2.xè necessario configurarlo in questo modo:

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

Kotlin:

@Bean
fun objectMapper(builder: Jackson2ObjectMapperBuilder) = builder.build()

2

Sopra Spring 4, non è necessario configurare MappingJacksonHttpMessageConverterse si intende solo configurare ObjectMapper.

(la configurazione MappingJacksonHttpMessageConverterti farà perdere altri MessageConverter)

Devi solo fare:

public class MyObjectMapper extends ObjectMapper {

    private static final long serialVersionUID = 4219938065516862637L;

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

E nella tua configurazione Spring, crea questo bean:

@Bean 
public MyObjectMapper myObjectMapper() {        
    return new MyObjectMapper();
}

Non funziona - - customObjectMapper: definito dal metodo "customObjectMapper" nella risorsa percorso classi [com / Config.class] -_halObjectMapper: defined in null
Angshuman Agarwal

1
E questo funzionerà solo con Spring Boot. Non semplice Spring Web.
Dormouse

2

Per configurare un convertitore di messaggi in semplice spring-web, in questo caso per abilitare Java 8 JSR-310 JavaTimeModule, devi prima implementarlo WebMvcConfigurernella tua @Configurationclasse e quindi sovrascrivere il configureMessageConvertersmetodo:

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

In questo modo è possibile registrare qualsiasi personalizzato definito ObjectMapperin una configurazione Spring basata su Java.


Preferirei che ObjectMapper fosse iniettato e configurato tramite Spring Boot.
Constantino Cronemberger

1
Certo, se usi Spring Boot. Questa non era una domanda di Spring Boot.
Dormouse

Sì, è interessante notare che quando si utilizza Spring Web con Spring Boot non utilizza ObjectMapper fornito da Spring Boot ed è per questo che sono finito in questa domanda che mi ha aiutato.
Constantino Cronemberger,

Ciò cancellerebbe tutti i convertitori di messaggi predefiniti! Sono abbastanza sicuro che nessuno lo voglia. Vedi la mia risposta per la soluzione.
adrhc

Bene, se si desidera estendere i convertitori di messaggi, la soluzione corretta sarebbe l'override extendMessageConverters, come da documentazione di Spring . Ma non ne avevo bisogno, perché ho solo bisogno di un personalizzato MappingJackson2HttpMessageConverter.
Dormouse

1

Sto usando Spring 3.2.4 e Jackson FasterXML 2.1.1.

Ho creato un JacksonObjectMapper personalizzato che funziona con annotazioni esplicite per ogni attributo degli oggetti mappati:

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

Quindi questo viene istanziato nella configurazione del contesto ( 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>

Funziona bene!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.