我們有一個目前正在使用JSON的服務。我們想稍微重構這個JSON(向上移動一個屬性),但也要實現優雅的遷移,以便我們的服務可以處理舊結構以及新結構。我們使用Jackson進行JSON反序列化。在與Jackson反序列化之前重構JSON
在與Jackson進行反序列化之前,我們如何重構JSON?
這是一個MCVE。
假設我們的老JSON如下所示:
{"reference": {"number" : "one", "startDate" : [2016, 11, 16], "serviceId" : "0815"}}
我們要移動serviceId
一個級別:
{"reference": {"number" : "one", "startDate" : [2016, 11, 16]}, "serviceId" : "0815"}
這是我們想從既的新老反序列化的類JSONs:
public final static class Container {
public final Reference reference;
public final String serviceId;
@JsonCreator
public Container(@JsonProperty("reference") Reference reference, @JsonProperty("serviceId") String serviceId) {
this.reference = reference;
this.serviceId = serviceId;
}
}
public final static class Reference {
public final String number;
public final LocalDate startDate;
@JsonCreator
public Reference(@JsonProperty("number") String number, @JsonProperty("startDate") LocalDate startDate) {
this.number = number;
this.startDate = startDate;
}
}
我們在我希望serviceId
在Container
,而不是在這兩個類。
我已經有了工作如下解串器:
public static class ServiceIdMigratingContainerDeserializer extends JsonDeserializer<Container> {
private final ObjectMapper objectMapper;
{
objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
@Override
public Container deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
ObjectNode node = p.readValueAsTree();
migrate(node);
return objectMapper.treeToValue(node, Container.class);
}
private void migrate(ObjectNode containerNode) {
TreeNode referenceNode = containerNode.get("reference");
if (referenceNode != null && referenceNode.isObject()) {
TreeNode serviceIdNode = containerNode.get("serviceId");
if (serviceIdNode == null) {
TreeNode referenceServiceIdNode = referenceNode.get("serviceId");
if (referenceServiceIdNode != null && referenceServiceIdNode.isValueNode()) {
containerNode.set("serviceId", (ValueNode) referenceServiceIdNode);
}
}
}
}
}
這解串器首先檢索樹,操縱它,然後用自己的ObjectMapper
實例解串器它。它有效,但我們真的不喜歡這裏有另一個ObjectMapper
的實例。如果我們不創建它並以某種方式使用系統範圍的ObjectMapper
實例,我們會得到一個無限循環,因爲當我們嘗試調用objectMapper.treeToValue
時,我們的反序列化器會被遞歸調用。所以這個工作(自己的實例ObjectMapper
),但它不是一個最佳的解決方案。
我試着用一個BeanDeserializerModifier
和自己JsonDeserializer
其「包裝」的默認的序列的另一種方法:
public static class ServiceIdMigrationBeanDeserializerModifier extends BeanDeserializerModifier {
@Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc,
JsonDeserializer<?> defaultDeserializer) {
if (beanDesc.getBeanClass() == Container.class) {
return new ModifiedServiceIdMigratingContainerDeserializer((JsonDeserializer<Container>) defaultDeserializer);
} else {
return defaultDeserializer;
}
}
}
public static class ModifiedServiceIdMigratingContainerDeserializer extends JsonDeserializer<Container> {
private final JsonDeserializer<Container> defaultDeserializer;
public ModifiedServiceIdMigratingContainerDeserializer(JsonDeserializer<Container> defaultDeserializer) {
this.defaultDeserializer = defaultDeserializer;
}
@Override
public Container deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
ObjectNode node = p.readValueAsTree();
migrate(node);
return defaultDeserializer.deserialize(new TreeTraversingParser(node, p.getCodec()), ctxt);
}
private void migrate(ObjectNode containerNode) {
TreeNode referenceNode = containerNode.get("reference");
if (referenceNode != null && referenceNode.isObject()) {
TreeNode serviceIdNode = containerNode.get("serviceId");
if (serviceIdNode == null) {
TreeNode referenceServiceIdNode = referenceNode.get("serviceId");
if (referenceServiceIdNode != null && referenceServiceIdNode.isValueNode()) {
containerNode.set("serviceId", (ValueNode) referenceServiceIdNode);
}
}
}
}
}
「包裝」默認解串器似乎是一個更好的辦法,但這種失敗NPE:
java.lang.NullPointerException
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:157)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:150)
at de.db.vz.rikernpushadapter.migration.ServiceIdMigrationTest$ModifiedServiceIdMigratingContainerDeserializer.deserialize(ServiceIdMigrationTest.java:235)
at de.db.vz.rikernpushadapter.migration.ServiceIdMigrationTest$ModifiedServiceIdMigratingContainerDeserializer.deserialize(ServiceIdMigrationTest.java:1)
at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:1623)
at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1217)
at ...
整個MCVE代碼位於以下PasteBin。這是一個單類所有包含測試案例,它演示了這兩種方法。 migratesViaDeserializerModifierAndUnmarshalsServiceId
失敗。
因此,這給我留下了一個問題:
我們如何重組與傑克遜反序列化JSON之前?