2017-09-05 42 views
3

我適應這個傑克遜代碼:如何使用json-b反序列化接口?

@JsonDeserialize(as = EntityImpl.class) 
public interface Entity { ... } 

原始代碼工作得很好,甚至嵌套的實體對象。

如何在新的json-b規範中做到這一點?我試過使用@JsonbTypeDeserializer,但

  1. 這真的是要走的路嗎?它似乎缺乏指定類的簡單性。
  2. 它似乎沒有使用嵌套實體合作,這是我最大的問題:

    javax.json.bind.JsonbException:無法推斷類型的解編爲:實體

  3. 該註釋在實體上未被拾取。我必須手動添加JsonbConfig :: withDeserializers。

這裏是我的解串器代碼:

public class EntityDeserializer implements JsonbDeserializer<Entity> { 

    @Override 
    public Entity deserialize(JsonParser parser, DeserializationContextdeserializationContext, Type runtimeType) { 
     Class<? extends Entity> entityClass = EntityImpl.class.asSubclass(Entity.class); 
     return deserializationContext.deserialize(entityClass, parser); 
    } 
} 

任何提示或幫助非常感謝:-)

+1

我已經在Yasson上創建了一個[pullrequest](https://github.com/eclipse/yasson/pull/64),它應該解決這個問題。您可以在ImplementationClassTest中查看使用情況並對其進行評論。 – Bravehorsie

回答

5

JSON-B不聲明序列化多態類型的標準方法。但是您可以使用自定義串行器和解串器手動實現它。我會在一個簡單的例子中解釋它。

想象一下,你有Shape接口和兩個類SquareCircle實現它。

public interface Shape { 
    double surface(); 
    double perimeter(); 
} 

public static class Square implements Shape { 
    private double side; 

    public Square() { 
    } 

    public Square(double side) { 
     this.side = side; 
    } 

    public double getSide() { 
     return side; 
    } 

    public void setSide(double side) { 
     this.side = side; 
    } 

    @Override 
    public String toString() { 
     return String.format("Square[side=%s]", side); 
    } 

    @Override 
    public double surface() { 
     return side * side; 
    } 

    @Override 
    public double perimeter() { 
     return 4 * side; 
    } 
} 

public static class Circle implements Shape { 
    private double radius; 

    public Circle() { 
    } 

    public Circle(double radius) { 
     this.radius = radius; 
    } 

    public double getRadius() { 
     return radius; 
    } 

    public void setRadius(double radius) { 
     this.radius = radius; 
    } 

    @Override 
    public String toString() { 
     return String.format("Circle[radius=%s]", radius); 
    } 

    @Override 
    public double surface() { 
     return Math.PI * radius * radius; 
    } 

    @Override 
    public double perimeter() { 
     return 2 * Math.PI * radius; 
    } 
} 

您需要序列化和反序列化List,其中可以包含任何Shape實現。

連載作品開箱:

JsonbConfig config = new JsonbConfig().withFormatting(true); 
Jsonb jsonb = JsonbBuilder.create(config); 

// Create a sample list 
List<SerializerSample.Shape> shapes = Arrays.asList(
      new SerializerSample.Square(2), 
      new SerializerSample.Circle(5)); 

// Serialize 
String json = jsonb.toJson(shapes); 
System.out.println(json); 

結果將是:

[ 
    { 
     "side": 2.0 
    }, 
    { 
     "radius": 5.0 
    } 
] 

這是確定的,但如果你試圖反序列化將無法正常工作。在反序列化期間,JSON-B需要創建一個SquareCircle的實例,並且JSON文檔中沒有關於對象類型的信息。

爲了解決這個問題,我們需要在那裏手動添加這些信息。這裏序列化器和反序列化器將會有所幫助。我們可以創建一個序列化器,它將一種序列化對象放入JSON文檔和解串器中,該序列化器讀取它並創建一個適當的實例。它可以這樣做:

public static class ShapeSerializer implements JsonbSerializer<SerializerSample.Shape> { 
    @Override 
    public void serialize(SerializerSample.Shape shape, JsonGenerator generator, SerializationContext ctx) { 
     generator.writeStartObject(); 
     ctx.serialize(shape.getClass().getName(), shape, generator); 
     generator.writeEnd(); 
    } 
} 

public static class ShapeDeserializer implements JsonbDeserializer<SerializerSample.Shape> { 
    @Override 
    public SerializerSample.Shape deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { 
     parser.next(); 

     String className = parser.getString(); 
     parser.next(); 

     try { 
      return ctx.deserialize(Class.forName(className).asSubclass(Shape.class), parser); 
     } catch (ClassNotFoundException e) { 
      e.printStackTrace(); 
      throw new JsonbException("Cannot deserialize object."); 
     } 
    } 
} 

現在我們需要將它插入到JSON-B引擎並嘗試序列化。在序列化/反序列化過程中,您不應該忘記將通用類型傳遞給JSON-B引擎。否則它將無法正常工作。

// Create JSONB engine with pretty output and custom serializer/deserializer 
JsonbConfig config = new JsonbConfig() 
     .withFormatting(true) 
     .withSerializers(new SerializerSample.ShapeSerializer()) 
     .withDeserializers(new SerializerSample.ShapeDeserializer()); 
Jsonb jsonb = JsonbBuilder.create(config); 

// Create a sample list 
List<SerializerSample.Shape> shapes = Arrays.asList(
     new SerializerSample.Square(2), 
     new SerializerSample.Circle(5)); 

// Type of our list 
Type type = new ArrayList<SerializerSample.Shape>() {}.getClass().getGenericSuperclass(); 

// Serialize 
System.out.println("Serialization:"); 
String json = jsonb.toJson(shapes); 
System.out.println(json); 

系列化的結果將是:

[ 
    { 
     "jsonb.sample.SerializerSample$Square": { 
      "side": 2.0 
     } 
    }, 
    { 
     "jsonb.sample.SerializerSample$Circle": { 
      "radius": 5.0 
     } 
    } 

]

你看到該對象類型由ShapeSerializer加入。現在,讓我們嘗試反序列化和打印結果:

// Deserialize 
List<SerializerSample.Shape> deserializedShapes = jsonb.fromJson(json, type); 

// Print results 
System.out.println("Deserialization:"); 
for (SerializerSample.Shape shape : deserializedShapes) { 
    System.out.println(shape); 
} 

結果是:

Square[side=2.0] 
Circle[radius=5.0] 

因此,它完美的作品。希望能幫助到你。 :)

+1

感謝您的信息。儘管如此,它仍然不適用於我們的情況。我們只有每個接口的一個實現。所以它不是真正的多態。 OTH,我們需要反序列化json中的嵌套接口。 JSON-B的參考實現似乎有問題。事實上。如果我們用一些具體類型替換類定義中的所有嵌套對象,它就會起作用。請參閱https://github.com/dAti/siren4j/blob/master/src/test/java/com/google/code/siren4j/converter/ReflectingConverterTest.java中的testToJsonThereAndBackEntity()和相應的解串器。 – David

+0

我面臨同樣的問題,我只是想配置Jsonb實例來返回我想要解析的接口的具體實現。我試着用一個包含'return ctx.deserialize(CustomerImpl.class,parser)'的JsonbDeserializer;'但是它會導致stackoverflow。 @David是否已經解決了這個問題? –