ObjectMapper im Frühjahr konfigurieren

89

Mein Ziel ist es, die zu konfigurieren objectMapper , dass nur Elemente serialisiert werden, mit denen Anmerkungen versehen sind @JsonProperty.

Um dies zu tun, folgte ich diesem Zweck habe Erklärung der angegeben ist, wie der Objectmapper konfiguriert wird.

Ich habe den benutzerdefinierten Objectmapper wie beschrieben eingefügt hier .

Allerdings wenn die Klasse NumbersOfNewEvents serialisiert wird, enthält sie jedoch weiterhin alle Attribute im JSON.

Hat jemand einen Hinweis? Danke im Voraus

Jackson 1.8.0 Frühling 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
quelle

Antworten:

132

Mit Spring Boot (1.2.4) und Jackson (2.4.6) hat die folgende annotationsbasierte Konfiguration für mich funktioniert.

@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
quelle
3
Spring unterstützt das Anpassen einiger Eigenschaften von Jackson in application.properties: docs.spring.io/spring-boot/docs/current/reference/html/…
NangSaigon
9
Hallo Leute. Ich hatte das Problem, dass beim Deklarieren von Bean mit dem Namen objectMapper in der Java-Konfiguration, wie im Beispiel gezeigt, diese Bean von Spring Boot ignoriert wird, da Bean mit diesem Namen bereits im System registriert ist, selbst wenn Sie \ @ platzieren Primäre Anmerkung dazu. Um dieses Problem zu beheben, müssen Sie Bean mit einem anderen Namen registrieren, z. B. jsonObjectMapper, und es mit \ @Primary Annotation
Demwis
Bitte beziehen Sie sich auf meine Antwort, wenn Sie Spring Boot nicht verwenden.
Siebenschläfer
Eine Frage: Sie müssen die Klasse benennen JacksonConfiguration? Es ist eine automatische Möglichkeit, Spring Boot anzuweisen, diese Klasse als Konfigurationsklasse für Jackson zu verwenden.
Marco Sulla
Funktioniert es wirklich auch bei der Verwendung @EnableWebMvc? Ich bezweifle, weil ... siehe in meiner Antwort : Warum ist die Verwendung von @EnableWebMvc ein Problem?
Adrrc
25

Es kann sein, dass ich Spring 3.1 verwende (anstelle von Spring 3.0.5, wie in Ihrer Frage angegeben), aber Steve Eastwoods Antwort hat bei mir nicht funktioniert. Diese Lösung funktioniert für Spring 3.1:

In Ihrem Frühlings-XML-Kontext:

<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
quelle
Dies half bei der Verwendung von Jackson 2.x mit Spring 3.1: stackoverflow.com/questions/10420040/…
Aram Kocharyan
Nützlich! Musste diesen Ansatz anwenden, um meinen benutzerdefinierten ObjectMapper beim Upgrade von Spring 3.0 auf 3.2 zu registrieren. Hatte zuvor beim Definieren einer MappingJacksonJsonView-Bean eine objectMapper-Eigenschaft festgelegt, aber das funktionierte nicht mehr. Dies ist mit Jackson 1.7
David Easley
18

Das gibt es schon org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBeanlange. Ab Version 1.2 von Spring Boot gibt esorg.springframework.http.converter.json.Jackson2ObjectMapperBuilder für Java Config.

In String Boot kann die Konfiguration so einfach sein wie:

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.propertiesoder ein Java-Code in der @ConfigurationKlasse:

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

Sehen:

Gavenkoa
quelle
2
Dies funktioniert in den meisten Anwendungsfällen. Die Jackson-Konfiguration von Spring Boot wird jedoch ignoriert, wenn Sie die Annotation @EnableWebMvc verwenden (getestet in Spring Boot 2.0.3).
John
1
Obwohl ich nicht getestet habe, bin ich mir sicher, dass dies mit @EnableWebMvc nicht funktioniert (trotz Verwendung oder Nicht-Spring-Boot). Warum? Siehe WebMvcConfigurationSupport (DelegatingWebMvcConfiguration erweitert WebMvcConfigurationSupport) : Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json().
Adrrc
16

Ich habe dies mit Jackson 2.x und Spring 3.1.2+ verwendet

servlet-context.xml:

Beachten Sie, dass das Stammelement ist <beans:beans>, sodass Sie je nach Einrichtung möglicherweise einige dieser Elemente entfernen beansund hinzufügen müssen mvc.

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

Ich habe die Lösung jetzt basierend auf https://github.com/FasterXML/jackson-module-hibernate gefunden

Ich habe den Objekt-Mapper erweitert und die Attribute im geerbten Konstruktor hinzugefügt.

Dann wird der neue Objekt-Mapper als Bean registriert.

<!-- 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
quelle
Vielen Dank für die Lösung! Ich habe über vier Stunden damit verbracht, mich um <annotationsgetrieben> zu kümmern , es hat einfach die Konverter nicht aufgenommen. Versuchte deine Antwort, endlich funktioniert es!
Snowindy
1
Um Anmerkungen zu verwenden, können Sie einfach Ihren benutzerdefinierten Mapper mit Anmerkungen versehen, der sich ObjectMapperals @Componentund erstreckt @Primary. Dies ist der im Spring Reference Guide empfohlene Ansatz, der für mich gut funktioniert hat.
OlgaMaciaszek
9

Wenn Sie einen benutzerdefinierten ObjectMapper zum Registrieren benutzerdefinierter Serializer hinzufügen möchten, versuchen Sie meine Antwort.

In meinem Fall (Spring 3.2.4 und Jackson 2.3.1), XML-Konfiguration für benutzerdefinierten Serializer:

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

wurde auf unerklärliche Weise von etwas auf Standard zurückgeschrieben.

Das hat bei mir funktioniert:

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

In <mvc:message-converters>(...)</mvc:message-converters>meiner Lösung ist keine XML-Konfiguration ( ) erforderlich.

user11153
quelle
6

Ich verwende Spring 4.1.6 und 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>

Dies funktioniert bei meiner applicationContext.xml-Konfigration

jecyhw
quelle
6

LÖSUNG 1

Erste funktionierende Lösung (getestet), die besonders bei Verwendung von @EnableWebMvc nützlich ist:

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

LÖSUNG 2

Natürlich funktioniert auch der folgende allgemeine Ansatz (auch mit @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));
    }

Warum ist die Verwendung von @EnableWebMvc ein Problem?

@EnableWebMvc verwendet, DelegatingWebMvcConfigurationwas erweitert, WebMvcConfigurationSupportwas dies tut:

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

Dies bedeutet, dass es keine Möglichkeit gibt, Ihre eigenen ObjectMapperzu injizieren , um sie für die Erstellung der Standardeinstellung MappingJackson2HttpMessageConverterbei Verwendung von @EnableWebMvc vorzubereiten.

adrhc
quelle
Toll! Tatsächlich verwendet RepositoryRestMvcConfiguration, Teil von spring-data-rest-webmvc: 3.2.3, keinen von Spring erstellten ObjectMapper, und nur benutzerdefinierter objectMapper mit Modulen funktioniert nicht sofort. Der erste Ansatz ist aktuell und ersetzt ObjectMapper, der von spring-data-rest-webmvc angepasst wurde. Vielen Dank!
Valtoni Boaventura
Ich hatte ein Problem, bei dem ich meinen ObjectMapper nicht anpassen konnte, der von EnableWebMvc miserabel war. Ich bin so froh, dass ich darüber gestolpert bin.
Evan White
3

In Spring Boot müssen 2.2.xSie es folgendermaßen konfigurieren:

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

Kotlin:

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

Oberhalb von Spring 4 ist keine Konfiguration erforderlich, MappingJacksonHttpMessageConverterwenn Sie nur die Konfiguration beabsichtigen ObjectMapper.

(Durch das Konfigurieren MappingJacksonHttpMessageConverterverlieren Sie andere MessageConverter.)

Sie müssen nur tun:

public class MyObjectMapper extends ObjectMapper {

    private static final long serialVersionUID = 4219938065516862637L;

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

Erstellen Sie in Ihrer Spring-Konfiguration diese Bean:

@Bean 
public MyObjectMapper myObjectMapper() {        
    return new MyObjectMapper();
}
GMsoF
quelle
Funktioniert nicht - - customObjectMapper: definiert durch die Methode 'customObjectMapper' in der Klassenpfadressource [com / Config.class] -_halObjectMapper: defined in null
Angshuman Agarwal
1
Und das funktioniert nur mit Spring Boot. Nicht einfach Spring Web.
Siebenschläfer
2

Um eine Nachricht Konverter im Klarfeder Web zu konfigurieren, in diesem Fall die Java 8 JSR-310 JavaTimeModule zu aktivieren, müssen Sie zuerst implementieren WebMvcConfigurerin Ihrer @ConfigurationKlasse und dann setzen die Zuweisung configureMessageConvertersMethode:

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

Auf diese Weise können Sie alle benutzerdefinierten Elemente registrieren, die ObjectMapperin einer Java-basierten Spring-Konfiguration definiert sind.

Siebenschläfer
quelle
Ich würde es vorziehen, den ObjectMapper über Spring Boot injizieren und konfigurieren zu lassen.
Constantino Cronemberger
1
Sicher, wenn Sie Spring Boot verwenden. Dies war keine Spring Boot-Frage.
Siebenschläfer
Ja, interessant zu bemerken, dass bei der Verwendung von Spring Web mit Spring Boot nicht der von Spring Boot bereitgestellte ObjectMapper verwendet wird, und deshalb bin ich auf diese Frage gestoßen, die mir geholfen hat.
Constantino Cronemberger
Dies würde alle Standardnachrichtenkonverter löschen! Ich bin mir ziemlich sicher, dass das niemand will. Siehe meine Antwort für die Lösung.
Adrrc
Wenn Sie die Nachrichtenkonverter erweitern möchten, ist die richtige Lösung das Überschreiben extendMessageConvertersgemäß der Spring-Dokumentation . Aber ich hatte keine Notwendigkeit dafür, weil ich nur eine maßgeschneiderte brauche MappingJackson2HttpMessageConverter.
Siebenschläfer
1

Ich verwende Spring 3.2.4 und Jackson FasterXML 2.1.1.

Ich habe einen benutzerdefinierten JacksonObjectMapper erstellt , der mit expliziten Anmerkungen für jedes Attribut der zugeordneten Objekte arbeitet:

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

Dies wird dann in der Kontextkonfiguration ( servlet-context.xml ) instanziiert :

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

Das funktioniert gut!

Carlos AG
quelle