我尝试创建一个自定义 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 类型注册相同的适配器,但该类是私有的,我无权访问它的类。
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]]