Mengonfigurasi ObjectMapper di Spring

91

tujuan saya adalah untuk mengkonfigurasi objectMappersedemikian rupa sehingga hanya elemen yang diserialisasikan dengan yang dijelaskan @JsonProperty.

Untuk melakukannya saya mengikuti penjelasan ini yang mengatakan bagaimana mengkonfigurasi objectmapper.

Saya menyertakan objectmapper kustom seperti yang dijelaskan di sini .

Namun ketika kelas NumbersOfNewEventstersebut dibuat berseri itu masih berisi semua atribut di json.

Apakah ada yang punya petunjuk? Terima kasih sebelumnya

Jackson 1.8.0 spring 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
sumber

Jawaban:

136

Dengan menggunakan Spring Boot (1.2.4) dan Jackson (2.4.6) konfigurasi berbasis anotasi berikut berfungsi untuk saya.

@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
sumber
3
Dukungan Spring menyesuaikan beberapa properti Jackson di application.properties: docs.spring.io/spring-boot/docs/current/reference/html/…
NangSaigon
10
Hai teman-teman. Saya menghadapi masalah ketika Anda mendeklarasikan kacang dengan nama objectMapper di konfigurasi java, seperti yang ditunjukkan pada contoh, kacang ini diabaikan oleh Spring Boot, karena kacang dengan nama tersebut sudah terdaftar di sistem, bahkan jika Anda menempatkan \ @ Anotasi utama di atasnya. Untuk memperbaiki masalah ini, Anda perlu mendaftarkan kacang dengan nama yang berbeda, misalnya jsonObjectMapper, dan menandainya dengan \ @Primary annotation
Demwis
Silakan lihat jawaban saya jika Anda tidak menggunakan Spring Boot.
Dormouse
Sebuah pertanyaan: Anda harus menyebutkan kelasnya JacksonConfiguration? Ini adalah cara otomatis untuk menginstruksikan Spring Boot menggunakan kelas ini sebagai kelas konfigurasi untuk Jackson?
Marco Sulla
Apakah ini benar-benar berfungsi saat menggunakan juga @EnableWebMvc? Saya ragu karena ... lihat jawaban saya : Mengapa penggunaan @EnableWebMvc menjadi masalah?
adrhc
25

Mungkin karena saya menggunakan Spring 3.1 (bukan Spring 3.0.5 sebagai pertanyaan Anda), tetapi jawaban Steve Eastwood tidak berhasil untuk saya. Solusi ini berfungsi untuk Spring 3.1:

Dalam konteks xml musim semi Anda:

<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
sumber
Ini membantu untuk menggunakan Jackson 2.x dengan Spring 3.1: stackoverflow.com/questions/10420040/…
Aram Kocharyan
Berguna! Harus mengadopsi pendekatan ini untuk mendaftarkan ObjectMapper kustom saya pada peningkatan dari Spring 3.0 ke 3.2. Sebelumnya telah menyetel properti objectMapper saat mendefinisikan kacang MappingJacksonJsonView, tetapi itu tidak lagi berfungsi. Ini dengan Jackson 1.7
David Easley
20

Ada org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBeanuntuk waktu yang lama. Mulai dari 1.2 rilis Spring Boot ada org.springframework.http.converter.json.Jackson2ObjectMapperBuilderuntuk Java Config.

Dalam konfigurasi String Boot bisa sesederhana:

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

di classpath:application.propertiesatau beberapa kode Java di @Configurationkelas:

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

Lihat:

gavenkoa.dll
sumber
2
Ini berfungsi untuk sebagian besar kasus penggunaan, namun konfigurasi jackson Spring Boot diabaikan jika Anda menggunakan anotasi @EnableWebMvc (diuji di Spring Boot 2.0.3).
Yohanes
1
Meskipun saya tidak menguji, saya yakin itu tidak akan berfungsi dengan @EnableWebMvc (meskipun menggunakan atau tidak boot musim semi). Mengapa? melihat WebMvcConfigurationSupport (DelegatingWebMvcConfiguration meluas WebMvcConfigurationSupport): Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json().
adrhc
16

Saya telah menggunakan ini dengan Jackson 2.x dan Spring 3.1.2+

servlet-context.xml:

Perhatikan bahwa elemen root adalah <beans:beans>, jadi Anda mungkin perlu menghapus beansdan menambahkan mvcbeberapa elemen ini tergantung pada pengaturan Anda.

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

Saya menemukan solusinya sekarang berdasarkan https://github.com/FasterXML/jackson-module-hibernate

Saya memperluas pemeta objek dan menambahkan atribut dalam konstruktor yang diwarisi.

Kemudian pemeta objek baru didaftarkan sebagai kacang.

<!-- 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
sumber
Terima kasih banyak atas solusinya! Saya telah menghabiskan lebih dari empat jam untuk menggali <annotation-driven> , itu sama sekali tidak menemukan konverternya. Mencoba jawaban Anda, akhirnya berhasil!
snowindy
1
Untuk menggunakan anotasi, Anda bisa membuat anotasi Mapper kustom Anda yang meluas ObjectMappersebagai @Componentdan @Primary. Ini adalah pendekatan yang direkomendasikan dalam Panduan Referensi Musim Semi dan telah bekerja dengan baik untuk saya.
OlgaMaciaszek
9

Jika Anda ingin menambahkan ObjectMapper kustom untuk mendaftarkan serializers kustom, coba jawaban saya.

Dalam kasus saya (Spring 3.2.4 dan Jackson 2.3.1), konfigurasi XML untuk serializer khusus:

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

dengan cara yang tidak dapat dijelaskan ditimpa kembali ke default oleh sesuatu.

Ini berhasil untuk saya:

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

Tidak ada konfigurasi XML ( <mvc:message-converters>(...)</mvc:message-converters>) yang diperlukan dalam solusi saya.

pengguna11153
sumber
6

Saya menggunakan Spring 4.1.6 dan 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>

ini berfungsi di konfigurasi applicationContext.xml saya

jecyhw
sumber
6

SOLUSI 1

Solusi kerja pertama (teruji) berguna terutama saat menggunakan @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);
        });
    }

SOLUSI 2

Tentu saja pendekatan umum di bawah ini juga berfungsi (juga bekerja dengan @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));
    }

Mengapa penggunaan @EnableWebMvc menjadi masalah?

@EnableWebMvc menggunakan DelegatingWebMvcConfigurationextends WebMvcConfigurationSupportyang melakukan ini:

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

yang berarti bahwa tidak ada cara untuk memasukkan milik Anda sendiri ObjectMapperdengan tujuan mempersiapkannya untuk digunakan dalam membuat default MappingJackson2HttpMessageConvertersaat menggunakan @EnableWebMvc.

adrhc
sumber
Bagus! Sebenarnya RepositoryRestMvcConfiguration, bagian dari spring-data-rest-webmvc: 3.2.3 tidak menggunakan ObjectMapper yang dibuat oleh Spring dan hanya objectMapper khusus dengan modul yang tidak berfungsi secara out-of-the-box. Pendekatan pertama aktual dan ganti ObjectMapper yang disesuaikan dengan spring-data-rest-webmvc. Terima kasih!
Valtoni Boaventura
Saya memiliki masalah di mana saya tidak bisa menyesuaikan ObjectMapper saya yang menyedihkan karena EnableWebMvc. Senang sekali saya menemukan ini.
Evan White
3

Di Spring Boot 2.2.xAnda perlu mengkonfigurasinya seperti ini:

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

Kotlin:

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

Di atas Spring 4, Anda tidak perlu melakukan konfigurasi MappingJacksonHttpMessageConverterjika Anda hanya bermaksud untuk mengkonfigurasi ObjectMapper.

(konfigurasi MappingJacksonHttpMessageConverterakan menyebabkan Anda kehilangan MessageConverter lainnya)

Anda hanya perlu melakukan:

public class MyObjectMapper extends ObjectMapper {

    private static final long serialVersionUID = 4219938065516862637L;

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

Dan dalam konfigurasi Spring Anda, buat kacang ini:

@Bean 
public MyObjectMapper myObjectMapper() {        
    return new MyObjectMapper();
}
Sam YC
sumber
Tidak berfungsi - - customObjectMapper: ditentukan oleh metode 'customObjectMapper' di sumber jalur kelas [com / Config.class] -_halObjectMapper: defined in null
Angshuman Agarwal
1
Dan ini hanya akan bekerja dengan Spring Boot. Bukan Web Spring biasa.
Dormouse
2

Untuk mengonfigurasi konverter pesan di web pegas biasa, dalam hal ini untuk mengaktifkan Java 8 JSR-310 JavaTimeModule, Anda harus mengimplementasikannya WebMvcConfigurerdi @Configurationkelas terlebih dahulu lalu mengganti configureMessageConvertersmetode:

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

Seperti ini, Anda dapat mendaftarkan kustom apa pun yang ditentukan ObjectMapperdalam konfigurasi Spring berbasis Java.

Orang yg suka tidur
sumber
Saya lebih suka ObjectMapper diinjeksi dan dikonfigurasi melalui Spring Boot.
Constantino Cronemberger
1
Tentu, jika Anda menggunakan Spring Boot. Ini bukanlah pertanyaan tentang Sepatu Musim Semi.
Dormouse
Ya, menarik untuk diperhatikan bahwa saat menggunakan Spring Web dengan Spring Boot, ia tidak menggunakan ObjectMapper yang disediakan oleh Spring Boot dan itulah mengapa saya berakhir dengan pertanyaan ini yang membantu saya.
Constantino Cronemberger
Ini akan menghapus semua pengonversi pesan default! Saya cukup yakin tidak ada yang menginginkan ini. Lihat jawaban saya untuk solusinya.
adrhc
Nah, jika Anda ingin memperluas konverter pesan, solusi yang tepat adalah menimpa extendMessageConverters, sesuai dokumentasi Spring . Tetapi saya tidak membutuhkannya, karena saya hanya perlu yang disesuaikan MappingJackson2HttpMessageConverter.
Dormouse
1

Saya menggunakan Spring 3.2.4 dan Jackson FasterXML 2.1.1.

Saya telah membuat JacksonObjectMapper khusus yang berfungsi dengan anotasi eksplisit untuk setiap atribut Objek yang dipetakan:

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

Kemudian ini dibuat dalam konteks-konfigurasi ( servlet-konteks.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>

Ini bekerja dengan baik!

Carlos AG
sumber