Jackson nomi di proprietà dinamiche

Vorrei serializzare un object in modo tale che uno dei campi sarà nominato diversamente in base al tipo di campo. Per esempio:

public class Response { private Status status; private String error; private Object data; [ getters, setters ] } 

Qui, vorrei che i data del campo fossero serializzati in qualcosa come data.getClass.getName() invece di avere sempre un campo chiamato data che contiene un tipo diverso a seconda della situazione.

Come potrei ottenere un simile trucco usando Jackson?

Utilizzando un JsonSerializer personalizzato.

 public class Response { private String status; private String error; @JsonProperty("p") @JsonSerialize(using = CustomSerializer.class) private Object data; // ... } public class CustomSerializer extends JsonSerializer { public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { jgen.writeStartObject(); jgen.writeObjectField(value.getClass().getName(), value); jgen.writeEndObject(); } } 

E poi, supponiamo di voler serializzare i seguenti due oggetti:

 public static void main(String... args) throws Exception { ObjectMapper mapper = new ObjectMapper(); Response r1 = new Response("Error", "Some error", 20); System.out.println(mapper.writeValueAsString(r1)); Response r2 = new Response("Error", "Some error", "some string"); System.out.println(mapper.writeValueAsString(r2)); } 

Il primo stamperà:

 {"status":"Error","error":"Some error","p":{"java.lang.Integer":20}} 

E il secondo:

 {"status":"Error","error":"Some error","p":{"java.lang.String":"some string"}} 

Ho usato il nome p per l’object wrapper dal momento che servirà solo come un p laceholder. Se vuoi rimuoverlo, dovresti scrivere un serializzatore personalizzato per l’ intera class, cioè un JsonSerializer .

Ho avuto una soluzione più semplice usando @JsonAnyGetter annotazione @JsonAnyGetter , e ha funzionato come un fascino.

 import java.util.Collections; import java.util.Map; public class Response { private Status status; private String error; @JsonIgnore private Object data; [getters, setters] @JsonAnyGetter public Map any() { //add the custom name here //use full HashMap if you need more than one property return Collections.singletonMap(data.getClass().getName(), data); } } 

Non è necessario alcun wrapper, non è necessario alcun serializzatore personalizzato.

la mia soluzione.

 @Data @EqualsAndHashCode @ToString @JsonSerialize(using = ElementsListBean.CustomSerializer.class) public class ElementsListBean { public ElementsListBean() { } public ElementsListBean(final String fieldName, final List elements) { this.fieldName = fieldName; this.elements = elements; } private String fieldName; private List elements; public int length() { return (this.elements != null) ? this.elements.size() : 0; } private static class CustomSerializer extends JsonSerializer { public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { if (value instanceof ElementsListBean) { final ElementsListBean o = (ElementsListBean) value; jgen.writeStartObject(); jgen.writeArrayFieldStart(o.getFieldName()); for (Object e : o.getElements()) { jgen.writeObject(e); } jgen.writeEndArray(); jgen.writeNumberField("length", o.length()); jgen.writeEndObject(); } } } } 

Puoi usare l’annotazione JsonTypeInfo , che dice esattamente a Jackson e non hai bisogno di scrivere un serializzatore personalizzato. C’è un modo diverso di includere queste informazioni, ma per la tua domanda specifica As.WRAPPER_OBJECT e Id.CLASS . Per esempio:

 public static class Response { private Status status; private String error; @JsonTypeInfo(include = As.WRAPPER_OBJECT, use = Id.CLASS) private Object data; } 

Questo, tuttavia, non funzionerà su tipi primitivi, come String o Integer. Non hai comunque bisogno di quell’informazione per i primitivi, dato che sono rappresentati in modo nativo in JSON e Jackson sa come gestirli. Il vantaggio in più nell’usare l’annotazione è che si ottiene la deserializzazione gratuitamente, se ne hai mai bisogno. Ecco un esempio:

 public static void main(String[] args) throws Exception { ObjectMapper mapper = new ObjectMapper(); Response r1 = new Response("Status", "An error", "some data"); Response r2 = new Response("Status", "An error", 10); Response r3 = new Response("Status", "An error", new MyClass("data")); System.out.println(mapper.writeValueAsString(r1)); System.out.println(mapper.writeValueAsString(r2)); System.out.println(mapper.writeValueAsString(r3)); } @JsonAutoDetect(fieldVisibility=Visibility.ANY) public static class MyClass{ private String data; public MyClass(String data) { this.data = data; } } 

e il risultato:

 {"status":"Status","error":"An error","data":"some data"} {"status":"Status","error":"An error","data":10} {"status":"Status","error":"An error","data":{"some.package.MyClass":{"data":"data"}}}