2016-11-16 58 views
1

我們有一個目前正在使用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; 
     } 
    } 

我們在我希望serviceIdContainer,而不是在這兩個類。

我已經有了工作如下解串器:

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之前?

回答

0

在最好的傳統,發佈問題後,我已經設法解決這個問題。

兩件事情:

  • 我不得不這樣做newJsonParser.nextToken();避免NPE。
  • 擴展DelegatingDeserializer

這裏有一個工作DelegatingDeserializer

公共靜態類ModifiedServiceIdMigratingContainerDeserializer擴展DelegatingDeserializer {

public ModifiedServiceIdMigratingContainerDeserializer(JsonDeserializer<?> defaultDeserializer) { 
     super(defaultDeserializer); 
    } 

    @Override 
    protected JsonDeserializer<?> newDelegatingInstance(JsonDeserializer<?> newDelegatee) { 
     return new ModifiedServiceIdMigratingContainerDeserializer(newDelegatee); 
    } 

    @Override 
    public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { 
     return super.deserialize(restructure(p), ctxt); 
    } 

    @Override 
    public Object deserialize(JsonParser p, DeserializationContext ctxt, Object intoValue) throws IOException, 
      JsonProcessingException { 
     return super.deserialize(restructure(p), ctxt, intoValue); 
    } 

    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) 
      throws IOException, JsonProcessingException { 
     return super.deserializeWithType(restructure(jp), ctxt, typeDeserializer); 
    } 

    public JsonParser restructure(JsonParser p) throws IOException, JsonParseException { 
     final ObjectNode node = p.readValueAsTree(); 
     migrate(node); 
     final TreeTraversingParser newJsonParser = new TreeTraversingParser(node, p.getCodec()); 
     newJsonParser.nextToken(); 
     return newJsonParser; 
    } 

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