Я пытаюсь десериализовать объект из карты (строка, объект) с помощью метода FasterJackson ObjectMapper.convertValue (карта, ComplexRecord.class).
Мой целевой объект содержит много других объектов внутри него. Единственным проблемным объектом является встроенный объект AnyObject, который использует методы FasterJackson (Джексона) AnyGetter/AnySetter.
Экземпляр AnyObject работает для всех других вариантов использования с FasterJackson, за исключением, на данный момент, этого случая, где он требует более сложной десериализации «ComplexRecord».
Вот как это выглядит:
@Data
public class ComplexRecord {
private String id;
private AnyObject data;
private String status;
private Instant createdDateTime;
}
@Data
public class AnyObject {
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
private final Map<String, Object> data;
public AnyObject() {
this(new LinkedHashMap<>());
}
public AnyObject(Map<String, Object> sourceData) {
this.data = new LinkedHashMap<>(sourceData);
}
@JsonAnySetter
public void setData(String name, Object value) {
this.data.put(name, value);
}
@JsonAnyGetter
public Map<String, Object> getData() {
return Collections.unmodifiableMap(this.data);
}
}
Когда я пытаюсь использовать ObjectMapper.convertValue(map, ComplexRecord.class), мне не удается десериализовать поле «данные» из-за этой ошибки:
Cannot construct instance of `AnyObject`
(although at least one Creator exists): no String-argument
constructor/factory method to deserialize from String value
('{"id":"123","v":"anything"}')
at [Source: UNKNOWN; line: -1, column: -1]
Я хотел бы найти очень чистый обходной путь для этого. Эта проблема, по-видимому, связана с тем, что я использую метод ObjectMapper.convertValue из сложного источника карты, где ключ «данные» изначально доступен в виде строки. Если я выполняю аналогичную операцию, но с ObjectMapper.readValue() вместо ObjectMapper.convertValue(), тогда десериализация в AnyObject работает нормально.
Поскольку я не могу позволить себе роскошь изменить исходный объект Map на что-то, что будет работать с методом ObjectMapper.readValue(), у меня может остаться только несколько вариантов.
Один из вариантов, который я нашел, — это использование пользовательского десериализатора FasterJackson. Единственная загвоздка, которую я вижу, заключается в том, что нет четкого способа доступа к внутреннему ObjectMapper, который предоставляется десериализатору. Когда я отлаживаю десериализатор, я вижу, что JsonParser.getCodec() является ObjectMapper, но даже при попытке выполнить readValue в пользовательском десериализаторе десериализация завершается с той же ошибкой. то есть
AnyObject value = jsonParser.getCodec().readValue(p, AnyObject.class);
Однако следующий набор вызовов работает нормально:
String stringValue = jsonParser.getCodec().readValue(p, String.class);
AnyObject anyObject = objMapper.readValue(stringValue, AnyObject.class);
Кроме того, что это двухэтапный процесс десериализации, а не одноэтапный процесс; единственная другая проблема заключается в том, что у меня нет чистого способа использовать экземпляр «objMapper» (ObjectMapper), о котором я говорю выше, без преобразования кодека в экземпляр ObjectMapper.
Мне это кажется хаком, но я хотел бы узнать ваши мысли, а также посмотреть, есть ли какое-либо другое более дружественное решение.
Есть еще несколько мыслей/вариантов, но я хотел бы получить непредвзятое мнение по этому поводу, чтобы увидеть, есть ли гораздо более простой способ обработки этого типа «сложного» преобразования.
Самый идеальный результат — принудительное приведение String к нужному мне типу — AnyObject. С помощью аннотации или какой-то очень простой стратегии.
Но если мне придется иметь дело с этим с помощью пользовательского десериализатора, я бы предпочел иметь возможность получить дескриптор внутрипроцессного ObjectMapper чистым способом.
Мысли?