Jackson:JSON文字列をそのままオブジェクトに出し入れする
問題
言い方が難しいのだが、やりたいことはこういうことだ。
以下のように、フィールドyにはJSON文字列を格納する。
public class Foo {
String x = "a";
String y = "{\"b\": 123}";
}
このFooオブジェクトをJacksonで普通にJSON化すると以下になる。
{"x":"a","y":"{\"b\": 123}"}
これは当然だ、yに格納されている文字列は「単なる文字列」であって、JSONとはみなされず、全体がダブルクォートで囲まれ、中にあったダブルクォートはエスケープされてしまう。「”{\”b\”: 123}”」の二つの「\”」の部分だ。
しかし、これを以下のようにシリアライズしたい。
{"x":"a","y":{"b": 123}}
さらに、デシリアライズ時には元のyのJSON文字列を取得したい。
※シリアライズ側に細工して上記のようなJSON文字列が得られても、そのままではデシリアライズできない。なぜなら、yフィールドは文字列であり、そこに{“b”:123}をデシリアライズすることはできないからだ。
理由
これがやりたい理由というのはこうだ。
別の場所でJSON化されたオブジェクトAがあり、それをこちらのオブジェクトBに格納してJSON化した場合、先のようにただの文字列になってしまうのだが、これではPretty-Printした場合にもAは文字列として扱われてしまい、A部分はPretty-Printされず、構造がわかりにくい。
つまり、BをPretty-Printした時に、Aも同時にPrettyになって欲しいのである。
解決
以下で解決することができた。
// JSONをそのまま格納するクラスを作成
public class JsonAsIs {
public final String json;
public JsonAsIs(String json) {
this.json = json;
}
@Override
public String toString() {
return json;
}
}
...............
// JsonAsIsクラスのシリアライゼーション・デシリアライゼーションを指定するモジュール
SimpleModule module = new SimpleModule()
.addSerializer(JsonAsIs.class, new StdSerializer<JsonAsIs>(JsonAsIs.class) {
@Override
public void serialize(
JsonAsIs value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
jgen.writeRawValue(value.toString());
}
})
.addDeserializer(JsonAsIs.class, new StdDeserializer<JsonAsIs>(JsonAsIs.class) {
@Override
public JsonAsIs deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
TreeNode tree = jp.getCodec().readTree(jp);
return new JsonAsIs( tree.toString());
}
});
// モジュールを指定してObjectmapperを作成
ObjectMapper mapper = new ObjectMapper()
.setVisibility(PropertyAccessor.ALL, Visibility.NONE)
.setVisibility(PropertyAccessor.FIELD, Visibility.ANY)
.setSerializationInclusion(Include.NON_NULL)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
.registerModule(module)
;
これを以下のように使う。
public class Foo {
String x = "abc";
// StringではなくJsonAsIsに格納
JsonAsIs y = new JsonAsIs("{\"b\": 123}");
}
....
String serialized = mapper.writeValueAsString(new Foo());
System.out.println(serialized);
Foo deserialized = mapper.readValue(serialized, Foo.class);
System.out.println(deserialized.y);
出力結果は以下だ。
{"x":"abc","y":{"b": 123}}
{"b":123}
参考
以下を参考にした。
ディスカッション
コメント一覧
このクラスがpublic static になっている理由ってなんなのでしょうか?
ありません。別のクラス内部からそのまま持ってきたせいです。修正しときます