Konfigurowanie ObjectMappera na wiosnę

91

moim celem jest skonfigurowanie elementu objectMapperw taki sposób, aby serializował tylko elementy, które są oznaczone adnotacją @JsonProperty.

Aby to zrobić, postępowałem zgodnie z tym wyjaśnieniem, które mówi, jak skonfigurować obiekt mapowania.

Dołączyłem niestandardowy obiekt mapowania zgodnie z opisem tutaj .

Jednak gdy klasa NumbersOfNewEventsjest serializowana, nadal zawiera wszystkie atrybuty w pliku json.

Czy ktoś ma podpowiedź? Z góry dziękuję

Jackson 1.8.0, wiosna 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
źródło

Odpowiedzi:

136

Używając Spring Boot (1.2.4) i Jacksona (2.4.6), działała dla mnie następująca konfiguracja oparta na adnotacjach.

@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
źródło
3
Spring obsługuje dostosowywanie niektórych właściwości Jacksona w application.properties: docs.spring.io/spring-boot/docs/current/reference/html/…
NangSaigon
10
Cześć ludzie. Spotkałem się z problemem, że kiedy deklarujesz bean o nazwie objectMapper w konfiguracji java, tak jak jest to pokazane na przykładzie, ten bean jest ignorowany przez Spring Boot, ponieważ bean o takiej nazwie jest już zarejestrowany w systemie, nawet jeśli umieścisz \ @ Główna adnotacja na nim. Aby rozwiązać ten problem, musisz zarejestrować Bean z inną nazwą, np jsonObjectMapper, oraz oznaczyć je \ @Primary adnotacji
Demwis
Proszę zapoznać się z moją odpowiedzią, jeśli nie używasz Spring Boot.
Dormouse
Pytanie: musisz nazwać klasę JacksonConfiguration? To automagiczny sposób, aby poinstruować Spring Boot, aby używał tej klasy jako klasy konfiguracyjnej dla Jacksona?
Marco Sulla
Czy to naprawdę działa, gdy używasz również @EnableWebMvc? Wątpię, ponieważ ... zobacz w mojej odpowiedzi : Dlaczego używanie @EnableWebMvc jest problemem?
adrhc
25

Może to być spowodowane tym, że używam Spring 3.1 (zamiast Spring 3.0.5, jak określiło twoje pytanie), ale odpowiedź Steve'a Eastwooda nie zadziałała dla mnie. To rozwiązanie działa w wersji Spring 3.1:

W Twoim kontekście wiosennego 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
źródło
Pomogło to w używaniu Jacksona 2.x ze Spring 3.1: stackoverflow.com/questions/10420040/ ...
Aram Kocharyan
Przydatny! Musiałem przyjąć to podejście, aby zarejestrować mój niestandardowy obiekt ObjectMapper podczas aktualizacji ze Spring 3.0 do 3.2. Wcześniej ustawiano właściwość objectMapper podczas definiowania komponentu bean MappingJacksonJsonView, ale to już nie działało. To jest z Jacksonem 1.7
Davidem
20

Jest org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBeanod dawna. Począwszy od wersji 1.2 Spring Boot dostępna jest org.springframework.http.converter.json.Jackson2ObjectMapperBuilderJava Config.

W konfiguracji String Boot może być tak prosta, jak:

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.propertieslub jakiś kod Java w @Configurationklasie:

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

Widzieć:

gavenkoa
źródło
2
Działa to w większości przypadków użycia, jednak konfiguracja Spring Boot jackson jest ignorowana, jeśli używasz adnotacji @EnableWebMvc (testowane w Spring Boot 2.0.3).
John
1
Chociaż nie testowałem, jestem pewien, że to nie zadziała z @EnableWebMvc (pomimo używania lub braku rozruchu sprężynowego). Czemu? patrz WebMvcConfigurationSupport (DelegatingWebMvcConfiguration rozciąga WebMvcConfigurationSupport) Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json().
adrhc
16

Użyłem tego z Jackson 2.x i Spring 3.1.2+

servlet-context.xml:

Zwróć uwagę, że element główny to <beans:beans>, więc może być konieczne usunięcie beansi dodanie mvcniektórych z tych elementów w zależności od konfiguracji.

    <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
źródło
10

Znalazłem rozwiązanie teraz oparte na https://github.com/FasterXML/jackson-module-hibernate

Rozszerzyłem mapowanie obiektów i dodałem atrybuty w odziedziczonym konstruktorze.

Następnie nowy element mapujący obiekty jest rejestrowany jako komponent 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
źródło
Bardzo dziękuję za rozwiązanie! Spędziłem ponad cztery godziny na kopaniu wokół <annotation-driven> , po prostu nie odebrałem konwerterów. Wypróbowałem twoją odpowiedź, w końcu działa!
snowindy,
1
Aby użyć adnotacji, możesz po prostu dodać adnotację do swojego niestandardowego programu Mapper, który rozszerza się ObjectMapperna @Componenti @Primary. Jest to podejście zalecane w Przewodniku po sprężynach i dobrze mi się sprawdziło.
OlgaMaciaszek
9

Jeśli chcesz dodać niestandardowy ObjectMapper do rejestrowania niestandardowych serializatorów, wypróbuj moją odpowiedź.

W moim przypadku (Spring 3.2.4 i Jackson 2.3.1), konfiguracja XML dla niestandardowego serializatora:

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

został przez coś w niewyjaśniony sposób nadpisany z powrotem do wartości domyślnej.

To zadziałało dla mnie:

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

<mvc:message-converters>(...)</mvc:message-converters>W moim rozwiązaniu nie jest wymagana żadna konfiguracja XML ( ).

user11153
źródło
6

Używam Spring 4.1.6 i 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>

działa to w mojej konfiguracji applicationContext.xml

jecyhw
źródło
6

ROZWIĄZANIE 1

Pierwsze działające rozwiązanie (przetestowane) przydatne zwłaszcza przy korzystaniu z @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);
        });
    }

ROZWIĄZANIE 2

Oczywiście wspólne podejście poniżej działa również (działa również z @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));
    }

Dlaczego używanie @EnableWebMvc jest problemem?

@EnableWebMvc używa DelegatingWebMvcConfigurationwhich extends, WebMvcConfigurationSupportco robi to:

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

co oznacza, że ​​nie ma możliwości wstrzyknięcia własnego ObjectMapperw celu przygotowania go do użycia do utworzenia domyślnego MappingJackson2HttpMessageConverterpodczas korzystania z @EnableWebMvc.

adrhc
źródło
Świetny! Właściwie RepositoryRestMvcConfiguration, część spring-data-rest-webmvc: 3.2.3 nie używa ObjectMapper stworzonego przez Spring i tylko niestandardowy obiekt objectMapper z modułami nie działa od razu po zainstalowaniu. Pierwsze podejście jest aktualne i zastąpienie ObjectMapper dostosowanego przez spring-data-rest-webmvc. Dzięki!
Valtoni Boaventura
Miałem problem polegający na tym, że nie mogłem dostosować mojego ObjectMappera, który był nieszczęśliwy b / c EnableWebMvc. Tak się cieszę, że się na to natknąłem.
Evan White
3

W Spring Boot 2.2.xmusisz to skonfigurować w następujący sposób:

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

Kotlin:

@Bean
fun objectMapper(builder: Jackson2ObjectMapperBuilder) = builder.build()
CorayThan
źródło
2

Powyżej Spring 4 nie ma potrzeby konfigurowania, MappingJacksonHttpMessageConverterjeśli zamierzasz tylko konfigurować ObjectMapper.

(konfiguracja MappingJacksonHttpMessageConverterspowoduje utratę innych MessageConverter)

Musisz tylko:

public class MyObjectMapper extends ObjectMapper {

    private static final long serialVersionUID = 4219938065516862637L;

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

A w konfiguracji Spring utwórz tę fasolkę:

@Bean 
public MyObjectMapper myObjectMapper() {        
    return new MyObjectMapper();
}
Sam YC
źródło
Nie działa - - customObjectMapper: zdefiniowany przez metodę „customObjectMapper” w zasobie ścieżki klasy [com / Config.class] -_halObjectMapper: defined in null
Angshuman Agarwal
1
I to zadziała tylko z Spring Boot. Nie zwykły Spring Web.
Dormouse
2

Aby skonfigurować konwerter komunikatów w zwykłym spring-web, w tym przypadku, aby włączyć JavaTimeModule JSR-310 Java 8, musisz najpierw zaimplementować WebMvcConfigurerw swojej @Configurationklasie, a następnie nadpisać configureMessageConvertersmetodę:

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

W ten sposób możesz zarejestrować dowolną niestandardową ObjectMapperkonfigurację Springa opartą na Javie.

Koszatka
źródło
Wolałbym, aby ObjectMapper był wstrzykiwany i konfigurowany przez Spring Boot.
Constantino Cronemberger
1
Jasne, jeśli używasz Spring Boot. To nie było pytanie dotyczące Spring Boot.
Dormouse
Tak, ciekawe jest to, że podczas używania Spring Web ze Spring Boot nie używa on ObjectMapper dostarczonego przez Spring Boot i dlatego trafiłem na to pytanie, które mi pomogło.
Constantino Cronemberger
Spowoduje to wyczyszczenie wszystkich domyślnych konwerterów wiadomości! Jestem prawie pewien, że nikt tego nie chce. Zobacz moją odpowiedź na rozwiązanie.
adrhc
Cóż, jeśli chcesz rozszerzyć konwertery wiadomości, właściwym rozwiązaniem byłoby zastąpienie extendMessageConverters, zgodnie z dokumentacją Springa . Ale nie potrzebowałem tego, ponieważ potrzebuję tylko niestandardowego MappingJackson2HttpMessageConverter.
Dormouse
1

Używam Springa 3.2.4 i Jackson FasterXML 2.1.1.

Utworzyłem niestandardowy JacksonObjectMapper, który działa z wyraźnymi adnotacjami dla każdego atrybutu mapowanych obiektów:

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

Następnie jest tworzony egzemplarz w konfiguracji kontekstu ( 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>

To działa dobrze!

Carlos AG
źródło