2016-11-16 14 views
1

現在、JSONを使用しているサービスがあります。このJSONをわずかに再構築し(1つのプロパティを1レベル上に移動する)、サービスが古い構造だけでなく新しい構造も処理できるように、優雅な移行を実装したいと考えています。私たちはJSONの逆シリアル化にJacksonを使用しています。ジャクソンとデシリアライズする前のJSONを再構築する

ジャクソンとの直列化を解除する前にJSONをどのように再構築しますか?

ここにMCVEがあります。

{"reference": {"number" : "one", "startDate" : [2016, 11, 16], "serviceId" : "0815"}} 

我々はserviceId 1つ上のレベルに移動したい:

は、次のように私たちの古いJSONが見えたと

{"reference": {"number" : "one", "startDate" : [2016, 11, 16]}, "serviceId" : "0815"} 

これは、我々は新しいの両方古いから非直列化するクラスですJSON:

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のインスタンスで)動作しますが、最適な解決策ではありません。

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

「ラッピング」のデフォルトのデシリアライザは、より良い方法のようですが、これはで失敗します。

私が試したもう一つの方法は、BeanDeserializerModifierと自身のJsonDeserializer「ラップ」デフォルトのシリアライザを使用していました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

最高の伝統では、質問を投稿した直後に、私はこれを解決することができました。

2つのこと:

  • 私はNPEを避けるためにnewJsonParser.nextToken();をしなければなりませんでした。
  • ここで働いてDelegatingDeserializerDelegatingDeserializer

を拡張:

のpublic staticクラス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); 
       } 
      } 
     } 
    } 
} 
を拡張
関連する問題