Serializzare le enumerazioni con Jackson


90

Ho un Enum desrcibed di seguito:

public enum OrderType {

  UNKNOWN(0, "Undefined"),
  TYPEA(1, "Type A"),
  TYPEB(2, "Type B"),
  TYPEC(3, "Type C");

  private Integer id;
  private String name;

  private WorkOrderType(Integer id, String name) {
    this.id = id;
    this.name = name;
  }

  //Setters, getters....
}

Restituisco l'array enum con il mio controller ( new OrderType[] {UNKNOWN,TYPEA,TYPEB,TYPEC};) e Spring lo serializza nella seguente stringa json:

["UNKNOWN", "TYPEA", "TYPEB", "TYPEC"] 

Qual è l'approccio migliore per costringere Jackson a serializzare enumerazioni proprio come i POJO? Per esempio:

[
  {"id": 1, "name": "Undefined"},
  {"id": 2, "name": "Type A"},
  {"id": 3, "name": "Type B"},
  {"id": 4, "name": "Type C"}
]

Ho giocato con diverse annotazioni ma non sono riuscito a ottenere questo risultato.


1
Sembra che tu abbia già trovato la soluzione; grande! Sei curioso di sapere perché ne hai bisogno?
StaxMan

Sto sviluppando un'applicazione GWT che comunica con il lato server tramite JSON. Questo enum fornirà i valori delle opzioni per la casella combinata.
Nofate

Ah ok. Quindi una specie di scorciatoia per un insieme di valori ... interessante.
StaxMan

Risposte:


87

Finalmente ho trovato la soluzione da solo.

Ho dovuto annotare enum @JsonSerialize(using = OrderTypeSerializer.class)e implementare il serializzatore personalizzato:

public class OrderTypeSerializer extends JsonSerializer<OrderType> {

  @Override
  public void serialize(OrderType value, JsonGenerator generator,
            SerializerProvider provider) throws IOException,
            JsonProcessingException {

    generator.writeStartObject();
    generator.writeFieldName("id");
    generator.writeNumber(value.getId());
    generator.writeFieldName("name");
    generator.writeString(value.getName());
    generator.writeEndObject();
  }
}

4
Si noti che per configurare Jackson in modo che utilizzi l'elaborazione della (de) serializzazione personalizzata, un'alternativa all'uso di un'annotazione è registrare (de) serializzatori con un modulo di configurazione. wiki.fasterxml.com/JacksonHowToCustomSerializers
Programmer Bruce

1
Questo non ha funzionato per me usando Spring 3.1.1. Il mio @Controller restituisce ancora json senza i miei attributi.
Dave

Ho alcuni enumerativi e voglio ottenere tutti gli enumerativi con una funzione. Come posso farlo?
Morteza Malvandi

Per un tipo di enumerazione, devo definire un deserializzatore personalizzato. Esiste una soluzione generica?
Chao

78
@JsonFormat(shape= JsonFormat.Shape.OBJECT)
public enum SomeEnum

disponibile da https://github.com/FasterXML/jackson-databind/issues/24

appena testato funziona con la versione 2.1.2

risposta a TheZuck :

Ho provato il tuo esempio, ho ottenuto Json:

{"events":[{"type":"ADMIN"}]}

Il mio codice:

@RequestMapping(value = "/getEvent") @ResponseBody
  public EventContainer getEvent() {
    EventContainer cont = new EventContainer();
    cont.setEvents(Event.values());
    return cont;
 }

class EventContainer implements Serializable {

  private Event[] events;

  public Event[] getEvents() {
    return events;
 }

 public void setEvents(Event[] events) {
   this.events = events;
 }
}

e le dipendenze sono:

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-annotations</artifactId>
  <version>${jackson.version}</version>
</dependency>

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>${jackson.version}</version>
</dependency>

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>${jackson.version}</version>
  <exclusions>
    <exclusion>
      <artifactId>jackson-annotations</artifactId>
      <groupId>com.fasterxml.jackson.core</groupId>
    </exclusion>
    <exclusion>
      <artifactId>jackson-core</artifactId>
      <groupId>com.fasterxml.jackson.core</groupId>
    </exclusion>
  </exclusions>
</dependency>

<jackson.version>2.1.2</jackson.version>

2
Mi piace questa alternativa, è più pulita, tuttavia, l'ho provata con questa classe e il tipo non viene serializzato, idea di cosa non va? @JsonFormat (shape = JsonFormat.Shape.OBJECT) @JsonAutoDetect () enumerazione pubblica Evento {VISIT_WEBSITE (Type.ADMIN); @JsonProperty public Type type; public Type getType () {return type; } Evento (tipo tipo) {this.type = tipo; } public enum Type {ADMIN, CONSUMER,}} Sto usando Jackson 2.1.2
TheZuck

Ho aggiunto ulteriori dettagli al corpo della risposta
Vecnas

scoperto cosa c'era che non andava, stavo usando Jackson 2.1.2 ma la mia versione Spring era ancora 3.1 quindi non supportava questa versione. Aggiornato alla 3.2.1 e ora va tutto bene. Grazie!
TheZuck

@Vecnas Posso sovrascrivere il valore predefinito dell'enumerazione @JsonFormatquando viene utilizzato in un'altra entità? per esempio un'entità in cui voglio che l'enum venga serializzato come una stringa invece di un oggetto. cerco di aggiungerne un altro @JsonFormatnel campo della classe che usa enum ma è sempre serializzato come oggetto.
herau

Quello che ho trovato, usa - @JsonSerialize (using = ToStringSerializer.class) per un campo, usa toString (). Soluzione non rigorosa, ma funziona
Vecnas

25

Ho trovato una soluzione molto carina e concisa, particolarmente utile quando non puoi modificare le classi enum come era nel mio caso. Quindi è necessario fornire un ObjectMapper personalizzato con una determinata funzionalità abilitata. Queste funzionalità sono disponibili da Jackson 1.6.

public class CustomObjectMapper extends ObjectMapper {
    @PostConstruct
    public void customConfiguration() {
        // Uses Enum.toString() for serialization of an Enum
        this.enable(WRITE_ENUMS_USING_TO_STRING);
        // Uses Enum.toString() for deserialization of an Enum
        this.enable(READ_ENUMS_USING_TO_STRING);
    }
}

Sono disponibili altre funzionalità relative all'enumerazione, vedere qui:

https://github.com/FasterXML/jackson-databind/wiki/Serialization-features https://github.com/FasterXML/jackson-databind/wiki/Deserialization-Features


4
Sono d'accordo. Inoltre, in Jackson 2.5, non è necessario un mappatore di oggetti personalizzato. Basta fare questo: objMapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);e questo:objMapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
Jake Toronto

14

Ecco la mia soluzione. Voglio trasformare enum in{id: ..., name: ...} form.

Con Jackson 1.x :

pom.xml:

<properties>
    <jackson.version>1.9.13</jackson.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.codehaus.jackson</groupId>
        <artifactId>jackson-core-asl</artifactId>
        <version>${jackson.version}</version>
    </dependency>
    <dependency>
        <groupId>org.codehaus.jackson</groupId>
        <artifactId>jackson-mapper-asl</artifactId>
        <version>${jackson.version}</version>
    </dependency>
</dependencies>

Rule.java:

import org.codehaus.jackson.map.annotate.JsonSerialize;
import my.NamedEnumJsonSerializer;
import my.NamedEnum;

@Entity
@Table(name = "RULE")
public class Rule {
    @Column(name = "STATUS", nullable = false, updatable = true)
    @Enumerated(EnumType.STRING)
    @JsonSerialize(using = NamedEnumJsonSerializer.class)
    private Status status;
    public Status getStatus() { return status; }
    public void setStatus(Status status) { this.status = status; }

    public static enum Status implements NamedEnum {
        OPEN("open rule"),
        CLOSED("closed rule"),
        WORKING("rule in work");

        private String name;
        Status(String name) { this.name = name; }
        public String getName() { return this.name; }
    };
}

NamedEnum.java:

package my;

public interface NamedEnum {
    String name();
    String getName();
}

NamedEnumJsonSerializer.java:

package my;

import my.NamedEnum;
import java.io.IOException;
import java.util.*;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;

public class NamedEnumJsonSerializer extends JsonSerializer<NamedEnum> {
    @Override
    public void serialize(NamedEnum value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
        Map<String, String> map = new HashMap<>();
        map.put("id", value.name());
        map.put("name", value.getName());
        jgen.writeObject(map);
    }
}

Con Jackson 2.x :

pom.xml:

<properties>
    <jackson.version>2.3.3</jackson.version>
</properties>

<dependencies>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>${jackson.version}</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>${jackson.version}</version>
    </dependency>
</dependencies>

Rule.java:

import com.fasterxml.jackson.annotation.JsonFormat;

@Entity
@Table(name = "RULE")
public class Rule {
    @Column(name = "STATUS", nullable = false, updatable = true)
    @Enumerated(EnumType.STRING)
    private Status status;
    public Status getStatus() { return status; }
    public void setStatus(Status status) { this.status = status; }

    @JsonFormat(shape = JsonFormat.Shape.OBJECT)
    public static enum Status {
        OPEN("open rule"),
        CLOSED("closed rule"),
        WORKING("rule in work");

        private String name;
        Status(String name) { this.name = name; }
        public String getName() { return this.name; }
        public String getId() { return this.name(); }
    };
}

Rule.Status.CLOSEDtradotto in {id: "CLOSED", name: "closed rule"}.


Eccellente. Mi hai salvato la giornata :-)
sriram

4

Un modo semplice per serializzare Enum è usare l'annotazione @JsonFormat. @JsonFormat può configurare la serializzazione di un Enum in tre modi.

@JsonFormat.Shape.STRING
public Enum OrderType {...}

utilizza OrderType :: name come metodo di serializzazione. La serializzazione di OrderType.TypeA è“TYPEA”

@JsonFormat.Shape.NUMBER
Public Enum OrderTYpe{...}

utilizza OrderType :: ordinal come metodo di serializzazione. La serializzazione di OrderType.TypeA è1

@JsonFormat.Shape.OBJECT
Public Enum OrderType{...}

considera OrderType come un POJO. La serializzazione di OrderType.TypeA è{"id":1,"name":"Type A"}

JsonFormat.Shape.OBJECT è ciò di cui hai bisogno nel tuo caso.

Un modo un po 'più complicato è la tua soluzione, specificando un serializzatore per Enum.

Dai un'occhiata a questo riferimento: https://fasterxml.github.io/jackson-annotations/javadoc/2.2.0/com/fasterxml/jackson/annotation/JsonFormat.html


3

Usa l'annotazione @JsonCreator, crea il metodo getType (), viene serializzato con toString o oggetto funzionante

{"ATIVO"}

o

{"type": "ATIVO", "descricao": "Ativo"}

...

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeType;

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum SituacaoUsuario {

    ATIVO("Ativo"),
    PENDENTE_VALIDACAO("Pendente de Validação"),
    INATIVO("Inativo"),
    BLOQUEADO("Bloqueado"),
    /**
     * Usuarios cadastrados pelos clientes que não possuem acesso a aplicacao,
     * caso venham a se cadastrar este status deve ser alterado
     */
    NAO_REGISTRADO("Não Registrado");

    private SituacaoUsuario(String descricao) {
        this.descricao = descricao;
    }

    private String descricao;

    public String getDescricao() {
        return descricao;
    }

    // TODO - Adicionar metodos dinamicamente
    public String getType() {
        return this.toString();
    }

    public String getPropertieKey() {
        StringBuilder sb = new StringBuilder("enum.");
        sb.append(this.getClass().getName()).append(".");
        sb.append(toString());
        return sb.toString().toLowerCase();
    }

    @JsonCreator
    public static SituacaoUsuario fromObject(JsonNode node) {
        String type = null;
        if (node.getNodeType().equals(JsonNodeType.STRING)) {
            type = node.asText();
        } else {
            if (!node.has("type")) {
                throw new IllegalArgumentException();
            }
            type = node.get("type").asText();
        }
        return valueOf(type);
    }

}

0

In Spring Boot 2, il modo più semplice è dichiarare in application.properties:

spring.jackson.serialization.WRITE_ENUMS_USING_TO_STRING=true
spring.jackson.deserialization.READ_ENUMS_USING_TO_STRING=true

e definisci il metodo toString () delle tue enumerazioni.

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.