FasterJackson - не удается преобразовать ключ (строку JSON) в карте в объект, использующий аннотации AnyGetter/AnySetter.

Я пытаюсь десериализовать объект из карты (строка, объект) с помощью метода 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 чистым способом.

Мысли?


person Mich Betancourt    schedule 12.03.2018    source источник


Ответы (1)


Попробовал несколько вещей и пришел к следующему, внедрив собственный десериализатор FasterJackson:

JsonNode node = jsonParser.readValueAsTree();
AnyObject val = jsonParser.getCodec().getFactory().createParser(node.asText().getBytes()).readValueAs(AnyObject.class);

Я по-прежнему открыт для решений, которые могут быть проще или склоняться к более высокой производительности с меньшим количеством промежуточной генерации мусора во время синтаксического анализа.

person Mich Betancourt    schedule 12.03.2018