#java #json #jackson
#java #json #джексон
Вопрос:
Я использую Jackson для десериализации JSON этой формы:
{
"foo" : { "bar" : "baz" }
}
Код Джексона может выглядеть так:
@JsonCreator
public class MyProxy {
@JsonProperty("foo") final FooProxy foo;
}
public class FooProxy {
@JsonProperty("bar") final String bar;
}
Представьте, что потребитель этого API создает недопустимый JSON, подобный этому:
{
"foo" : { "bar" : 1 }
}
Однако в этом случае я получаю MismatchedInputException
сообщение, и ошибка выглядит следующим образом:
Не удается создать экземпляр
MyProxy
(хотя существует хотя бы один создатель): нет конструктора int / Int-argument / фабричного метода для десериализации из числового значения (1)
Когда я проверяю исключение MismatchedInputException и вызываю ex.getPathReference()
, я получаю:
FooProxy[«bar»]->java.lang.Объект [0]
Я хотел бы иметь возможность возвращать путь к нарушенному значению пользователю без какой-либо ссылки на базовые классы Java.
"foo.bar must be an Object."
Как я могу вернуть сообщение об ошибке с путем JSON и удалить любую ссылку на реализацию Java?
Ответ №1:
Что-то вроде этого должно это сделать:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
public class DeserializationErrorTest {
@Test
void testDeserializeWrongInput() throws IOException {
final ObjectMapper mapper = new ObjectMapper();
try {
mapper.readValue("{"foo" : { "bar" : "not-int" }}", MyProxy.class);
} catch (MismatchedInputException e) {
throw remapMismatchedInputException(e, RuntimeException.class);
}
}
private <T extends Exception> T remapMismatchedInputException(final MismatchedInputException e, Class<T> exClass) {
try {
final String fieldName =
e.getPath().stream().map(JsonMappingException.Reference::getFieldName).collect(Collectors.joining("."));
return exClass.getConstructor(String.class).newInstance(fieldName " must be of type " e.getTargetType().getSimpleName());
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException pE) {
throw new IllegalArgumentException("Cannot instantiate exception class " exClass.getSimpleName());
}
}
static class MyProxy {
@JsonProperty("foo") final FooProxy foo;
@JsonCreator
public MyProxy(@JsonProperty("foo") final FooProxy pFoo) {foo = pFoo;}
}
static class FooProxy {
@JsonProperty("bar") final Integer bar;
@JsonCreator
public FooProxy(@JsonProperty("bar") final Integer pBar) {bar = pBar;}
}
}
И приведет к:
java.lang.RuntimeException: foo.bar must be of type Integer
Комментарии:
1. e.getPath() возвращает список всех иерархий поля с ошибкой. В случае вложенных объектов ваш метод вернет ту же ошибку для каждого элемента в иерархии