การกำหนดอนุกรมแบบกำหนดเองของ Jackson JSON สำหรับบางฟิลด์


97

มีวิธีใช้ Jackson JSON Processor เพื่อทำการซีเรียลไลเซชันระดับฟิลด์แบบกำหนดเองหรือไม่? ตัวอย่างเช่นฉันต้องการเรียน

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

อนุกรมกับ JSON ต่อไปนี้:

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

หมายเหตุว่าอายุ = 25 จะถูกเข้ารหัสเป็นจำนวนขณะ favoriteNumber = 123 ถูกเข้ารหัสเป็นสตริง ออกจากมาร์แชลกล่องแจ็คสันintเป็นตัวเลข ในกรณีนี้ฉันต้องการให้ favoriteNumber เข้ารหัสเป็นสตริง


1
ฉันเขียนโพสต์เกี่ยวกับHow to Write a Custom Serializer with Jacksonซึ่งอาจเป็นประโยชน์กับบางคน
Sam Berry

คำตอบ:


110

คุณสามารถติดตั้ง Serializer แบบกำหนดเองได้ดังนี้:

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 ควรจัดการ autoboxing จากintถึงIntegerให้คุณ


4
Jackson-databind (อย่างน้อย 2.1.3) มี ToStringSerializer พิเศษอยู่แล้วดูคำตอบของฉัน
werupokz

@KevinBowersox คุณช่วยแก้ปัญหา deserializingของฉันได้ไหม?
JJD


มีวิธีที่น่ากลัวน้อยกว่านี้หรือไม่? ชอบPerson implements ToJson?
jameshfisher

1
ในกรณีของฉันมันล้มเหลวในas=String.classบางส่วนเนื่องจากประเภทที่ฉันใช้ @ kevin-bowersox ฉันขอแนะนำให้อัปเดตความคิดเห็นของคุณตามสิ่งที่ @GarethLatty กล่าว
เบิร์ต

56

Jackson-databind (อย่างน้อย 2.1.3) ให้พิเศษToStringSerializer( com.fasterxml.jackson.databind.ser.std.ToStringSerializer)

ตัวอย่าง:

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

3
สิ่งที่เกี่ยวกับการย้อนกลับที่ต้องแปลง String เป็น int? ฉันไม่เห็น ToIntSerializer.class
jEremyB

@jEremyB คุณอาจต้องเขียนdeserializer แบบกำหนดเอง
Drew Stephens

ToStringSerializer ใช้งานได้ แต่ FloatSerializer นำข้อความนี้: ไม่สามารถเขียนเนื้อหา: java.lang.Integer ไม่สามารถส่งไปที่ java.lang.Float
Arnie Schwarzvogel

13

jackson-annotations ให้@JsonFormatซึ่งสามารถจัดการการปรับแต่งได้มากมายโดยไม่จำเป็นต้องเขียน serializer แบบกำหนดเอง

ตัวอย่างเช่นการขอSTRINGรูปร่างสำหรับเขตข้อมูลที่มีประเภทตัวเลขจะส่งออกค่าตัวเลขเป็นสตริง

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

จะทำให้ได้ผลลัพธ์ที่ต้องการ

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

11

เพิ่ม@JsonPropertygetter ที่มีคำอธิบายประกอบซึ่งส่งคืน a StringสำหรับfavoriteNumberฟิลด์:

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

ในกรณีที่คุณไม่ต้องการสร้างมลพิษให้กับโมเดลของคุณด้วยคำอธิบายประกอบและต้องการดำเนินการแบบกำหนดเองคุณสามารถใช้มิกซ์อิน

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

ลบล้างอายุ:

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

ทำสิ่งที่คุณต้องการตามอายุ:

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

ด้วยความช่วยเหลือของ@JsonViewเราสามารถตัดสินใจเลือกฟิลด์ของคลาสโมเดลที่จะทำให้เป็นอนุกรมซึ่งตรงตามเกณฑ์ขั้นต่ำ (เราต้องกำหนดเกณฑ์) เช่นเราสามารถมีคลาสคอร์หนึ่งคลาสที่มีคุณสมบัติ 10 อย่าง แต่มีเพียง 5 คุณสมบัติเท่านั้นที่สามารถทำให้เป็นอนุกรมซึ่งจำเป็นสำหรับลูกค้า เท่านั้น

กำหนดมุมมองของเราโดยสร้างคลาสต่อไปนี้:

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

คลาสโมเดลที่มีคำอธิบายประกอบพร้อมมุมมอง:

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

ตอนนี้เราต้องเขียนตัวแปลง json ที่กำหนดเองโดยเพียงแค่ขยายคลาส HttpMessageConverter จากฤดูใบไม้ผลิเป็น:

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

}

ตอนนี้จำเป็นต้องบอกให้ spring ใช้การแปลง json ที่กำหนดเองนี้โดยใส่สิ่งนี้ใน 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>

นั่นคือวิธีที่คุณจะสามารถตัดสินใจได้ว่าช่องใดที่จะทำให้อนุกรม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.