Configuración de ObjectMapper en Spring

91

mi objetivo es configurar el objectMapperde manera que solo serialice los elementos que están anotados @JsonProperty.

Para hacerlo, seguí esta explicación que dice cómo configurar el objectmapper.

Incluí el mapeador de objetos personalizado como se describe aquí .

Sin embargo, cuando la clase NumbersOfNewEventsse serializa, todavía contiene todos los atributos en json.

¿Alguien tiene una pista? Gracias por adelantado

Jackson 1.8.0 resorte 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
fuente

Respuestas:

136

Usando Spring Boot (1.2.4) y Jackson (2.4.6), la siguiente configuración basada en anotaciones funcionó para mí.

@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
fuente
3
Spring admite personalizar algunas propiedades de Jackson en application.properties: docs.spring.io/spring-boot/docs/current/reference/html/…
NangSaigon
10
Hola chicos. Me enfrenté a un problema que cuando declara un bean con el nombre objectMapper en la configuración de Java, tal como se muestra en el ejemplo, Spring Boot ignora este bean, porque el bean con ese nombre ya está registrado en el sistema, incluso si coloca \ @ Anotación primaria en él. Para solucionar este problema, debe registrar un bean con un nombre diferente, por ejemplo, jsonObjectMapper, y marcarlo con la anotación \ @Primary
Demwis
Por favor refiérase a mi respuesta si estás no utilizando Spring arranque.
Lirón
Una pregunta: ¿debes nombrar la clase JacksonConfiguration? ¿Es una forma automática de indicar a Spring Boot que use esta clase como clase de configuración para Jackson?
Marco Sulla
¿Realmente funciona cuando se usa también @EnableWebMvc? Dudo porque ... vea en mi respuesta : ¿Por qué el uso de @EnableWebMvc es un problema?
adrhc
25

Puede ser porque estoy usando Spring 3.1 (en lugar de Spring 3.0.5 como especificó su pregunta), pero la respuesta de Steve Eastwood no funcionó para mí. Esta solución funciona para Spring 3.1:

En su contexto xml de primavera:

<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
fuente
Esto ayudó a usar Jackson 2.x con Spring 3.1: stackoverflow.com/questions/10420040/…
Aram Kocharyan
¡Útil! Tuve que adoptar este enfoque para registrar mi ObjectMapper personalizado al actualizar de Spring 3.0 a 3.2. Anteriormente había estado configurando una propiedad objectMapper al definir un bean MappingJacksonJsonView, pero eso ya no funcionaba. Esto es con Jackson 1.7
David Easley
20

Hay org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBeanpor mucho tiempo. A partir de la versión 1.2 de Spring Boot, existe org.springframework.http.converter.json.Jackson2ObjectMapperBuilderJava Config.

En String Boot la configuración puede ser tan simple como:

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

en classpath:application.propertieso algún código Java en @Configurationclase:

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

Ver:

Gavenkoa
fuente
2
Esto funciona para la mayoría de los casos de uso, sin embargo, la configuración de Spring Boot jackson se ignora si está utilizando la anotación @EnableWebMvc (probada en Spring Boot 2.0.3).
Juan
1
Aunque no lo probé, estoy seguro de que no funcionará con @EnableWebMvc (a pesar de usar o no el arranque de primavera). ¿Por qué? ver WebMvcConfigurationSupport (DelegatingWebMvcConfiguration extiende WebMvcConfigurationSupport): Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json().
adrhc
16

He usado esto con Jackson 2.xy Spring 3.1.2+

servlet-context.xml:

Tenga en cuenta que el elemento raíz es <beans:beans>, por lo que es posible que deba eliminar beansy agregar mvcalgunos de estos elementos según su configuración.

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

Encontré la solución ahora basada en https://github.com/FasterXML/jackson-module-hibernate

Extendí el mapeador de objetos y agregué los atributos en el constructor heredado.

Luego, el nuevo mapeador de objetos se registra como un 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
fuente
¡Muchas gracias por la solución! He pasado más de cuatro horas investigando <annotation-driven> , simplemente no recogió los convertidores. Probé tu respuesta, ¡finalmente funciona!
snowindy
1
Para usar anotaciones, puede simplemente anotar su Mapper personalizado que se extiende ObjectMappercomo @Componenty @Primary. Este es el enfoque recomendado en la Guía de referencia de Spring y me ha funcionado bien.
OlgaMaciaszek
9

Si desea agregar ObjectMapper personalizado para registrar serializadores personalizados, pruebe mi respuesta.

En mi caso (Spring 3.2.4 y Jackson 2.3.1), configuración XML para serializador personalizado:

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

se sobrescribió de forma inexplicable de nuevo al valor predeterminado por algo.

Esto funcionó para mí:

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

No <mvc:message-converters>(...)</mvc:message-converters>se necesita ninguna configuración XML ( ) en mi solución.

usuario11153
fuente
6

Estoy usando Spring 4.1.6 y 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>

esto funciona en mi configuración applicationContext.xml

jecyhw
fuente
6

SOLUCION 1

Primera solución de trabajo (probada) útil especialmente cuando se usa @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);
        });
    }

SOLUCION 2

Por supuesto, el enfoque común a continuación también funciona (también funciona 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));
    }

¿Por qué el uso de @EnableWebMvc es un problema?

@EnableWebMvc está usando DelegatingWebMvcConfigurationque se extiende WebMvcConfigurationSupportque hace esto:

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

lo que significa que no hay forma de inyectar el tuyo propio ObjectMappercon el propósito de prepararlo para ser usado para crear el predeterminado MappingJackson2HttpMessageConverteral usar @EnableWebMvc.

adrhc
fuente
¡Excelente! En realidad, RepositoryRestMvcConfiguration, parte de spring-data-rest-webmvc: 3.2.3 no usa ObjectMapper creado por Spring y solo objectMapper personalizado con módulos no funciona de inmediato. El primer enfoque es real y reemplaza ObjectMapper personalizado por spring-data-rest-webmvc. ¡Gracias!
Valtoni Boaventura
Tuve un problema en el que no podía personalizar mi ObjectMapper que era miserable b / c de EnableWebMvc. Me alegro mucho de haberme encontrado con esto.
Evan White
3

En Spring Boot 2.2.xnecesitas configurarlo así:

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

Kotlin:

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

Por encima de Spring 4, no es necesario configurar MappingJacksonHttpMessageConvertersi solo tiene la intención de configurar ObjectMapper.

(la configuración MappingJacksonHttpMessageConverterhará que pierda otro MessageConverter)

Solo necesitas hacer:

public class MyObjectMapper extends ObjectMapper {

    private static final long serialVersionUID = 4219938065516862637L;

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

Y en su configuración de Spring, cree este bean:

@Bean 
public MyObjectMapper myObjectMapper() {        
    return new MyObjectMapper();
}
Sam YC
fuente
No funciona - - customObjectMapper: definido por el método 'customObjectMapper' en el recurso de ruta de clase [com / Config.class] -_halObjectMapper: defined in null
Angshuman Agarwal
1
Y esto solo funcionará con Spring Boot. No es simple Spring Web.
Lirón
2

Para configurar un conversor de mensajes en spring-web simple, en este caso para habilitar Java 8 JSR-310 JavaTimeModule, primero debe implementar WebMvcConfigureren su @Configurationclase y luego anular el configureMessageConvertersmétodo:

@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 esta manera, puede registrar cualquier personalizado definido ObjectMapperen una configuración de Spring basada en Java.

Lirón
fuente
Preferiría tener el ObjectMapper inyectado y configurado a través de Spring Boot.
Constantino Cronemberger
1
Claro, si usa Spring Boot. Esta no era una pregunta de Spring Boot.
Lirón
Sí, es interesante notar que al usar Spring Web con Spring Boot no usa el ObjectMapper provisto por Spring Boot y es por eso que terminé en esta pregunta que me ayudó.
Constantino Cronemberger
¡Esto borraría todos los convertidores de mensajes predeterminados! Estoy bastante seguro de que nadie quiere esto. Vea mi respuesta para la solución.
adrhc
Bueno, si desea extender los convertidores de mensajes, la solución correcta sería anular extendMessageConverters, según la documentación de Spring . Pero no lo he necesitado, solo necesito un personalizado MappingJackson2HttpMessageConverter.
Lirón
1

Estoy usando Spring 3.2.4 y Jackson FasterXML 2.1.1.

He creado un JacksonObjectMapper personalizado que funciona con anotaciones explícitas para cada atributo de los Objetos asignados:

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

Luego, esto se instancia en la configuración de contexto ( 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>

¡Esto funciona bien!

Carlos AG
fuente