2015-02-12 2 views
3

У меня есть десериализатор для определенного класса, который нуждается в некотором упорядочении при чтении полей. Предположим, у меня есть два поля в моем классе (field1 и field2), и для того, чтобы читать field2, ему сначала нужно field1.Чтение пропущенных детей в Jackson Custom Deserializer

Например для следующих данных JSon это работает, потому что, когда десериализатор разбирает field2, field1 уже установлен:

{"field1": 3, "field2": 4} 

Однако если мы поменяем поля:

{"field2": 4, "field1": 3} 

мне нужно пропустить field2 через jp.skipChildren т.к. field1 не установлен. Когда разобран field1, Джексон должен перечитать и разобрать field2.

Один из вариантов состоит в том, чтобы проанализировать поле2 вместо пропуска и удерживать его в переменной, чтобы при установке поля 1 он мог использовать переменную, содержащую данные в поле2. Однако; основанный на значении field1, мне может не понадобиться разобрать field2, поэтому я ищу лучшее решение, так как производительность важна в этой части кода.

Я использую метод Mapper.readValue(byte[], MyClass.class), и кажется, что Джексон использует ReaderBasedJsonParser для разбора. Несмотря на то, что можно получить позицию маркера, я не смог найти способ установить позицию маркера.

+0

У вас возникла проблема в том, что вы полагаетесь на порядок членов объекта, который RFC для JSON (RFC 7159) говорит, что это не имеет значения. Зачем вам заказ? Почему бы вам просто не попробовать и десериализировать оба? – fge

+0

@fge Поскольку определенное поле содержит информацию о том, как следует анализировать другие поля. Разбор дерева JSON для общего JsonNode - это всегда вариант, но он имеет накладные расходы, поэтому я хотел его избежать. – Boyolame

+0

@fge, так как вы знаете об Avro, я хотел бы разобрать JSON на модель Avro. Некоторые из полей в JSON содержат информацию об схеме Avro, поэтому, чтобы разобрать дерево, сначала мне нужно знать эти значения. Вы можете посмотреть код отсюда: https://github.com/buremba/rakam/blob/19d96f7ff35df81b4faa4a57452520f905122c74/src/main/java/org/rakam/collection/event/EventDeserializer.java – Boyolame

ответ

2

Наконец-то я нашел способ сделать это. Это на самом деле обходной путь, но он передает те тесты, которые я написал. Когда вы передаете массив байтов в mapper.readValue, он использует ReaderBasedJsonParser, который выполняет итерацию по массиву и анализирует дерево JSON.

public static class SaveableReaderBasedJsonParser extends ReaderBasedJsonParser { 
    private int savedInputPtr = -1; 

    public SaveableReaderBasedJsonParser(IOContext ctxt, int features, Reader r, ObjectCodec codec, CharsToNameCanonicalizer st, char[] inputBuffer, int start, int end, boolean bufferRecyclable) { 
     super(ctxt, features, r, codec, st, inputBuffer, start, end, bufferRecyclable); 
    } 

    public void save() { 
     savedInputPtr = _inputPtr; 
    } 

    public boolean isSaved() { 
     return savedInputPtr>-1; 
    } 

    public void load() { 
     _currToken = JsonToken.START_OBJECT; 
     _inputPtr = savedInputPtr; 
     _parsingContext = _parsingContext.createChildObjectContext(0, 0); 
    } 
} 

При использовании этого JsonParser, то JsonParser экземпляр, который будет передан на ваш десериализатор EventDeserializer.deserialize (JsonParser, DeserializationContext) будет SaveableReaderBasedJsonParser, так что вы можете смело бросить его.

Если вы хотите сохранить позицию, позвоните по телефону jp.save(), так что, когда вам нужно вернуться назад, вы можете позвонить только по телефону jp.load().

Как я уже сказал, это на самом деле обходной путь, но когда вам нужна эта функция и вы не хотите анализировать дерево дважды по соображениям производительности, вы можете попробовать.

2

Для использования пользовательского десериализатора используется потоковый API. Невозможно вернуться, перепрофилировать и т. Д.

Вы зарегистрировали пользовательский десериализатор для типа поля или для класса, который содержит поля, которые нуждаются в этом специальном лечении?

Если вы зарегистрируете десериализатор для класса, который содержит эти поля, вы можете просто использовать потоковый API, прочитать во всех полях экземпляра, временно сохранить их, например. в HashMap, а затем назначьте значения.

BTW: Ваш вопрос пахнет XYProblem. Возможно, вам стоит написать еще один вопрос о причине, вам нужно решить эту проблему здесь и посмотреть, есть ли для этого лучший подход.

+0

Я нашел способ сделать это, используя пользовательский JsonParser. К сожалению, HashMap поставляется с накладными расходами, и, как я сказал, производительность важна для этой части приложения. – Boyolame

Смежные вопросы