java - GSON 无法序列化 BufferedImages

标签 java json gson awt bufferedimage

使用 GSON 在 JSON 中序列化 BufferedImages 似乎有问题。我正在使用 Derby 来存储图像。当我查询数据库时,我构建了一个具有一些文本字段和一个 BufferedImage 字段的 JavaBean。然后我使用 GSON 将 JavaBean 转换为 JSON,这就是发生异常的地方。

异常信息如下:
java.lang.IllegalArgumentException:类 sun.awt.image.ByteInterleavedRaster 声明了多个名为 maxX 的 JSON 字段

我确实在这里发现了类似的问题GSON java.lang.IllegalArgumentException: class 'xx' declares multiple JSON fields named 'XX' AND StackOverflowError在这里 class A declares multiple JSON fields

但问题出在 Java 中包含的 awt 库。如果我可以访问 AWT 源代码,我可以按照其他 stackoverflow 答案中提供的答案进行操作,但我该怎么做呢?

最佳答案

您必须知道,并非每个类都设计为可(反)序列化,尤其是(反)序列化是基于目标类二进制结构的。您的方法至少有以下缺点:

  • sun.awt.image.ByteInterleavedRaster 类字段在另一个 JVM/JRE 上不一定相同,因此您可能会被供应商锁定;
  • 在 JSON 中保存二进制数据可能不是最佳选择(在(反)序列化、存储消耗、性能期间可能会消耗巨大且可怕的内存)——也许通用 blob 存储更适合二进制数据?
  • 使用 Java AWT 读取图像并将其写回并不能保证相同的二进制输出:例如,我的测试图像 1.2K 被反序列化为另一个大小的图像 0.9K;
  • 您必须选择目标持久图像格式或检测最有效的格式(如何?)。

考虑以下简单类:

final class ImageHolder {

    final RenderedImage image;

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

}

现在您必须创建一个类型适配器来告诉 Gson 如何存储和恢复特定类型实例:

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

}

请注意,目前 Gson 的设计并不是很好地支持字节转换,但它可能是 somewhat better将来如果修复。

使用示例:

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

示例输出(里面有真正的小 (32x32) PNG 文件):

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

我认为存在太多缺陷,我强烈建议您尽可能重新设计二进制文件存储并按原样存储二进制内容。

关于java - GSON 无法序列化 BufferedImages,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43301580/

相关文章:

java - 内部类是否需要在外部类中进行修补?

java - 将 Spring 存储库注入(inject) Spring @Configuration 类

json - JSON 文件中的 Ansible 变量替换问题

.net - ASP .NET - JsonIgnore 似乎不起作用

javascript - 如何获取MongoDB中嵌入文档中同一级别的所有字段

java - 如何将复杂的 Json 字符串转换为对象?

java - 检查 JDBC 请求是否正确执行

java - 穿过迷宫时出现堆栈溢出错误

java - 如何使用 auto-value-gson 映射未知的 json 字段

java - jackson VS。 Gson