Как игнорировать поля перечисления в сопоставлении Джексона JSON-to-Object?
У меня есть JSON Object что-то вроде:
{"name":"John", "grade":"A"}
или
{"name":"Mike", "grade":"B"}
или
{"name":"Simon", "grade":"C"}
и т.д.
Я пытаюсь сопоставить вышеуказанный 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;
}
}
приведенное выше отображение работает нормально, но в будущем будет больше типов "класса", пусть говорят D, E и т.д., что нарушает существующее сопоставление и выдает следующее исключение.
05-08 09:56:28.130: W/System.err(21309): org.codehaus.jackson.map.JsonMappingException: Can not construct instance of Employee from String value 'D': value not one of declared Enum instance names
Есть ли способ игнорировать неизвестные поля в типах перечисления?
Спасибо
Ответы
Ответ 1
Я думаю, вы должны определить внешний deserializer для Grade
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 + "]";
}
}
Теперь синтаксический анализатор может выглядеть так:
class GradeDeserializer extends JsonDeserializer<Grade> {
@Override
public Grade deserialize(JsonParser parser, DeserializationContext context)
throws IOException, JsonProcessingException {
return Grade.fromString(parser.getValueAsString());
}
}
Пример использования:
public class JacksonProgram {
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);
}
}
Вывод:
Employee [grade=UNKNOWN, name=John]
Если вы не хотите добавлять дополнительное поле, вы бы вернули null
, например.
Ответ 2
Я нашел способ сделать это следующим образом:
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());
}
Выводится:
нулевым
другие классы:
import com.fasterxml.jackson.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.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
public enum Grade {A, B, C}
Я еще не встречал способ сделать это с аннотацией.
Надеюсь, это поможет.
Ответ 3
@JsonCreator
предлагает более краткое решение по сравнению с @JsonDeserialize
.
Идея состоит в том, чтобы аннотировать замену valueOf()
(в этом примере называемую safeValueOf()
) с помощью @JsonCreator
и затем Джексон десериализует строки, используя вашу реализацию.
Обратите внимание, что реализация находится внутри перечисления, вы можете использовать ее как поле в других объектах без изменений.
Приведенное ниже решение включено в unit тест, поэтому вы можете запустить его напрямую.
import static junit.framework.TestCase.assertEquals;
import java.io.IOException;
import org.junit.Test;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.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));
}
}