Принуждение Джексона к десериализации в конкретный тип примитивов - PullRequest
12 голосов
/ 06 сентября 2011

Я сериализую и десериализую следующий объект домена в JSON, используя Jackson 1.8.3

public class Node {
    private String key;
    private Object value;
    private List<Node> children = new ArrayList<Node>();
    /* getters and setters omitted for brevity */
}

Затем объект сериализуется и десериализуется с использованием следующего кода

ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(destination, rootNode);

А затем десериализовано с

mapper.readValue(destination, Node.class);

Исходные значения объекта: Strings, Doubles, Longs или Booleans. Однако во время сериализации и десериализации Джексон преобразует длинные значения (например, 4) в целые числа.

Как я могу "заставить" Джексона десериализовать числовые недесятичные значения в Long вместо Integer?

Ответы [ 7 ]

25 голосов
/ 22 июля 2015

Специально для этого случая в Jackson 2.6 появилась новая функция:

настроить ObjectMapper для использования DeserializationFeature.USE_LONG_FOR_INTS

см. https://github.com/FasterXML/jackson-databind/issues/504

cowtowncoderвыдвинул коммит, который закрыл эту проблему 19 мая 2015 г. Исправление № 504 и № 797

9 голосов
/ 07 сентября 2011

Если тип объявлен как java.lang.Object, Джексон использует «естественное» отображение, которое использует Integer, если значение помещается в 32 бита.Помимо пользовательских обработчиков вам придется принудительно включать информацию о типе (либо добавляя @JsonTypeInfo рядом с полем / получателем, либо включив так называемую «типизацию по умолчанию»).

4 голосов
/ 07 сентября 2011

В итоге я создал собственный десериализатор, поскольку в моей логике приложения есть только четыре разных типа значений (Double, Long, Integer и String).

Я не уверен, что это лучшее из возможных решений, но пока оно работает.

public class MyDeserializer extends JsonDeserializer<Object> {

@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt)
        throws IOException, JsonProcessingException {
    try {
        Long l = Long.valueOf(p.getText());
        return l;
    } catch (NumberFormatException nfe) {
      // Not a Long
    }
    try {
      Double d = Double.valueOf(p.getText());
      return d;
    } catch (NumberFormatException nfe) {
      // Not a Double
    }
    if ("TRUE".equalsIgnoreCase(p.getText())
          || "FALSE".equalsIgnoreCase(p.getText())) {
      // Looks like a boolean
      return Boolean.valueOf(p.getText());
    }
    return String.valueOf(p.getText());
  }
}
1 голос
/ 14 сентября 2018

В моем случае я не хотел использовать DeserializationFeature.USE_LONG_FOR_INTS для ObjectMapper, потому что это повлияет на весь проект. Я использовал следующее решение: используйте специальный десериализатор:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;

import java.io.IOException;

public class LongInsteadOfIntegerDeserializer extends JsonDeserializer<Object> {

    @Override
    public Object deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
        ObjectCodec codec = jsonParser.getCodec();
        JsonNode jsonNode = codec.readTree(jsonParser);
        if (jsonNode.isInt()) {
            return jsonNode.asLong();
        }
        return codec.treeToValue(jsonNode, Object.class);
    }
}

И добавьте его в поле типа Object: открытый класс SomeTOWithObjectField {

    //...  other fields

    @JsonDeserialize(using = LongInsteadOfIntegerDeserializer.class)
    private Object value;

    //...  other fields
}

И он десериализовал целые числа как long, но другие типы, такие как String, boolean, double и т. Д. Были десериализованы, как и должно быть по умолчанию.

1 голос
/ 24 августа 2013

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

@JsonIgnoreProperties(ignoreUnknown = true)
public class Message {
    public Long ID;

    @JsonCreator
    private Message(Map<String,Object> properties) {
        try {
            this.ID = (Long) properties.get("id");
        } catch (ClassCastException e) {
            this.ID = ((Integer) properties.get("id")).longValue();
        }
    }
}
0 голосов
/ 12 апреля 2019

В Джексоне 2 мы можем использовать TypeReference для подробного указания универсального типа. Существует и перегруженный метод для readValue(), который принимает TypeReference в качестве 2-го параметра:

readValue ([Файл | Строка | и т. Д.], Com.fasterxml.jackson.core.type.TypeReference))

Если вы хотите получить список Long вместо Integer, вы можете сделать следующее.

ObjectMapper mapper = new ObjectMapper();
TypeReference ref = new TypeReference<List<Integer>>() { };
List<Integer> list = mapper.readValue(<jsonString>, ref);

Это работает и для карт:

TypeReference ref = new TypeReference<Map<String,Long>>() { };
Map<String, Long> map = mapper.readValue(<jsonString>, ref);

В вашем случае вы можете преобразовать свой класс в общий. то есть Node<T>. При создании узлов сделайте как Node<String/Integer/etc> И используйте ссылку на тип, чтобы прочитать значение.

0 голосов
/ 18 ноября 2018

Если вы хотите превратить примитив в определенный класс, вы можете выполнить следующее (пример в Kotlin):

data class Age(
    @JsonValue
    val value: Int
)

И теперь ваши Int примитивы будут проанализированы в класс Age, и наоборот - в возрастной класс в примитив Int.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...