使用GSON和TypeAdapter将BSON(mongoDB)读入POJO

我正在寻找一种使用GSON将MongoDB文档读入POJO的方法。直到您遇到日期和长整数之类的东西时,它才能正常工作。

我想为Gson写一个自定义适配器,它将转换任何长时间编码的BSON,我创建了自己的适配器:

public class BsonLongTypeAdapter extends TypeAdapter<Long>

{

@Override

public void write(JsonWriter out, Long value) throws IOException

{

out.beginObject()

.name("$numberLong")

.value(value.toString())

.endObject();

}

@Override

public Long read(JsonReader in) throws IOException

{

in.beginObject();

assert "$numberLong".equals(in.nextName());

Long value = in.nextLong();

in.endObject();

return value;

}

}

我定义了以下测试来检查是否可行:

@Test

public void canWriteCorrectJSON() {

Gson gson = new GsonBuilder().registerTypeAdapter(Long.class, new BsonLongTypeAdapter()).create();

MyTestObject obj = new MyTestObject(1458569479431L);

String gsonString = gson.toJson(obj);

assertEquals("{\"timestamp\":{\"$numberLong\":\"1458569479431\"}}",gsonString);

}

@Test

public void canReadFromJSON() {

Gson gson = new GsonBuilder().registerTypeAdapter(Long.class, new BsonLongTypeAdapter()).create();

MyTestObject actualTaskObject = gson.fromJson("{\"timestamp\":{\"$numberLong\":\"1458569479431\"}}", MyTestObject.class);

MyTestObject taskObject = new MyTestObject(1458569479431L);

assertEquals(taskObject.getTimestamp(),actualTaskObject.getTimestamp());

}

private static class MyTestObject

{

long timestamp;

public MyTestObject(long ts)

{

timestamp = ts;

}

public long getTimestamp()

{

return timestamp;

}

public void setTimestamp(long timestamp)

{

this.timestamp = timestamp;

}

}

第一个(写)测试工作正常,但是读测试在以下方面失败:

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a long but was BEGIN_OBJECT at line 1 column 15 path $.timestamp

因为从未调用过我的适配器的读取功能。我想这可能是因为我想映射到MyTestObject而不是Long,但是我不想为所有包含long的类编写适配器。

是否可以为GSON编写一个适配器,以将我发送的所有BSON长转换为该适配器?

回答:

我使用CustomizedTypeAdapterFactory解决了它。

基本上首先写一个定制的适配器:

public abstract class CustomizedTypeAdapterFactory<C>

implements TypeAdapterFactory

{

private final Class<C> customizedClass;

public CustomizedTypeAdapterFactory(Class<C> customizedClass) {

this.customizedClass = customizedClass;

}

@SuppressWarnings("unchecked") // we use a runtime check to guarantee that 'C' and 'T' are equal

public final <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {

return type.getRawType() == customizedClass

? (TypeAdapter<T>) customizeMyClassAdapter(gson, (TypeToken<C>) type)

: null;

}

private TypeAdapter<C> customizeMyClassAdapter(Gson gson, TypeToken<C> type) {

final TypeAdapter<C> delegate = gson.getDelegateAdapter(this, type);

final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);

return new TypeAdapter<C>() {

@Override public void write(JsonWriter out, C value) throws IOException

{

JsonElement tree = delegate.toJsonTree(value);

beforeWrite(value, tree);

elementAdapter.write(out, tree);

}

@Override public C read(JsonReader in) throws IOException {

JsonElement tree = elementAdapter.read(in);

afterRead(tree);

return delegate.fromJsonTree(tree);

}

};

}

/**

* Override this to muck with {@code toSerialize} before it is written to

* the outgoing JSON stream.

*/

protected void beforeWrite(C source, JsonElement toSerialize) {

}

/**

* Override this to muck with {@code deserialized} before it parsed into

* the application type.

*/

protected void afterRead(JsonElement deserialized) {

}

}

然后为所有需要考虑的类创建一个子类。您必须为每个包含long的类创建一个(在这种情况下)。但是除了long值(以及任何其他bson特定值)之外,您无需序列化任何内容

public class MyTestObjectTypeAdapterFactory extends CustomizedTypeAdapterFactory<MyTestObject>

{

public MyTestObjectTypeAdapterFactory()

{

super(MyTestObject.class);

}

@Override

protected void beforeWrite(MyTestObject source, JsonElement toSerialize)

{

//you could convert back the other way here, I let mongo's document parser take care of that.

}

@Override

protected void afterRead(JsonElement deserialized)

{

JsonObject timestamp = deserialized.getAsJsonObject().get("timestamp").getAsJsonObject();

deserialized.getAsJsonObject().remove("timestamp");

deserialized.getAsJsonObject().add("timestamp",timestamp.get("$numberLong"));

}

}

然后生成具有以下内容的Gson:

Gson gson = new GsonBuilder().registerTypeAdapterFactory(new MyTestObjectTypeAdapterFactory()).create();

以上是 使用GSON和TypeAdapter将BSON(mongoDB)读入POJO 的全部内容, 来源链接: utcz.com/qa/397422.html

回到顶部