当前位置: 首页 > 面试题库 >

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

秦鸿羽
2023-03-14
问题内容

我正在寻找一种使用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();


 类似资料:
  • 本文向大家介绍mongodb BSON的基本使用教程,包括了mongodb BSON的基本使用教程的使用技巧和注意事项,需要的朋友参考一下 查找 Find 这里查找时间戳内,账号为account,标签为tag的数据并统计个数。 聚合管道在mgo中为Pipe(pipeline interface{}) 这个和bash中使用的管道很像,数据可以被层层处理。一般传入的参数为[]bson.M。这个[]bs

  • 问题内容: gson是一个很棒的图书馆-运作良好。有时我有自定义要求,并且可以制作和注册TypeAdapters和TypeAdaptorFactories-效果也很好。 但是令我困惑的是如何委托回json序列化…大多数时候,我需要使用它进行收集,但为了说明这一点- 假设我有一个配对类,gson显然会很高兴地序列化,但是出于某种原因我需要自己的自定义序列化程序。好吧…如果我是 如果我为此编写了一个类

  • 然而,让我困惑的是,如何将委托返回到json序列化中...大多数时候,我需要这个集合,但为了说明这一点-假设我有一个pair类,gson显然会很高兴地序列化它,但出于某种原因,我需要自己的自定义序列化器。嗯...如果我的双是 如果我为此编写了一个类型适配器-您将希望write函数看起来如下所示: 所以你可以看到问题--我不知道第一个和第二个的类型,也不知道它们是如何序列化的。我可以使用gson.t

  • 我想用GSON库用java读这个JSON文件。我刚开始使用gson Libray。有人请纠正我的代码我的JSON文件如下所示: 这是我为读取这个文件而编写的java代码: 但我得到以下异常:

  • 我已经编写了整型适配器,并用作注释来实现以下输出,但在gson库中编写的代码完全不允许使用注释配置进行空处理。 如果我取消注释1,2,4行和注释3行,那么我仍然没有实现我的愿望输出。 当我调试第189行的库代码(给定的屏幕截图)时,没有到达我的自定义适配器代码,因此空处理无法使用注释。有没有其他方法可以根据注释配置实现所需的输出? 期望输出: 猫科动物 - "" (用作注释并具有空值) phone

  • 问题内容: 我正在尝试使用pymongo模块从URL中提取一个JSON文件并将其按原样发送到mongoDB。 我有以下代码 执行此操作后,出现此错误,引发TypeError(“ documents must be a non-empty list”)TypeError:文档必须为非空列表 理想情况下,我希望能够从url中提取json并更新mongoDB,因为此json文件每周都会更新。谢谢 问题答