【问题标题】:Serializing enums with Jackson使用 Jackson 序列化枚举
【发布时间】:2011-12-07 16:24:02
【问题描述】:

我有一个下面描述的枚举:

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

我用我的控制器(new OrderType[] {UNKNOWN,TYPEA,TYPEB,TYPEC};)返回枚举数组,然后 Spring 将其序列化为以下 json 字符串:

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

强制 Jackson 像 POJO 一样序列化枚举的最佳方法是什么?例如:

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

我玩了不同的注释,但无法获得这样的结果。

【问题讨论】:

  • 看起来您已经找到了解决方案;伟大的!好奇你为什么需要它?
  • 我正在开发一个通过 JSON 与服务器端通信的 GWT 应用程序。此枚举将为组合框提供选项值。
  • 嗯,好的。这么多值的简写……很有趣。

标签: java json spring spring-mvc jackson


【解决方案1】:

在 Spring Boot 2 中,最简单的方法是在你的 application.properties 中声明:

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

并定义枚举的 toString() 方法。

【讨论】:

    【解决方案2】:
    @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>
    

    【讨论】:

    • 我喜欢这个替代方案,它更干净,但是,我用这个类尝试过,类型没有被序列化,知道有什么问题吗? @JsonFormat(shape= JsonFormat.Shape.OBJECT) @JsonAutoDetect() 公共枚举事件 { VISIT_WEBSITE(Type.ADMIN); @JsonProperty 公共类型类型;公共类型 getType() { 返回类型; } 事件(类型类型){ this.type = type; } public enum Type { ADMIN, CONSUMER, } } 我使用的是 Jackson 2.1.2
    • 我已在答案正文中添加了其他详细信息
    • 发现了问题所在,我使用的是 Jackson 2.1.2,但我的 Spring 版本仍然是 3.1,因此不支持此版本。升级到 3.2.1,现在一切正常。谢谢!
    • @Vecnas 在另一个实体中使用枚举时,我可以覆盖默认的@JsonFormat 吗?例如,我希望将枚举序列化为字符串而不是对象的实体。我尝试在使用枚举的类的字段中添加另一个@JsonFormat,但它始终序列化为对象。
    • 我发现,使用 - @JsonSerialize(using = ToStringSerializer.class) 作为字段,它使用 toString()。不是严格的解决方案,但有效
    【解决方案3】:

    终于我自己找到了解决办法。

    我必须用 @JsonSerialize(using = OrderTypeSerializer.class) 注释枚举并实现自定义序列化程序:

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

    【讨论】:

    • 请注意,要将 Jackson 配置为使用自定义(反)序列化处理,使用注释的替代方法是使用配置模块注册(反)序列化程序。 wiki.fasterxml.com/JacksonHowToCustomSerializers
    • 这对我使用 Spring 3.1.1 不起作用。我的@Controller 仍然返回没有我的属性的 json。
    • 我有一些枚举,我想用一个函数获取所有枚举。我该怎么做?
    • 对于一种枚举类型,我必须定义一个自定义反序列化器。有没有通用的解决方案?
    【解决方案4】:

    我找到了一个非常简洁的解决方案,当您无法像我一样修改枚举类时特别有用。然后,您应该提供一个启用了特定功能的自定义 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);
        }
    }
    

    还有更多与枚举相关的功能可用,请参见此处:

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

    【讨论】:

    • 我同意。此外,在 Jackson 2.5 中,您不需要自定义对象映射器。这样做:objMapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); 和这个:objMapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
    【解决方案5】:

    序列化 Enum 的一种简单方法是使用 @JsonFormat 注释。 @JsonFormat 可以通过三种方式配置 Enum 的序列化。

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

    使用 OrderType::name 作为序列化方法。 OrderType.TypeA 的序列化为“TYPEA”

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

    使用 OrderType::ordinal 作为序列化方法。 OrderType.TypeA 的序列化为1

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

    将 OrderType 视为 POJO。 OrderType.TypeA 的序列化为{"id":1,"name":"Type A"}

    JsonFormat.Shape.OBJECT 是您需要的。

    一种更复杂的方法是您的解决方案,为 Enum 指定一个序列化程序。

    查看此参考: https://fasterxml.github.io/jackson-annotations/javadoc/2.2.0/com/fasterxml/jackson/annotation/JsonFormat.html

    【讨论】:

      【解决方案6】:

      使用@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);
          }
      
      }
      

      【讨论】:

        【解决方案7】:

        这是我的解决方案。我想将枚举转换为{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"}

        【讨论】:

        • 优秀。你拯救了我的一天:-)
        猜你喜欢
        • 2015-10-19
        • 1970-01-01
        • 1970-01-01
        • 2014-07-16
        • 2020-07-17
        • 1970-01-01
        • 2021-12-27
        • 2018-07-31
        相关资源
        最近更新 更多