【问题标题】:How to deserialize Enum with Jackson?如何用杰克逊反序列化枚举?
【发布时间】:2020-06-20 20:05:31
【问题描述】:

用户将{"lang": ["en_US", "en_UK"]} 发布到 REST API。我想填充一个具有此属性List<EnmLanguage> lang; 的对象。除了 JSON 数组之外,其他一切都有效。

我的 EnmLanguage.java 是:

@JsonSerialize(
    using = BaseEnumSerializer.class
)
@JsonDeserialize(
    using = BaseEnumDeserializer.class
)
public enum EnmLanguage implements BaseEnum, Serializable {
    en_US {
        public String getCode() {
            return "en_US";
        }

        public String getText(EnmLanguage s) {
            return s == EnmLanguage.en_US ? "English" : "İngilizce";
        }
    },

我的 BaseEnum.java 是:

public interface BaseEnum {
    Object getCode();

    String getText(EnmLanguage lang);

    default String getText() {
        return this.getText(EnmLanguage.tr_TR);
    }

    default Map<String, Object> getMap(EnmLanguage lang) {
        return new HashMap<String, Object>() {
            {
                this.put("name", BaseEnum.this.getText(lang));
                this.put("id", BaseEnum.this.getCode());
            }
        };
    }

    default Map<String, Object> getMap() {
        return this.getMap(EnmLanguage.tr_TR);
    }

    static <T extends Enum<T> & BaseEnum> T fromCode(Class<T> parent, Object code) {
        Map<Object, T> lookup = new HashMap();
        Enum[] var3 = (Enum[])parent.getEnumConstants();
        int var4 = var3.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            T d = var3[var5];
            lookup.put(((BaseEnum)d).getCode().toString(), d);
        }

        return (Enum)lookup.get(code.toString());
    }
}

我的 BaseEnumDeseraliazer 是:

public class BaseEnumDeserializer<T extends Enum<T> & BaseEnum> extends StdDeserializer<T> {
    public BaseEnumDeserializer() {
        this((Class)null);
    }

    public BaseEnumDeserializer(Class<T> vc) {
        super(vc);
    }

    public T deserialize(JsonParser jsonparser, DeserializationContext context) throws IOException {
        Field field = this.findField(jsonparser.getCurrentName(), jsonparser.getCurrentValue().getClass());
        Class<T> javaType = field.getType();
        return BaseEnum.fromCode(javaType, jsonparser.getText());
    }

    public Field findField(String name, Class<?> c) {
        while(c != null) {
            Field[] var3 = c.getDeclaredFields();
            int var4 = var3.length;

            for(int var5 = 0; var5 < var4; ++var5) {
                Field field = var3[var5];
                if (!Modifier.isStatic(field.getModifiers()) && field.getName().equals(name)) {
                    return field;
                }
            }

            c = c.getSuperclass();
        }

        return null;
    }
}

但最后它不会将"lang": ["en_US", "en_UK"] 转换为EnmLanguageList。我认为它不知道BaseEnum 转换为哪个。我应该如何让它知道它应该将其转换为List&lt;EnmLanguage&gt;

【问题讨论】:

  • ObjectMapper是如何配置的?听起来这可能是 Spring Rest?反序列化过程中是否出现异常?
  • 你好,你能给你的变量(var3、var4、var5...)提供人类可读的(和驼峰式)名称吗?
  • 为什么需要BaseEnum
  • @IndraBasak 以便项目中的所有枚举都有标准方法。
  • @IndraBasak,我还有几个实现 BaseEnum 的枚举

标签: java serialization jackson


【解决方案1】:

对于您的具体情况,在基本类型中添加静态 @JsonCreator 方法可能就足够了。

interface Lang {
    @JsonCreator
    static Lang valueOf(String value) {
        return Languages.valueOf(value);
    }
}

@JsonFormat(shape = JsonFormat.Shape.STRING)
enum Languages implements Lang {
    en_US,
    en_GB
}

然后你可以将它解析为任何其他类

class LangTest {
    private final ObjectMapper mapper = new ObjectMapper()
            .setAnnotationIntrospector(new JacksonAnnotationIntrospector());

    @Test
    void readsJson() throws IOException {
        final Lang[] tree = this.mapper
                .readerFor(Lang[].class)
                .readValue("[\"en_US\", \"en_GB\"]");
        Assertions.assertArrayEquals(
                tree,
                new Lang[]{
                        Languages.en_US,
                        Languages.en_GB
                }
        );
    }
}

【讨论】:

    【解决方案2】:

    这是一个简单的BaseEnum界面,

    public interface BaseEnum {
        Object getCode();
    
        String getText();
    }
    

    这是实现BaseEnum接口的EnmLanguage

    public enum EnmLanguage implements BaseEnum {
    
        en_US("en_US", "English"),
        en_UK("en_UK", "English"),
        tr_TR("tr_TR", "Turkish");
    
        private String code;
    
        private String text;
    
        EnmLanguage(String code, String text) {
            this.code = code;
            this.text = text;
        }
    
        @Override
        public String getCode() {
            return code;
        }
    
        @Override
        public String getText() {
            return text;
        }
    }
    

    这是另一个实现BaseEnum 接口的EnmElement

    public enum EnmElement implements BaseEnum {
    
        H("H", "Hydrogen"),
        He("He", "Helium"),
        Li("Li", "Lithium");
    
        private String code;
    
        private String text;
    
        EnmElement(String code, String text) {
            this.code = code;
            this.text = text;
        }
    
        @Override
        public String getCode() {
            return code;
        }
    
        @Override
        public String getText() {
            return text;
        }
    }
    

    这是一个简单的单元测试,

    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.junit.Test;
    
    import java.io.IOException;
    
    import static org.junit.Assert.assertArrayEquals;
    import static org.junit.Assert.assertEquals;
    import static org.junit.Assert.assertNotNull;
    
    public class EnumTest {
    
        @Test
        public void testDeserializeLanguage() throws IOException {
            ObjectMapper mapper = new ObjectMapper();
    
            EnmLanguage[] expected = {EnmLanguage.en_US, EnmLanguage.tr_TR};
            EnmLanguage[] actual = mapper
                    .readerFor(EnmLanguage[].class)
                    .readValue(mapper.writeValueAsString(expected));
            assertNotNull(actual);
            assertArrayEquals(expected, actual);
        }
    
        @Test
        public void testDeserializeElement() throws IOException {
            ObjectMapper mapper = new ObjectMapper();
    
            EnmElement[] expected = {EnmElement.H, EnmElement.He, EnmElement.Li};
            EnmElement[] actual = mapper
                    .readerFor(EnmElement[].class)
                    .readValue(mapper.writeValueAsString(expected));
            assertNotNull(actual);
            assertArrayEquals(expected, actual);
        }
    }
    

    使用枚举的稍微复杂一点的例子,

    import lombok.Data;
    
    @Data
    public class CompositeExample {
    
        private String name;
    
        private EnmLanguage[] lands;
    
        private EnmElement[] elements;
    }
    

    一个样本单元测试sn-p,

     @Test
     public void testDeserialize() throws IOException {
         CompositeExample expected = new CompositeExample();
    
         String name = "Hello";
         expected.setName(name);
    
         EnmLanguage[] langs = {EnmLanguage.en_US, EnmLanguage.tr_TR};
         expected.setLangs(langs);
    
         EnmElement[] elements = {EnmElement.H, EnmElement.He,  EnmElement.Li};
         expected.setElements(elements);
    
         ObjectMapper mapper = new ObjectMapper();
         String json = mapper.writeValueAsString(expected);
         //{"name":"Hello","langs":["en_US","tr_TR"],"elements":["H","He","Li"]}
    
         CompositeExample actual = mapper
                 .readerFor(CompositeExample.class)
                 .readValue(json);
         assertNotNull(actual);
         assertEquals(expected, actual);
     }
    

    【讨论】:

      【解决方案3】:

      尝试使用toString()启用读写枚举:

      objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
      objectMapper.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-09-10
        • 2021-09-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多