Serialisasi kustom Jackson JSON untuk bidang tertentu


97

Apakah ada cara menggunakan Prosesor Jackson JSON untuk melakukan serialisasi tingkat bidang khusus? Misalnya, saya ingin ada kelasnya

public class Person {
    public String name;
    public int age;
    public int favoriteNumber;
}

diserialkan ke JSON berikut:

{ "name": "Joe", "age": 25, "favoriteNumber": "123" }

Perhatikan bahwa age = 25 dikodekan sebagai angka sementara favouriteNumber = 123 dikodekan sebagai string . Di luar kotak Jackson menyusun intnomor. Dalam hal ini saya ingin favouriteNumber dikodekan sebagai string.


1
Saya menulis posting tentang Cara Menulis Serializer Kustom dengan Jackson yang mungkin bisa membantu beberapa.
Sam Berry

Jawaban:


110

Anda dapat menerapkan serializer khusus sebagai berikut:

public class Person {
    public String name;
    public int age;
    @JsonSerialize(using = IntToStringSerializer.class, as=String.class)
    public int favoriteNumber:
}


public class IntToStringSerializer extends JsonSerializer<Integer> {

    @Override
    public void serialize(Integer tmpInt, 
                          JsonGenerator jsonGenerator, 
                          SerializerProvider serializerProvider) 
                          throws IOException, JsonProcessingException {
        jsonGenerator.writeObject(tmpInt.toString());
    }
}

Java harus menangani autoboxing dari intmenjadi Integeruntuk Anda.


4
Jackson-databind (setidaknya 2.1.3) sudah berisi ToStringSerializer khusus, lihat jawaban saya.
werupokz

@KevinBowers Bisakah Anda membantu mengatasi masalah deserialisasi saya ?
JJD


Apakah ada cara yang lebih buruk untuk melakukan ini? Suka Person implements ToJson?
jameshfisher

1
Dalam kasus saya, itu bahkan gagal pada as=String.classbagian, karena tipe yang saya gunakan. @ kevin-bowersox, saya sarankan untuk memperbarui komentar Anda, sejalan dengan apa yang dikatakan @GarethLatty.
Bert

56

Jackson-databind (setidaknya 2.1.3) menyediakan khusus ToStringSerializer( com.fasterxml.jackson.databind.ser.std.ToStringSerializer)

Contoh:

public class Person {
    public String name;
    public int age;
    @JsonSerialize(using = ToStringSerializer.class)
    public int favoriteNumber:
}

3
Bagaimana dengan kebalikannya dimana String perlu diubah menjadi int? Saya tidak melihat ToIntSerializer.class.
jEremyB

@jEremyB Anda mungkin harus menulis deserializer khusus
Drew Stephens

ToStringSerializer berfungsi tetapi FloatSerializer menampilkan pesan ini: Tidak dapat menulis konten: java.lang.Integer tidak dapat dilemparkan ke java.lang.Float
Arnie Schwarzvogel

13

jackson-annotations menyediakan @JsonFormatyang dapat menangani banyak kustomisasi tanpa perlu menulis serializer kustom.

Misalnya, meminta STRINGbentuk untuk bidang dengan tipe numerik akan menghasilkan nilai numerik sebagai string

public class Person {
    public String name;
    public int age;
    @JsonFormat(shape = JsonFormat.Shape.STRING)
    public int favoriteNumber;
}

akan menghasilkan keluaran yang diinginkan

{"name":"Joe","age":25,"favoriteNumber":"123"}

11

Tambahkan @JsonPropertypengambil beranotasi, yang mengembalikan a String, untuk favoriteNumberbidang:

public class Person {
    public String name;
    public int age;
    private int favoriteNumber;

    public Person(String name, int age, int favoriteNumber) {
        this.name = name;
        this.age = age;
        this.favoriteNumber = favoriteNumber;
    }

    @JsonProperty
    public String getFavoriteNumber() {
        return String.valueOf(favoriteNumber);
    }

    public static void main(String... args) throws Exception {
        Person p = new Person("Joe", 25, 123);
        ObjectMapper mapper = new ObjectMapper();
        System.out.println(mapper.writeValueAsString(p)); 
        // {"name":"Joe","age":25,"favoriteNumber":"123"}
    }
}

8

Jika Anda tidak ingin mencemari model Anda dengan anotasi dan ingin melakukan beberapa operasi kustom, Anda dapat menggunakan mixin.

ObjectMapper mapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.setMixInAnnotation(Person.class, PersonMixin.class);
mapper.registerModule(simpleModule);

Ganti usia:

public abstract class PersonMixin {
    @JsonSerialize(using = PersonAgeSerializer.class)
    public String age;
}

Lakukan apapun yang Anda butuhkan dengan usia:

public class PersonAgeSerializer extends JsonSerializer<Integer> {
    @Override
    public void serialize(Integer integer, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeString(String.valueOf(integer * 52) + " months");
    }
}

3

dengan bantuan @JsonView kita dapat memutuskan bidang kelas model untuk membuat serial yang memenuhi kriteria minimal (kita harus menentukan kriteria) seperti kita dapat memiliki satu kelas inti dengan 10 properti tetapi hanya 5 properti yang dapat dibuat bersambung yang diperlukan untuk klien hanya

Tentukan Tampilan kami hanya dengan membuat kelas berikut:

public class Views
{
    static class Android{};
    static class IOS{};
    static class Web{};
}

Kelas model beranotasi dengan tampilan:

public class Demo 
{
    public Demo() 
    {
    }

@JsonView(Views.IOS.class)
private String iosField;

@JsonView(Views.Android.class)
private String androidField;

@JsonView(Views.Web.class)
private String webField;

 // getters/setters
...
..
}

Sekarang kita harus menulis konverter json khusus hanya dengan memperluas kelas HttpMessageConverter dari pegas sebagai:

    public class CustomJacksonConverter implements HttpMessageConverter<Object> 
    {
    public CustomJacksonConverter() 
        {
            super();
        //this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView(Views.ClientView.class));
        this.delegate.getObjectMapper().configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true);
        this.delegate.getObjectMapper().setSerializationInclusion(Include.NON_NULL);

    }

    // a real message converter that will respond to methods and do the actual work
    private MappingJackson2HttpMessageConverter delegate = new MappingJackson2HttpMessageConverter();

    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        return delegate.canRead(clazz, mediaType);
    }

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return delegate.canWrite(clazz, mediaType);
    }

    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return delegate.getSupportedMediaTypes();
    }

    @Override
    public Object read(Class<? extends Object> clazz,
            HttpInputMessage inputMessage) throws IOException,
            HttpMessageNotReadableException {
        return delegate.read(clazz, inputMessage);
    }

    @Override
    public void write(Object obj, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException 
    {
        synchronized(this) 
        {
            String userAgent = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("userAgent");
            if ( userAgent != null ) 
            {
                switch (userAgent) 
                {
                case "IOS" :
                    this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView(Views.IOS.class));
                    break;
                case "Android" :
                    this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView(Views.Android.class));
                    break;
                case "Web" :
                    this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView( Views.Web.class));
                    break;
                default:
                    this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView( null ));
                    break;
                }
            }
            else
            {
                // reset to default view
                this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView( null ));
            }
            delegate.write(obj, contentType, outputMessage);
        }
    }

}

Sekarang ada kebutuhan untuk memberitahu spring untuk menggunakan custom json convert ini hanya dengan meletakkan ini di dispatcher-servlet.xml

<mvc:annotation-driven>
        <mvc:message-converters register-defaults="true">
            <bean id="jsonConverter" class="com.mactores.org.CustomJacksonConverter" >
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

Begitulah cara Anda dapat memutuskan bidang mana yang akan dibuat serial.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.