Jackson JSON tùy chỉnh tuần tự hóa cho các trường nhất định


93

Có cách nào sử dụng Bộ xử lý Jackson JSON để thực hiện tuần tự hóa cấp trường tùy chỉnh không? Ví dụ, tôi muốn có lớp học

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

tuần tự đến JSON sau:

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

Lưu ý rằng age = 25 được mã hóa dưới dạng số trong khi favouriteNumber = 123 được mã hóa dưới dạng chuỗi . Rõ ràng là Jackson thống nhất intmột số. Trong trường hợp này, tôi muốn số yêu thích được mã hóa thành một chuỗi.


1
Tôi đã viết một bài về Cách viết Bộ nối tiếp tùy chỉnh với Jackson có thể hữu ích cho một số người.
Sam Berry

Câu trả lời:


106

Bạn có thể triển khai một bộ nối tiếp tùy chỉnh như sau:

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 sẽ xử lý tự động hộp từ intđến Integercho bạn.


3
Jackson-databind (ít nhất 2.1.3) đã chứa ToStringSerializer đặc biệt, hãy xem câu trả lời của tôi.
werupokz

@KevinBowersox Bạn có thể vui lòng giúp tôi với sự cố deserializing của tôi không?
JJD


Có cách nào ít khủng khiếp hơn để làm điều này không? Như Person implements ToJsonthế nào?
jameshfisher

1
Trong trường hợp của tôi, nó thậm chí không thành công as=String.classmột phần, do các loại tôi đã sử dụng. @ kevin-bowersox, tôi khuyên bạn nên cập nhật nhận xét của bạn, phù hợp với những gì @GarethLatty đã nói.
Bert

54

Jackson-databind (ít nhất 2.1.3) cung cấp đặc biệt ToStringSerializer( com.fasterxml.jackson.databind.ser.std.ToStringSerializer)

Thí dụ:

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

3
Còn điều ngược lại khi một Chuỗi cần được chuyển đổi thành một int? Tôi không thấy ToIntSerializer.class.
jEremyB

@jEremyB Bạn có thể phải viết một deserializer tùy chỉnh
Drew Stephens

ToStringSerializer hoạt động nhưng FloatSerializer mang thông điệp này: Could not nội dung ghi: java.lang.Integer không thể được đúc để java.lang.Float
Arnie Schwarzvogel

12

jackson-annotations cung cấp @JsonFormatcó thể xử lý rất nhiều tùy chỉnh mà không cần viết bộ nối tiếp tùy chỉnh.

Ví dụ: yêu cầu một STRINGhình dạng cho một trường có kiểu số sẽ xuất ra giá trị số dưới dạng chuỗi

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

sẽ dẫn đến đầu ra mong muốn

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

11

Thêm một @JsonPropertygetter có chú thích, trả về a String, cho favoriteNumbertrường:

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

7

Trong trường hợp bạn không muốn làm ô nhiễm mô hình của mình bằng các chú thích và muốn thực hiện một số thao tác tùy chỉnh, bạn có thể sử dụng mixin.

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

Ghi đè tuổi:

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

Làm bất cứ điều gì bạn cần với độ tuổi:

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

2

với sự trợ giúp của @JsonView, chúng ta có thể quyết định các trường của các lớp mô hình để tuần tự hóa đáp ứng các tiêu chí tối thiểu (chúng ta phải xác định các tiêu chí) như chúng ta có thể có một lớp lõi với 10 thuộc tính nhưng chỉ có 5 thuộc tính có thể được tuần tự hóa cần thiết cho khách hàng chỉ có

Xác định Chế độ xem của chúng tôi bằng cách đơn giản tạo lớp sau:

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

Lớp mô hình được chú thích với các chế độ xem:

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

Bây giờ chúng ta phải viết trình chuyển đổi json tùy chỉnh bằng cách mở rộng lớp HttpMessageConverter từ mùa xuân như sau:

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

}

Bây giờ cần phải nói với mùa xuân để sử dụng chuyển đổi json tùy chỉnh này bằng cách chỉ cần đặt nó vào 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>

Đó là cách bạn có thể quyết định trường nào sẽ được tuần tự hóa.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.