У меня есть следующий 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);
}
}
Тем не менее, реальный вариант использования намного сложнее, и было бы полезнее сохранить логику внутри десериализатора (если это возможно) для упрощения повторного использования.
Спасибо за помощь.
В качестве повтора Chaosfire вот еще немного пояснений к моему делу. Более реальный JSON, который мне нужно разобрать, выглядит так
{"val1":501, "scale":2, "val2":407, "val3":86}
Значение поля scale используется как разделитель для нескольких полей.
Объект JSON имеет около 10 полей, как указано выше, и 50 других относительно простых полей. Причина, по которой я бы предпочел десериализатор, заключается в том, чтобы избежать огромного @JsonCreator, который в основном повторял бы входные значения.




Это невозможно с вашей текущей настройкой, вы предоставляете десериализатору только узел 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.
@JohnSmith Проверьте отредактированный ответ :)
Вот и все. Благодарю вас! Я многому научился из вашего ответа.
Я бы не рекомендовал вам включать бизнес-логику в логику десериализации. Пожалуйста, держите проблемы раздельно, это упростит поддержку кода.