การต่ออนุกรม enums กับแจ็คสัน


90

ฉันมี Enum desrcibed ด้านล่าง:

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

ฉันส่งคืนอาร์เรย์ enum ด้วยคอนโทรลเลอร์ของฉัน ( new OrderType[] {UNKNOWN,TYPEA,TYPEB,TYPEC};) และ Spring ทำให้อนุกรมเป็นสตริง json ต่อไปนี้:

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

อะไรคือแนวทางที่ดีที่สุดในการบังคับให้แจ็คสันทำให้อนุกรม enums เหมือนกับ POJOs? เช่น:

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

ฉันเล่นกับคำอธิบายประกอบที่แตกต่างกัน แต่ไม่สามารถจัดการเพื่อให้ได้ผลลัพธ์ดังกล่าว


1
ดูเหมือนว่าคุณจะพบวิธีแก้ปัญหาแล้ว เยี่ยมมาก! อยากรู้ว่าทำไมคุณถึงต้องการ?
StaxMan

ฉันกำลังพัฒนาแอปพลิเคชัน GWT ที่สื่อสารกับฝั่งเซิร์ฟเวอร์ผ่าน JSON enum นี้จะให้ค่าตัวเลือกสำหรับ combobox
Nofate

อาโอเค. แบบสั้น ๆ สำหรับเซตค่า ... น่าสนใจ
StaxMan

คำตอบ:


87

ในที่สุดฉันก็พบทางออกด้วยตัวเอง

ฉันต้องใส่คำอธิบายประกอบ enum ด้วย@JsonSerialize(using = OrderTypeSerializer.class)และใช้ serializer ที่กำหนดเอง:

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
โปรดทราบว่าในการกำหนดค่า Jackson ให้ใช้การประมวลผลอนุกรมแบบกำหนดเอง (de) ทางเลือกในการใช้คำอธิบายประกอบคือการลงทะเบียน (de) serializers กับโมดูลการกำหนดค่า wiki.fasterxml.com/JacksonHowToCustomSerializers
Programmer Bruce

1
สิ่งนี้ใช้ไม่ได้สำหรับฉันเมื่อใช้ Spring 3.1.1 @Controller ของฉันยังคงส่งคืน json โดยไม่มีคุณสมบัติของฉัน
Dave

ฉันมี Enums บางตัวและฉันต้องการรับ enums ทั้งหมดด้วยฟังก์ชันเดียว ฉันจะทำมันได้อย่างไร?
Morteza Malvandi

สำหรับ enum ประเภทเดียวฉันต้องกำหนด deserializer ที่กำหนดเอง มีวิธีแก้ปัญหาทั่วไปหรือไม่?
เจ้า

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

มีให้ตั้งแต่https://github.com/FasterXML/jackson-databind/issues/24

เพิ่งทดสอบว่าใช้งานได้กับเวอร์ชัน 2.1.2

คำตอบสำหรับ TheZuck :

ฉันลองตัวอย่างของคุณแล้วมี Json:

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

รหัสของฉัน:

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

และการอ้างอิงคือ:

<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
ฉันชอบทางเลือกนี้มันสะอาดกว่า แต่ฉันลองกับคลาสนี้แล้วประเภทนี้ไม่ได้รับการต่อเนื่องมีความคิดว่ามีอะไรผิดปกติหรือไม่? @JsonFormat (shape = JsonFormat.Shape.OBJECT) @JsonAutoDetect () เหตุการณ์ enum สาธารณะ {VISIT_WEBSITE (Type.ADMIN); @JsonProperty ประเภท Public Type; ประเภทสาธารณะ getType () {ประเภทผลตอบแทน; } เหตุการณ์ (ประเภทประเภท) {this.type = type; } public enum Type {ADMIN, CONSUMER,}} ฉันใช้ Jackson 2.1.2
TheZuck

ฉันได้เพิ่มรายละเอียดเพิ่มเติมในเนื้อหาคำตอบ
Vecnas

พบว่ามีอะไรผิดปกติฉันใช้ Jackson 2.1.2 แต่เวอร์ชัน Spring ของฉันยังคงเป็น 3.1 ดังนั้นจึงไม่รองรับเวอร์ชันนี้ อัปเกรดเป็น 3.2.1 และตอนนี้ทุกอย่างเรียบร้อยดี ขอบคุณ!
TheZuck

@Vecnas ฉันสามารถแทนที่ค่าเริ่มต้น@JsonFormatของ enum เมื่อถูกใช้ในเอนทิตีอื่นได้หรือไม่? ตัวอย่างเช่นเอนทิตีที่ฉันต้องการให้ enum ควรต่อเนื่องเป็นสตริงแทนที่จะเป็นอ็อบเจกต์ ฉันพยายามเพิ่มอีก@JsonFormatช่องในฟิลด์ในคลาสที่ใช้ enum แต่จะทำให้เป็นอนุกรมเป็นวัตถุเสมอ
herau

สิ่งที่ฉันพบให้ใช้ - @JsonSerialize (โดยใช้ = ToStringSerializer.class) สำหรับฟิลด์จะใช้ toString () ไม่ใช่วิธีแก้ปัญหาที่เข้มงวด แต่ใช้ได้ผล
Vecnas

25

ฉันพบวิธีแก้ปัญหาที่ดีและรัดกุมโดยเฉพาะอย่างยิ่งเมื่อคุณไม่สามารถแก้ไขคลาส enum ได้เหมือนในกรณีของฉัน จากนั้นคุณควรจัดเตรียม ObjectMapper ที่กำหนดเองพร้อมคุณสมบัติบางอย่างที่เปิดใช้งาน คุณสมบัติเหล่านี้มีให้ตั้งแต่ 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);
    }
}

มีคุณสมบัติที่เกี่ยวข้องกับ enum เพิ่มเติมให้ดูที่นี่:

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


4
ฉันเห็นด้วย. นอกจากนี้ใน Jackson 2.5 คุณไม่จำเป็นต้องมีตัวทำแผนที่วัตถุแบบกำหนดเอง เพียงแค่ทำสิ่งนี้: objMapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);และสิ่งนี้:objMapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
Jake Toronto

14

นี่คือทางออกของฉัน ฉันต้องการเปลี่ยน enum เป็น{id: ..., name: ...}รูปแบบ

กับ 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);
    }
}

ด้วย 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.CLOSEDแปลเป็น{id: "CLOSED", name: "closed rule"}.


ยอดเยี่ยม. คุณบันทึกวันของฉัน :-)
sriram

4

วิธีง่ายๆในการทำให้ Enum เป็นอนุกรมคือการใช้คำอธิบายประกอบ @JsonFormat @JsonFormat สามารถกำหนดค่าการทำให้เป็นอนุกรมของ Enum ได้สามวิธี

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

ใช้ OrderType :: name เป็นวิธีการทำให้เป็นอนุกรม Serialization ของ OrderType.TypeA คือ“TYPEA”

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

ใช้ OrderType :: ลำดับเป็นวิธีการทำให้เป็นอนุกรม Serialization ของ OrderType.TypeA คือ1

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

ถือว่า OrderType เป็น POJO Serialization ของ OrderType.TypeA คือ{"id":1,"name":"Type A"}

JsonFormat.Shape.OBJECTคือสิ่งที่คุณต้องการในกรณีของคุณ

วิธีที่ซับซ้อนกว่าเล็กน้อยคือการแก้ปัญหาของคุณโดยระบุ serializer สำหรับ Enum

ดูข้อมูลอ้างอิงนี้: https://fasterxml.github.io/jackson-annotations/javadoc/2.2.0/com/fasterxml/jackson/annotation/JsonFormat.html


3

ใช้คำอธิบายประกอบ @JsonCreator สร้างเมธอด getType () ต่ออนุกรมกับ toString หรืออ็อบเจ็กต์ที่ทำงาน

{"ATIVO"}

หรือ

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

ใน Spring Boot 2 วิธีที่ง่ายที่สุดคือการประกาศใน application.properties ของคุณ:

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

และกำหนดเมธอด toString () ของ enums ของคุณ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.