2017-04-08 2 views
1

Il semble y avoir un problème avec la sérialisation de BufferedImages dans JSON à l'aide de GSON. J'utilise Derby pour stocker des images. Quand j'interroge la base de données je construis un JavaBean qui a quelques champs de texte et un champ BufferedImage. J'utilise ensuite GSON pour convertir le JavaBean en JSON, et c'est là que l'exception se produit.Le format GSON ne peut pas sérialiser BufferedImages

Le message d'exception est la suivante:
java.lang.IllegalArgumentException: classe sun.awt.image.ByteInterleavedRaster déclare plusieurs champs JSON nommés maxX

J'ai trouvé des problèmes similaires ici et GSON java.lang.IllegalArgumentException: class 'xx' declares multiple JSON fields named 'XX' AND StackOverflowError ici class A declares multiple JSON fields

Mais le problème est avec la bibliothèque awt incluse avec Java. Je pourrais suivre les réponses fournies dans ces autres réponses stackoverflow si je pouvais accéder au code source AWT, mais comment faire?

Répondre

2

Vous devez savoir que toutes les classes ne sont pas conçues pour être (dé) sérialisées, en particulier si la (dé) sérialisation est basée sur la structure binaire de la classe cible. Votre approche a au moins les points faibles suivants:

  • les sun.awt.image.ByteInterleavedRaster champs de classe ne sont pas nécessairement les mêmes sur une autre machine virtuelle Java/JRE, vous pouvez ainsi obtenir être verrouillé fournisseur;
  • données binaires persistantes dans JSON n'est probablement pas le meilleur choix (probablement énorme et terrible consommation de mémoire lors de (dé) sérialisation, consommation de stockage, performance) - peut-être un stockage blob générique est mieux pour les données binaires? Lire une image avec Java AWT et la réécrire ne garantit pas la même sortie binaire: par exemple, mon image de test, 1,2K, a été désérialisée en tant qu'image d'une autre taille, 0,9 K;
  • Vous devez choisir le format d'image persistant cible ou détecter le format le plus efficace (comment?).

Considérez la classe simple suivante:

final class ImageHolder { 

    final RenderedImage image; 

    ImageHolder(final RenderedImage image) { 
     this.image = image; 
    } 

} 

Maintenant, vous devez créer un adaptateur de type à dire Gson comment une instance de type particulier peut être stocké et restauré:

final class RenderedImageTypeAdapter 
     extends TypeAdapter<RenderedImage> { 

    private static final TypeAdapter<RenderedImage> renderedImageTypeAdapter = new RenderedImageTypeAdapter().nullSafe(); 

    private RenderedImageTypeAdapter() { 
    } 

    static TypeAdapter<RenderedImage> getRenderedImageTypeAdapter() { 
     return renderedImageTypeAdapter; 
    } 

    @Override 
    @SuppressWarnings("resource") 
    public void write(final JsonWriter out, final RenderedImage image) 
      throws IOException { 
     // Intermediate buffer 
     final ByteArrayOutputStream output = new ByteArrayOutputStream(); 
     // By the way, how to pick up the target image format? BMP takes more space, PNG takes more time, JPEG is lossy... 
     ImageIO.write(image, "PNG", output); 
     // Not sure about this, but converting to base64 is more JSON-friendly 
     final Base64.Encoder encoder = Base64.getEncoder(); 
     // toByteArray() returns a copy, not the original array (x2 more memory) 
     // + creating a string requires more memory to create the String internal buffer (x3 more memory) 
     final String imageBase64 = encoder.encodeToString(output.toByteArray()); 
     out.value(imageBase64); 
    } 

    @Override 
    public RenderedImage read(final JsonReader in) 
      throws IOException { 
     // The same in reverse order 
     final String imageBase64 = in.nextString(); 
     final Base64.Decoder decoder = Base64.getDecoder(); 
     final byte[] input = decoder.decode(imageBase64); 
     return ImageIO.read(new ByteArrayInputStream(input)); 
    } 

} 

Notez que Gson n'est actuellement pas très bien conçu pour prendre en charge la transformation des octets, mais il pourrait être à l'avenir somewhat better s'il est corrigé.

Exemple d'utilisation:

private static final Gson gson = new GsonBuilder() 
     .registerTypeHierarchyAdapter(RenderedImage.class, getRenderedImageTypeAdapter()) 
     .create(); 

public static void main(final String... args) 
     throws IOException { 
    try (final InputStream inputStream = getPackageResourceInputStream(Q43301580.class, "sample.png")) { 
     final RenderedImage image = ImageIO.read(inputStream); 
     final ImageHolder before = new ImageHolder(image); 
     final String json = gson.toJson(before); 
     System.out.println(json); 
     final ImageHolder after = gson.fromJson(json, ImageHolder.class); 
     ... 
    } 
} 

Exemple de sortie (avec du vrai petit (32x32) fichier PNG intérieur):

{ "image": "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADgklEQVR42t2XXUiTYRTHpxj4kSKShhgYGSihZGIXXYhU5J2BhBIhCH5cCF6oiWhG0k1BpHghgRgoJHiloBKEqFQ3frDNuemaOqdu0 + n8mFM3Nzf37z1n + JZUEPlOoQdetvd5L87vOed/Ph4ZznnJzsqQz + UFZ + M5HwBrezuUFy9CERoKY3U1jtzuwAFY29pgGxgQ350aDVSXLmFfLud9eVAQTHV1gQNYKi + HMiwM9uFhft/o6MBcTg6fWp + XB93duzhyOOA7POSwyAIR64UnTxhi9 + tXfhQhIdBlZ2P2wQM2Tmv11StY3rwJjAYIQl9QAGVUFPZGRzF7/z7kwcGw9ffzt80PHzAZE4ODu TnpAQ50OjgmJ3HkcmE + N5chdr98wfzDh5DLZPyo4uOx +/mz9Bqg + B8b0d6 + zSecFeJPInSo1XAbjXAKvxR/yUW4Pz7uV/vEBJ9OffUqNNev49BiYeGp4uLg0usDUwdIUNNpaTDV1op7rqUljvNKYyMLb7G4GIdWa2AAbH19LDIy8vNaefmSBRiQUkynMtXUYLGkBO7lZWx2dTEEnVjURFnZL1CSASyWlmL6xg1okpIwdeUK3CYTNjo7WYCGoiLOeU1yMtxmc2AA1NeuscA829uYTk1lEIJYf/eOIcgzP6tdEgAyRicjtatiY8V9EhdDpKTw/7XmZoYgGEkBzEITIQDzs2dsYPX1a/EbuZq8YG5o8GeG8E2dmIgjp/P0AJxGgku1GRnYVyh479jVdFrRE + vrXGqPl3dvTxoPeO12aDMz2aBDqRT315qa/VTR/wTgsdmw1d3NJVSMs + BmOqlYhARXL1dUSA/gWljg9FKGh/u72tgYQ1BqEcjvqtqpAHY + fcLOx4/+ durzcTOxvH3LXY1qOUFQ/CnVyAszN2 + eGK1OBWCur4cyIgIrL174Xb + 1hdl79xiERioqOFRSKf3sQ0MclvXWVmk8sN3b6 + 9UBsMvQwWtb3fuwD4ywpkwlZDAojNWVUk3lhsrK7Hw + PHJ + AudzKnVwrOzwwYP5ud50JhJT5cs9iLAxvv3UFy4wLVdn58P1eXLP4YKIfWor09GR0MZGYm1lhbpLyYUZ/Pz55i5dQu6rCwYnz4FhYXmNjJKKbYmiHG7p + fsb0aGwkIsC2PWuVzNaJ5j1Q8Oni0AVTkKCbmffs/8cuoVlK9/9IjHrP/qdvyn9R0SEM4flWsmCwAAAABJRU5ErkJggg \ u003d \ u003d "}

Je pense qu'il ya trop de défauts, et je vous recommande fortement de re-concevoir votre stockage binaires si possible et de stocker le contenu binaire tel quel.

+0

Voulez-vous dire que vous le conservez comme un binaire du côté client? Je ne fais aucune conversation du côté du serveur? – kiwicomb123

+0

@ kiwicomb123 Cela dépend de ce dont vous avez vraiment besoin dans votre cas. L'utilisation d'une image tamponnée résultera en une conversion d'image (est-ce acceptable?). En outre, le passer par JSON _using_ Gson n'est peut-être pas le meilleur choix, car Gson exige que toute la chaîne soit lue/écrite depuis/vers le flux JSON, pas par morceaux (peut-être que d'autres bibliothèques JSON peuvent le faire mieux?). En gardant simplement le binaire et en le stockant tel quel (vous pouvez appliquer une validation avant le téléchargement, bien sûr), vous pouvez simplement exposer le binaire via une URL directe (bien sûr, certaines autorisations peuvent aussi être appliquées). –

+0

@ kiwicomb123 Vous pouvez également expérimenter avec https://fasterxml.github.io/jackson-core/javadoc/2.6/com/fasterxml/jackson/core/JsonGenerator.html et http://fasterxml.github.io/jackson -core/javadoc/2.6/com/fasterxml/jackson/core/JsonParser.html - ils semblent prendre en charge les données brutes afin que vous puissiez essayer de combiner différents flux d'entrée/sortie afin de produire/valider vos données et les écrire directement sur JSON. –