Java 如何忽略 Jackson JSON-to-Object 映射中的枚举字段?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16446619/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 06:57:22  来源:igfitidea点击:

How to ignore enum fields in Hymanson JSON-to-Object mapping?

javaHymanson

提问by Saqib

I have a JSON Object something like:

我有一个类似的 JSON 对象:

{"name":"John", "grade":"A"}

or

或者

{"name":"Mike", "grade":"B"}

or

或者

{"name":"Simon", "grade":"C"}

etc

等等

I am trying to map the above JSON to:

我正在尝试将上述 JSON 映射到:

@JsonIgnoreProperties(ignoreUnknown = true)
public class Employee{
      @JsonIgnoreProperties(ignoreUnknown = true)
      public enum Grade{ A, B, C }
      Grade grade;
      String name;

  public Grade getGrade() {
    return grade;
  }

  public void setGrade(Grade grade) {
    this.grade = grade;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}

the above mapping works fine but in the future there will be more "Grade" types let say D,E etc which breaks the existing mapping and throws the following exception

上面的映射工作正常,但将来会有更多的“等级”类型,比如 D、E 等,这会破坏现有的映射并抛出以下异常

05-08 09:56:28.130: W/System.err(21309): org.codehaus.Hymanson.map.JsonMappingException: Can not construct instance of Employee from String value 'D': value not one of declared Enum instance names

Is there a way to ignore unknown fields with in enum types?

有没有办法忽略枚举类型中的未知字段?

Thanks

谢谢

采纳答案by Micha? Ziober

I think you should define external deserializerfor Gradeenum.

我认为您应该为枚举定义外部解串器Grade

I added additional field to enum - UNKNOWN:

我向枚举添加了额外的字段 - 未知:

enum Grade {
    A, B, C, UNKNOWN;

    public static Grade fromString(String value) {
        for (Grade grade : values()) {
            if (grade.name().equalsIgnoreCase(value)) {
                return grade;
            }
        }

        return UNKNOWN;
    }
}
class Employee {

    @JsonDeserialize(using = GradeDeserializer.class)
    private Grade grade;
    private String name;

    public Grade getGrade() {
        return grade;
    }

    public void setGrade(Grade grade) {
        this.grade = grade;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Employee [grade=" + grade + ", name=" + name + "]";
    }
}

Now, parser could look like that:

现在,解析器看起来像这样:

class GradeDeserializer extends JsonDeserializer<Grade> {
    @Override
    public Grade deserialize(JsonParser parser, DeserializationContext context)
            throws IOException, JsonProcessingException {
        return Grade.fromString(parser.getValueAsString());
    }
}

Example usage:

用法示例:

public class HymansonProgram {

    public static void main(String[] args) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();
        JsonFactory jsonFactory = new JsonFactory();
        JsonParser parser = jsonFactory
                .createJsonParser("{\"name\":\"John\", \"grade\":\"D\"}");
        Employee employee = objectMapper.readValue(parser, Employee.class);
        System.out.println(employee);
    }

}

Output:

输出:

Employee [grade=UNKNOWN, name=John]

If you don't want to add additional field, you would return nullfor example.

如果您不想添加其他字段,则可以返回null例如。

回答by stefaan dutry

I have found a way to do this like follows:

我找到了一种方法来做到这一点,如下所示:

public static void main(String[] args) throws JsonParseException, JsonMappingException, UnsupportedEncodingException, IOException {
    String json = "{\"name\":\"John\", \"grade\":\"D\"}";

    ObjectMapper mapper = new ObjectMapper();
    mapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
    Employee employee = mapper.readValue(new ByteArrayInputStream(json.getBytes("UTF-8")), Employee.class);

    System.out.println(employee.getGrade());
}

This outputs :

这输出:

null

空值

other classes:

其他类:

import com.fasterxml.Hymanson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown = true)
public class Employee {
    private String name;
    private Grade grade;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Grade getGrade() {
        return grade;
    }

    public void setGrade(Grade grade) {
        this.grade = grade;
    }
}



import com.fasterxml.Hymanson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown = true)
public enum Grade {A, B, C}

I haven't come across a way to do this with an annotation yet.

我还没有遇到用注释来做到这一点的方法。

I hope this helps.

我希望这有帮助。

回答by Bogdan Calmac

@JsonCreatorprovides a more concise solution compared to @JsonDeserialize.

@JsonCreator@JsonDeserialize.

The idea is to annotate your valueOf()replacement ( called safeValueOf()in this example) with @JsonCreatorand then Hymanson would deserialize strings using your implementation.

我们的想法是注释您的valueOf()替换(safeValueOf()在本例中称为),@JsonCreator然后 Hymanson 将使用您的实现反序列化字符串。

Note that the implementation is inside the enum, you can use it as field in other objects with no change.

请注意,实现位于枚举内部,您可以将其用作其他对象中的字段而无需更改。

The solution below is wrapped in a unit test so you can run it directly.

下面的解决方案包含在单元测试中,因此您可以直接运行它。

import static junit.framework.TestCase.assertEquals;

import java.io.IOException;

import org.junit.Test;

import com.fasterxml.Hymanson.annotation.JsonCreator;
import com.fasterxml.Hymanson.databind.ObjectMapper;

public class EmployeeGradeTest {

    public enum Grade {
        A, B, C, OTHER;

        @JsonCreator
        public static Grade safeValueOf(String string) {
            try {
                return Grade.valueOf(string);
            } catch (IllegalArgumentException e) {
                return OTHER;
            }
        }
    }

    @Test
    public void deserialize() throws IOException {
        assertEquals(Grade.A, new ObjectMapper().readValue("\"A\"", Grade.class));
    }

    @Test
    public void deserializeNewValue() throws IOException {
        assertEquals(Grade.OTHER, new ObjectMapper().readValue("\"D\"", Grade.class));
    }
}

回答by Jesse Nelson

I am using boot2 (although this make work in boot 1 too) but you can just enable the deserialization feature in the applicaiton properties/yaml;

我正在使用 boot2(尽管这也可以在引导 1 中工作)但您可以在应用程序属性/yaml 中启用反序列化功能;

spring:
  Hymanson:
    deserialization:
      READ_UNKNOWN_ENUM_VALUES_AS_NULL: true