Serialisasi enum dengan Jackson

90

Saya memiliki Enum yang dideskripsikan di bawah ini:

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

Saya mengembalikan enum array dengan controller ( new OrderType[] {UNKNOWN,TYPEA,TYPEB,TYPEC};) saya, dan Spring membuat serialisasi menjadi string json berikut:

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

Apa pendekatan terbaik untuk memaksa Jackson membuat serial enum seperti POJO? Misalnya:

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

Saya bermain dengan anotasi yang berbeda tetapi tidak bisa mendapatkan hasil seperti itu.

Nofate
sumber
1
Sepertinya Anda sudah menemukan solusinya; Bagus! Penasaran mengapa Anda membutuhkannya?
StaxMan
Saya sedang mengembangkan aplikasi GWT yang berkomunikasi dengan sisi server melalui JSON. Enum ini akan memberikan nilai opsi untuk kotak kombo.
Nofate
Ah ok. Jenis kependekan dari serangkaian nilai ... menarik.
StaxMan

Jawaban:

87

Akhirnya saya menemukan solusinya sendiri.

Saya harus membuat anotasi enum dengan @JsonSerialize(using = OrderTypeSerializer.class)dan menerapkan serializer khusus:

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();
  }
}
Nofate
sumber
4
Perhatikan bahwa untuk mengkonfigurasi Jackson agar menggunakan pemrosesan serialisasi khusus (de), alternatif untuk menggunakan anotasi adalah dengan mendaftarkan (de) serializers dengan modul konfigurasi. wiki.fasterxml.com/JacksonHowToCustomSerializers
Programmer Bruce
1
Ini tidak berhasil untuk saya yang menggunakan Spring 3.1.1. @Controller saya masih mengembalikan json tanpa atribut saya.
Dave
Saya memiliki Beberapa Enum, Dan saya ingin mendapatkan Semua enum dengan satu fungsi. Bagaimana saya bisa melakukannya?
Morteza Malvandi
Untuk satu jenis enum, saya harus menentukan deserializer kustom. Apakah ada solusi umum?
Chao
78
@JsonFormat(shape= JsonFormat.Shape.OBJECT)
public enum SomeEnum

tersedia sejak https://github.com/FasterXML/jackson-databind/issues/24

baru saja mengujinya berfungsi dengan versi 2.1.2

jawaban untuk TheZuck :

Saya mencoba contoh Anda, dapatkan Json:

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

Kode saya:

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

dan dependensinya adalah:

<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>
Vecnas
sumber
2
Saya suka alternatif ini, lebih bersih, namun, saya mencobanya dengan kelas ini dan tipe tidak serial, tahu apa yang salah? @JsonFormat (shape = JsonFormat.Shape.OBJECT) @JsonAutoDetect () enum publik Acara {VISIT_WEBSITE (Type.ADMIN); Tipe publik @JsonProty tipe; public Type getType () {return type; } Peristiwa (Tipe tipe) {this.type = type; } public enum Type {ADMIN, CONSUMER,}} Saya menggunakan Jackson 2.1.2
TheZuck
Saya telah menambahkan detail tambahan ke tubuh jawaban
Vecnas
menemukan apa yang salah, saya menggunakan Jackson 2.1.2 tetapi versi Spring saya masih 3.1 sehingga tidak mendukung versi ini. Diupgrade ke 3.2.1 dan semuanya baik-baik saja sekarang. Terima kasih!
TheZuck
@Vecnas Dapatkah saya mengganti default @JsonFormatenum saat digunakan di entitas lain? misalnya entitas di mana saya ingin enum harus berseri sebagai string, bukan sebagai objek. saya mencoba untuk menambahkan lain @JsonFormatdi lapangan di kelas yang menggunakan enum tetapi selalu serial sebagai objek.
Herau
Apa yang saya temukan, gunakan - @JsonSerialize (using = ToStringSerializer.class) untuk bidang, menggunakan toString (). Bukan solusi ketat, tetapi berhasil
Vecnas
25

Saya telah menemukan solusi yang sangat bagus dan ringkas, terutama berguna ketika Anda tidak dapat memodifikasi kelas enum seperti dalam kasus saya. Kemudian Anda harus menyediakan ObjectMapper khusus dengan fitur tertentu yang diaktifkan. Fitur tersebut tersedia sejak 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);
    }
}

Ada lebih banyak fitur terkait enum yang tersedia, lihat di sini:

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

lagivan.dll
sumber
4
Saya setuju. Selain itu, di Jackson 2.5, Anda tidak memerlukan pemeta objek khusus. Lakukan saja ini: objMapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);dan ini:objMapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
Jake Toronto
14

Inilah solusi saya. Saya ingin mengubah enum menjadi{id: ..., name: ...} bentuk.

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

Dengan 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.CLOSEDditerjemahkan ke {id: "CLOSED", name: "closed rule"}.

gavenkoa.dll
sumber
Luar biasa. Anda menyelamatkan hari saya :-)
sriram
4

Cara mudah untuk membuat serial Enum menggunakan anotasi @JsonFormat. @JsonFormat dapat mengkonfigurasi serialisasi Enum dalam tiga cara.

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

menggunakan OrderType :: name sebagai metode serialisasi. Serialisasi OrderType.TypeA adalah“TYPEA”

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

menggunakan OrderType :: ordinal sebagai metode serialisasi. Serialisasi OrderType.TypeA adalah1

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

memperlakukan OrderType sebagai POJO. Serialisasi OrderType.TypeA adalah{"id":1,"name":"Type A"}

JsonFormat.Shape.OBJECT adalah yang Anda butuhkan dalam kasus Anda.

Cara yang sedikit lebih rumit adalah solusi Anda, menentukan serializer untuk Enum.

Lihat referensi ini: https://fasterxml.github.io/jackson-annotations/javadoc/2.2.0/com/fasterxml/jackson/annotation/JsonFormat.html

sinar
sumber
3

Gunakan anotasi @JsonCreator, buat metode getType (), bersambung dengan toString atau objek yang berfungsi

{"ATIVO"}

atau

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

}
Gleidosn
sumber
0

Di Spring Boot 2, cara termudah adalah dengan mendeklarasikan di application.properties Anda:

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

dan tentukan metode toString () dari enum Anda.

JRA_TLL
sumber