2017-09-05 2 views
3

Je suis d'adapter ce code Jackson:Comment désérialiser une interface avec json-b?

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

Le code d'origine fonctionne bien, même pour les objets d'entités imbriquées. Comment faire la même chose avec la nouvelle spécification json-b? J'ai essayé d'utiliser @JsonbTypeDeserializer mais

  1. Est-ce vraiment le chemin à parcourir? Il semble manquer de la simplicité de simplement spécifier une classe.
  2. Il ne semble pas fonctionner avec des entités imbriquées, ce qui est mon plus gros problème:

    javax.json.bind.JsonbException: Ne peut pas déduire un type de unmarshalling dans: Entité

  3. L'annotation n'est pas récupérée sur l'entité. Je dois ajouter manuellement avec JsonbConfig :: withDeserializers.

Voici mon code désérialiseur:

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

Toute velléité ou une aide très appréciée :-)

+1

J'ai créé une [pullrequest] (https://github.com/eclipse/yasson/pull/64) sur Yasson, qui devrait résoudre ce problème. Vous pouvez regarder l'utilisation dans ImplementationClassTest et le commenter. – Bravehorsie

Répondre

5

JSON-B ne déclare pas un moyen standard de sérialisation types polymorphes. Mais vous pouvez le réaliser manuellement en utilisant un sérialiseur et un désérialiseur personnalisés. Je vais l'expliquer sur un échantillon simple. Imaginez que vous ayez l'interface Shape et deux classes Square et Circle l'implémentant.

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

Vous devez sérialisation et la désérialisation Liste qui peut contenir des Shape implémentations.

sérialisation fonctionne hors de la boîte:

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

Le résultat sera:

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

Il est ok, mais il ne fonctionnera pas si vous essayez de désérialiser. Lors de la désérialisation, JSON-B doit créer une instance de Square ou Circle et il n'y a aucune information sur le type d'objet dans le document JSON.

Pour le corriger, nous devons ajouter ces informations manuellement. Ici, les sérialiseurs et les désérialiseurs vont aider. Nous pouvons créer un sérialiseur qui met un type d'objet sérialisé dans un document JSON et un désérialiseur qui le lit et crée une instance appropriée. Il peut se faire comme ceci:

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

Maintenant, nous avons besoin de le brancher dans le moteur JSON-B et essayer sérialisation. Vous ne devez pas oublier de passer un type générique au moteur JSON-B pendant la sérialisation/désérialisation. Sinon, cela ne fonctionnera pas correctement.

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

Le résultat de sérialisation sera:

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

]

Vous voyez ce type d'objet est ajouté par ShapeSerializer. Maintenant, nous allons essayer de désérialiser et les résultats d'impression:

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

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

Le résultat est:

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

Ainsi, il fonctionne parfaitement. J'espère que cela aide. :)

+1

Merci pour l'info. Cela ne fonctionne toujours pas dans notre cas, cependant. Nous avons seulement une implémentation de chaque interface. donc ce n'est pas vraiment polymorphe. OTH, il y a des interfaces imbriquées dans le json que nous devons désérialiser. L'implémentation de référence de JSON-B semble avoir des problèmes avec cela. En réalité. Si nous remplaçons tous les objets imbriqués dans les définitions de classes par des types concrets, cela fonctionne simplement. Voir testToJsonThereAndBackEntity() dans https://github.com/dAti/siren4j/blob/master/src/test/java/com/google/code/siren4j/converter/ReflectingConverterTest.java et le désérialiseur correspondant. – David

+0

Je suis confronté au même problème, je veux simplement configurer l'instance de JSONB pour retourner des implémentations concrètes pour une interface que je veux analyser. J'ai essayé avec un JsonbDeserializer contenant 'return ctx.deserialize (CustomerImpl.class, parser);' mais il en résulte stackoverflow. @David avez-vous déjà résolu ce problème? –