2017-09-05 3 views
3

Ich Anpassung dieses Jackson Code:Wie eine Schnittstelle mit json-b deserialisieren?

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

Der ursprüngliche Code funktioniert gut, auch für verschachtelte Entity-Objekte.

Wie das gleiche mit der neuen json-b-Spezifikation zu tun? Ich versuchte mit @ JsonbTypeDeserializer aber

  1. Ist das wirklich der Weg zu gehen? Es scheint die Einfachheit zu fehlen, nur eine Klasse zu spezifizieren.
  2. Es scheint nicht mit verschachtelten Einheiten zu arbeiten, was mein größtes Problem ist:

    javax.json.bind.JsonbException: Entity

  3. : Es kann kein Typ für unmarshalling in folgern Die Anmerkung wird nicht auf Entität aufgenommen. Ich muss manuell mit JsonbConfig :: withDeserializers hinzufügen.

Hier ist mein Deserializer Code:

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

Jeder Hinweis oder stark :-)

+1

Ich habe eine [pullrequest] (https://github.com/eclipse/yasson/pull/64) auf Yasson erstellt, die dieses Problem beheben sollte. Sie können die Verwendung in ImplementationClassTest überprüfen und sie kommentieren. – Bravehorsie

Antwort

5

JSON-B deklarieren keinen Standardweg von Serialisierung polymorphen Arten geschätzt helfen. Aber Sie können es manuell mit benutzerdefinierten Serializer und Deserializer erreichen. Ich werde es an einer einfachen Probe erklären.

Stellen Sie sich vor, Sie haben Shape Schnittstelle und zwei Klassen Square und Circle implementieren es.

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

Sie müssen serialisiert und deserialisiert Liste, die alle Shape Implementierungen enthalten.

Serialisierung arbeitet aus der Box:

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

Das Ergebnis wird sein:

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

Es ist in Ordnung, aber es wird nicht funktionieren, wenn Sie versuchen, es zu deserialisieren. Während der Deserialisierung muss JSON-B eine Instanz von Square oder Circle erstellen, und es gibt keine Informationen zum Objekttyp im JSON-Dokument.

Um es zu beheben, müssen wir diese Informationen dort manuell hinzufügen. Hier helfen Serialisierer und Deserialisierer. Wir können einen Serializer erstellen, der ein serialisiertes Objekt in JSON-Dokument und Deserializer einfügt, das es liest und eine richtige Instanz erstellt. Es kann wie folgt geschehen:

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

Jetzt müssen wir es in JSON-B Motor stecken und versuchen, Serialisierung. Sie sollten nicht vergessen, während der Serialisierung/Deserialisierung einen generischen Typ an die JSON-B-Engine zu übergeben. Sonst wird es nicht richtig funktionieren.

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

Das Ergebnis der Serialisierung wird:

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

]

Sie sehen, dass Objekttyp von ShapeSerializer hinzugefügt wird. Jetzt versuchen wir es und Druckergebnisse deserialisieren:

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

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

Das Ergebnis ist:

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

So ist es perfekt funktioniert. Ich hoffe es hilft. :)

+1

Danke für die Info. In unserem Fall funktioniert das immer noch nicht. Wir haben nur eine Implementierung jeder Schnittstelle. also ist es nicht wirklich polymorph. OTH, es gibt verschachtelte Interfaces im JSON, die wir deserialisieren müssen. Die Referenzimplementierung von JSON-B scheint damit Probleme zu haben. Eigentlich. Wenn wir alle verschachtelten Objekte in den Klassendefinitionen durch einige konkrete Typen ersetzen, funktioniert es einfach. Siehe testToJsonThereAndBackEntity() in https://github.com/dAti/siren4j/blob/master/src/test/java/com/google/code/siren4j/converter/ReflectingConverterTest.java und den entsprechenden Deserializer. – David

+0

Ich bin mit dem gleichen Problem konfrontiert, ich möchte einfach die Jsonb-Instanz konfigurieren, um konkrete Implementierungen für eine Schnittstelle, die ich analysieren möchte, zurückzugeben. Ich probierte es mit einem JsonbDeserializer, der 'return ctx.deserialize (CustomerImpl.class, parser);' enthielt, aber es führte zu stackoverflow. @David hast du dieses Problem schon gelöst? –

Verwandte Themen