迫使Jackson反序列化为特定的原始类型

时间:2011-09-06 08:01:17

标签: java json jackson

我使用Jackson 1.8.3将以下域对象序列化和反序列化为JSON

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。但是,在序列化和反序列化期间,Jackson将Long值(例如4)转换为Integers。

如何“强制”杰克逊将数字非十进制值反序列化为Long而不是Integer?

6 个答案:

答案 0 :(得分:23)

杰克逊2.6中有一个专门用于此案例的新功能:

将ObjectMapper配置为使用DeserializationFeature.USE_LONG_FOR_INTS

请参阅https://github.com/FasterXML/jackson-databind/issues/504

  

cowtowncoder在2015年5月19日推出了一个关闭此问题的提交     修复#504和#797

答案 1 :(得分:9)

如果将type声明为java.lang.Object,则Jackson使用“自然”映射,如果值适合32位,则使用Integer。除了自定义处理程序之外,您还必须强制包含类型信息(通过在field / getter旁边添加@JsonTypeInfo;或者通过启用所谓的“默认类型”)。

答案 2 :(得分:4)

我最终创建了一个自定义反序列化程序,因为在我的应用程序逻辑中,值只有四种不同的类型(DoubleLongIntegerString)。

我不确定这是否是最好的解决方案,但现在可行。

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());
  }
}

答案 3 :(得分:1)

我使用了类似下面的内容来解决这个问题。

@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();
        }
    }
}

答案 4 :(得分:0)

如果要将原语包装到特定的类中,则可以执行以下操作(例如Kotlin中的示例):

data class Age(
    @JsonValue
    val value: Int
)

现在,您的Int基元将被解析为Age类,反之亦然-Age类为Int基元。

答案 5 :(得分:0)

在杰克逊2中,我们可以使用TypeReference详细指定通用类型。 readValue()有一个重载的方法,该方法将 TypeReference 作为第二个参数:

  

readValue([File | String | etc],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>并使用类型引用读取值。