Serializzazione personalizzata Jackson JSON per determinati campi


93

Esiste un modo utilizzando Jackson JSON Processor per eseguire la serializzazione a livello di campo personalizzata? Ad esempio, mi piacerebbe avere la classe

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

serializzato nel seguente JSON:

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

Nota che age = 25 è codificato come un numero mentre favouriteNumber = 123 è codificato come una stringa . Fuori dagli schemi Jackson fa il marshalling inta un numero. In questo caso voglio che favouriteNumber sia codificato come una stringa.


1
Ho scritto un post su Come scrivere un serializzatore personalizzato con Jackson che potrebbe essere utile ad alcuni.
Sam Berry

Risposte:


106

È possibile implementare un serializzatore personalizzato come segue:

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 dovrebbe gestire l'autoboxing da inta Integerper te.


3
Jackson-databind (almeno 2.1.3) contiene già ToStringSerializer speciale, vedi la mia risposta.
werupokz

@KevinBowersox Puoi aiutarmi con il mio problema di deserializzazione per favore?
JJD


C'è un modo meno terribile per farlo? Come Person implements ToJson?
jameshfisher

1
Nel mio caso, ha persino fallito da as=String.classparte, a causa dei tipi che ho usato. @ kevin-bowersox, suggerisco di aggiornare il tuo commento, in linea con quanto ha detto @GarethLatty.
Bert

54

Jackson-databind (almeno 2.1.3) fornisce speciali ToStringSerializer( com.fasterxml.jackson.databind.ser.std.ToStringSerializer)

Esempio:

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

3
E il contrario in cui una stringa deve essere convertita in un int? Non vedo ToIntSerializer.class.
jEremyB

@jEremyB Potrebbe essere necessario scrivere un deserializzatore personalizzato
Drew Stephens

ToStringSerializer funziona ma FloatSerializer porta questo messaggio: Impossibile scrivere il contenuto: java.lang.Integer non può essere lanciato su java.lang.Float
Arnie Schwarzvogel

12

jackson-annotations fornisce @JsonFormatche può gestire molte personalizzazioni senza la necessità di scrivere il serializzatore personalizzato.

Ad esempio, la richiesta di una STRINGforma per un campo con tipo numerico restituirà il valore numerico come stringa

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

risulterà nell'output desiderato

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

11

Aggiungi un @JsonPropertygetter annotato, che restituisce a String, per il favoriteNumbercampo:

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

Nel caso in cui non desideri inquinare il tuo modello con annotazioni e desideri eseguire alcune operazioni personalizzate, potresti usare i mixin.

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

Sostituisci età:

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

Fai tutto ciò di cui hai bisogno con l'età:

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

con l'aiuto di @JsonView possiamo decidere i campi delle classi del modello da serializzare che soddisfano i criteri minimi (dobbiamo definire i criteri) come se potessimo avere una classe principale con 10 proprietà ma solo 5 proprietà possono essere serializzate che sono necessarie per il cliente solo

Definisci le nostre viste semplicemente creando la seguente classe:

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

Classe modello annotata con viste:

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

Ora dobbiamo scrivere un convertitore json personalizzato semplicemente estendendo la classe HttpMessageConverter dalla primavera come:

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

}

Ora è necessario dire a spring di utilizzare questa conversione json personalizzata semplicemente inserendola in 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>

È così che sarai in grado di decidere quali campi serializzare.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.