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など)を整数に変換します。
Jacksonに10進数以外の数値を整数ではなくLongに逆シリアル化するように「強制」するにはどうすればよいですか?
タイプがJava.lang.Objectとして宣言されている場合、Jacksonは値が32ビットに収まる場合に整数を使用する「自然な」マッピングを使用します。カスタムハンドラーとは別に、タイプ情報を強制的に含める必要があります(field/getterの横に@JsonTypeInfoを追加するか、いわゆる「デフォルトの入力」を有効にする)。
特にこの場合のために、Jackson2.6には新しい機能があります。
DeserializationFeature.USE_LONG_FOR_INTS
を使用するようにObjectMapperを構成します
https://github.com/FasterXML/jackson-databind/issues/504 を参照してください
cowtowncoderは、2015年5月19日にこの問題を解決するコミットをプッシュしました修正#504および#797
アプリケーションロジックには値のタイプが4つしかないため(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());
}
}
この問題を回避するために、以下のようなものを使用しました。
@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();
}
}
}
私の場合、ObjectMapperにDeserializationFeature.USE_LONG_FOR_INTSを使用したくありませんでした。これは、すべてのプロジェクトに影響するためです。私は次のソリューションを使用しました:カスタムデシリアライザーを使用します:
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型のフィールドに追加します:public class SomeTOWithObjectField {
//... other fields
@JsonDeserialize(using = LongInsteadOfIntegerDeserializer.class)
private Object value;
//... other fields
}
また、整数をlongとして逆シリアル化しましたが、String、boolean、doubleなどの他のタイプは、デフォルトであるはずのように逆シリアル化されました。
プリミティブを特定のクラスにラップする場合は、次のようにします(Kotlinの例)。
data class Age(
@JsonValue
val value: Int
)
そして今、あなたのInt
プリミティブは、Ageクラスに、またはその逆に解析されます。
ジャクソン2では、 TypeReference を使用してジェネリック型を詳細に指定できます。 TypeReferenceを2番目のパラメーターとして受け取るreadValue()
のオーバーロードメソッドがあります。
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>
そして、型参照を使用して値を読み取ります。