如何让Gson序列化JDK17中的java.nio.HeapByteBuffer等私有类?

问题描述 投票:0回答:1

我尝试创建一个自定义 ByteBufferTypeAdapter 并将其注册到 java.nio.ByteBuffer 类。

但在运行时,该类实际上是私有的 java.nio.HeapByteBuffer,因此 Gson 根本找不到它的 TypeAdapter。

class ByteBufferTypeAdapter implements JsonDeserializer<ByteBuffer>, JsonSerializer<ByteBuffer> {
    @Override
    public JsonElement serialize(ByteBuffer src, Type typeOfSrc, JsonSerializationContext context) {
        return new JsonPrimitive(Base64.getEncoder().encodeToString(src.array()));
    }

    @Override
    public ByteBuffer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        byte[] bytes = Base64.getDecoder().decode(json.getAsString());
        return ByteBuffer.wrap(bytes);
    }
}
private static Gson GSON = new GsonBuilder()
            .registerTypeAdapter(java.nio.ByteBuffer.class, new ByteBufferTypeAdapter())
            .registerTypeAdapter(Instant.class,
                    (JsonSerializer<Instant>) (src, typeOfSrc, context) -> new JsonPrimitive(src.toString()))
            .create();

测试

        ByteBuffer test = ByteBuffer.wrap("Hello".getBytes(StandardCharsets.UTF_8));

        String asJson = gson.toJson(test);

does not use serialize method and produces: {"hb":[72,101,108,108,111],"offset":0,"isReadOnly":false,"bigEndian":true,"nativeByteOrder":false,"mark":-1,"position":0,"limit":5,"capacity":5,"address":0}


然后,我尝试使用 HeapByteBuffer 类型注册相同的适配器,但该类是私有的,我无权访问它的类。

java gson java-17
1个回答
0
投票

使用

GsonBuilder#registerTypeHierarchyAdatper(Class,Object)
注册类型适配器 及其子类型。这是 Gson 2.10.1 的文档:

配置 Gson 以进行继承类型层次结构的自定义序列化或反序列化。该方法结合了

TypeAdapter

JsonSerializer
JsonDeserializer
的注册。如果先前已为指定类型层次结构注册了类型适配器,则它将被覆盖。如果为类型层次结构中的特定类型注册了类型适配器,则将调用该类型适配器,而不是为类型层次结构注册的适配器。

参数:

baseType

 - 为基类或接口注册的类型适配器的类定义

typeAdapter

 - 该对象必须至少实现 
TypeAdapter
JsonSerializer
JsonDeserializer
 接口之一。

退货:

对此

GsonBuilder

 对象的引用,以实现“Builder”模式

自:

1.7


示例

这是一个工作示例,其中适配器将

ByteBuffer

 与 Base64 字符串相互转换。

源代码

ByteBufferTypeAdapter.java:

package com.example; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Base64; import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; public class ByteBufferTypeAdapter extends TypeAdapter<ByteBuffer> { /* * WARNING: This adapter does not store the following state of the ByteBuffer: * * * position * * limit * * capacity * * read-only vs writable * * direct vs non-direct * * byte order * * the mark value (there's no "get mark" method) * * As a consequence, the deserialized ByteBuffer may not match the * originally serialized ByteBuffer exactly. The deserialized buffer * will always have a position of 0, a limit equal to its capacity, * and a capacity equal to the size of the decoded bytes. It will * also always be a writable, non-direct buffer with a byte order * of BIG_ENDIAN. * * Use 'beginObject()' / 'endObject()' if you want to serialize and * deserialize more or all of the buffer's state. */ @Override public ByteBuffer read(JsonReader reader) throws IOException { if (reader.peek() == JsonToken.NULL) { reader.nextNull(); return null; } byte[] bytes = Base64.getDecoder().decode(reader.nextString()); return ByteBuffer.wrap(bytes); } @Override public void write(JsonWriter writer, ByteBuffer data) throws IOException { if (data == null) { writer.nullValue(); return; } /* * This approach avoids three things: * * 1. Avoids trying to use the backing array directly via 'array()', as that * method will throw an exception for direct buffers. Let the implementation * of 'encode(ByteBuffer)' handle if there's a backing array or not. * * 2. Calling 'data.duplicate()' means the state of 'data' is not modified by * the call to 'encode'. The "duplication" is cheap since the bytes themselves * are shared between the two instances. * * 3. Only the bytes from the buffer's position to its limit are encoded, which * avoids serializing unnecessary data. You could also call 'withoutPadding()' * on the encoder to avoid more possibly unnecessary data. */ ByteBuffer base64Bytes = Base64.getEncoder().encode(data.duplicate()); String base64String = StandardCharsets.ISO_8859_1.decode(base64Bytes).toString(); writer.value(base64String); } }

Data.java

package com.example: import java.nio.ByteBuffer; public record Data(String name, ByteBuffer buffer) {}

Main.java:

package com.example; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import com.google.gson.Gson; import com.google.gson.GsonBuilder; public class Main { public static void main(String[] args) throws Exception { Data original = new Data("Test", ByteBuffer.wrap("Hello, World!".getBytes(StandardCharsets.UTF_8))); System.out.printf("Original data: %s%n", original); Gson gson = new GsonBuilder() .registerTypeHierarchyAdapter(ByteBuffer.class, new ByteBufferTypeAdapter()) .setPrettyPrinting() // make output easier to read for example .create(); String jsonString = gson.toJson(original); System.out.printf("%nJSON string:%n%s%n%n", jsonString); Data decoded = gson.fromJson(jsonString, Data.class); System.out.printf("Decoded data: %s%n", decoded); } }
输出

Original data: Data[name=Test, buffer=java.nio.HeapByteBuffer[pos=0 lim=13 cap=13]] JSON string: { "name": "Test", "buffer": "SGVsbG8sIFdvcmxkIQ\u003d\u003d" } Decoded data: Data[name=Test, buffer=java.nio.HeapByteBuffer[pos=0 lim=13 cap=13]]
    
© www.soinside.com 2019 - 2024. All rights reserved.