Десериализовать несколько полей в одно по Джексону

У меня есть следующий json

{"val": 501, "scale": 2}

Поле scale показывает, насколько сдвинута десятичная точка в значении (поле val). В данном случае места нет, поэтому результатом является значение 5.01.

Я хотел бы сопоставить его со следующим классом

public class ValueClass {
    @JsonProperty("val")
    @JsonDeserialize(using = ValueDeserializer.class)
    private BigDecimal value;
}

Я хотел бы использовать для этого собственный десериализатор, однако мне не ясно, как получить доступ к другим полям JSON из десериализатора, а не к аннотированному.

@SuppressWarnings("serial")
class ValueDeserializer extends StdDeserializer<BigDecimal> {

    protected ValueDeserializer() {
        super(BigDecimal.class);
    }

    @Override
    public BigDecimal deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        var val = p.readValueAs(Integer.class);
        int scale = ??; // <-- How to access "scale" field here?
        return new BigDecimal(val).scaleByPowerOfTen(-scale);
    }
}

P.S. Я знаю, что смогу @JsonCreator в этом простом случае.

public class ValueClass {
    private BigDecimal value;

    @JsonCreator
    public ValueClass(//
            @JsonProperty("val") Integer val, //
            @JsonProperty("scale") Integer scale //
    ) {
        this.value = new BigDecimal(val).scaleByPowerOfTen(-scale);
    }
}

Тем не менее, реальный вариант использования намного сложнее, и было бы полезнее сохранить логику внутри десериализатора (если это возможно) для упрощения повторного использования.

Спасибо за помощь.

Изменить 1

В качестве повтора Chaosfire вот еще немного пояснений к моему делу. Более реальный JSON, который мне нужно разобрать, выглядит так

{"val1":501, "scale":2, "val2":407, "val3":86}

Значение поля scale используется как разделитель для нескольких полей.

Объект JSON имеет около 10 полей, как указано выше, и 50 других относительно простых полей. Причина, по которой я бы предпочел десериализатор, заключается в том, чтобы избежать огромного @JsonCreator, который в основном повторял бы входные значения.

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

cyberbrain 09.05.2022 10:05
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
1
61
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это невозможно с вашей текущей настройкой, вы предоставляете десериализатору только узел val, но вам нужен весь объект для доступа к узлу scale.

Поскольку использование @JsonCreator нежелательно, вы можете изменить десериализатор для обработки ValueClass:

public class ValueDeserializer extends StdDeserializer<ValueClass> {

    public ValueDeserializer() {
        super(ValueClass.class);
    }

    @Override
    public ValueClass deserialize(JsonParser parser, DeserializationContext context) throws IOException {
        JsonNode node = parser.getCodec().readTree(parser);
        int scale = node.get("scale").asInt();
        ValueClass valueClass = new ValueClass();

        JavaType javaType = context.getTypeFactory().constructType(ValueClass.class);
        // Introspect the given type
        BeanDescription beanDescription = context.getConfig().introspect(javaType);
        // Find properties
        List<BeanPropertyDefinition> properties = beanDescription.findProperties();
        for (BeanPropertyDefinition property : properties) {
            String propertyName = property.getName();//get name as in json
            String propertyValue = node.get(propertyName).asText();
            BigDecimal decimal = new BigDecimal(propertyValue).scaleByPowerOfTen(-scale);
            AnnotatedMember accessor = property.getMutator();
            accessor.setValue(valueClass, decimal);
        }
        return valueClass;
    }
}

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

И простой тест:

@JsonDeserialize(using = ValueDeserializer.class)
public class ValueClass {

    @JsonProperty("val1")
    private BigDecimal value1;
    private BigDecimal val2;
    private BigDecimal val3;

    //setters and getters

    @Override
    public String toString() {
        return "ValueClass{" +
                "value1 = " + value1 +
                ", val2 = " + val2 +
                ", val3 = " + val3 +
                '}';
    }
}

Главный:

public class Main {

    public static void main(String[] args) throws Exception {
        String json = "{\"val1\":501, \"scale\":2, \"val2\":407, \"val3\":86}";
        ObjectMapper mapper = new ObjectMapper();
        ValueClass value = mapper.readValue(json, ValueClass.class);
        System.out.println(value);
    }
}

Принты - ValueClass{value1=5.01, val2=4.07, val3=0.86}.

Спасибо за совет :-). Однако, и я сожалею об этом, я не описал свой случай достаточно подробно во время первоначального вопроса. См. Редактировать 1.

John Smith 09.05.2022 12:45

@JohnSmith Проверьте отредактированный ответ :)

Chaosfire 09.05.2022 18:45

Вот и все. Благодарю вас! Я многому научился из вашего ответа.

John Smith 10.05.2022 15:54

Другие вопросы по теме