You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

REST服务器与Android应用间ObjectId转换不匹配问题

问题:Android中MongoDB ObjectId反序列化与数据库值不匹配

问题说明

从Spring Boot REST接口获取MongoDB文档数据时,Android端解析出的_id(ObjectId)与数据库中存储的十六进制值不一致,但其他字段(如content、createdAt等)解析正常。Postman中查看接口返回数据是正确的,排除请求方式问题,核心是ObjectId的反序列化逻辑出错。

MongoDB文档示例

{
  "_id": {
    "$oid": "64a6d4ae3fe947597ff1fe23"
  },
  "content": "my first post ",
  "createdAt": {
    "$date": "2023-07-06T14:50:22.778Z"
  },
  "hashtags": [],
  "user": {
    "$ref": "app_users",
    "$id": {
      "$oid": "648da920b40b7222a0af8cb6"
    }
  },
  "_class": "dev.social.media.security.model.Post"
}

Android端相关代码

请求数据代码

// Make the API call to fetch the list of posts
PostService postService = retrofit.create(PostService.class);
Call<List<Post>> call = postService.getAllPosts(authToken);
call.enqueue(new Callback<List<Post>>() {
    @Override
    public void onResponse(@NonNull Call<List<Post>> call, @NonNull Response<List<Post>> response) {
        System.out.println("we should get the postts here " + response.body());
        System.out.println("header check " + response.headers());
        System.out.println("Status code from server" + response.isSuccessful());
        if (response.isSuccessful() && response.body() != null) {
            Gson gson = new Gson();
            List<Post> posts = response.body();
            String jsonResponse = gson.toJson(posts);
            System.out.println("Server response original: " + new Gson().toJson(posts));

            System.out.println("Response body: " + jsonResponse);
            if (posts != null) {
                System.out.println("get id from the original post "+ posts.get(0).getIdWrapper());
                System.out.println("get content from original post " + posts.get(0).getContent());
                postAdapter.setPostList(posts);
            } else {
                System.out.println("Received null posts list");
            }
        } else {
            System.out.println("Something went wrong displaying the post");
            Toast.makeText(MainActivity.this, "Login Error", Toast.LENGTH_SHORT).show();
        }
    }

    // 省略onFailure方法
});

Post模型类代码

public class Post  {
    @SerializedName("_id")
    private ObjectId idWrapper;

    @SerializedName("content")
    private String content;

    @SerializedName("createdAt")
    private String createdAt;

    @SerializedName("updatedAt")
    private String updatedAt;

    @SerializedName("hashtags")
    private List<String> hashtags;
    @SerializedName("comments")
    private List<Comment> comments;

    @SerializedName("likes")
    private List<Like> likes;

    @SerializedName("user")
    private AppUser user;

    // empty constructor
    public Post() {}

    public Post(ObjectId idWrapper, String content, String createdAt, String updatedAt, List<String> hashtags, List<Comment> comments, List<Like> likes, AppUser user) {
       this.idWrapper = idWrapper;
        this.content = content;
        this.createdAt = createdAt;
        this.updatedAt = updatedAt;
        this.hashtags = hashtags;
        this.comments = comments;
        this.likes = likes;
        this.user = user;
    }

    public ObjectId getIdWrapper() {
        return idWrapper;
    }

    public void setIdWrapper(ObjectId idWrapper) {
        this.idWrapper = idWrapper;
    }

    // 省略其他getter/setter
}

解决方案

问题根源:Gson默认无法识别MongoDB ObjectId的嵌套结构({"$oid": "xxx"}),直接用ObjectId类型接收会导致反序列化失败,生成错误的ID值。

方案1:自定义Gson TypeAdapter处理ObjectId

创建一个TypeAdapter来专门解析$oid结构:

public class ObjectIdTypeAdapter extends TypeAdapter<ObjectId> {
    @Override
    public void write(JsonWriter out, ObjectId value) throws IOException {
        if (value == null) {
            out.nullValue();
            return;
        }
        out.beginObject();
        out.name("$oid").value(value.toHexString());
        out.endObject();
    }

    @Override
    public ObjectId read(JsonReader in) throws IOException {
        if (in.peek() == JsonToken.NULL) {
            in.nextNull();
            return null;
        }
        in.beginObject();
        String oid = null;
        while (in.hasNext()) {
            if ("$oid".equals(in.nextName())) {
                oid = in.nextString();
            } else {
                in.skipValue();
            }
        }
        in.endObject();
        return oid == null ? null : new ObjectId(oid);
    }
}

然后在创建Retrofit时,使用注册了该Adapter的Gson实例:

Gson customGson = new GsonBuilder()
        .registerTypeAdapter(ObjectId.class, new ObjectIdTypeAdapter())
        .create();

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create(customGson))
        .build();

方案2:修改Post模型类直接接收ID字符串

将idWrapper改为String类型,并通过SerializedName直接映射_id.$oid字段:

public class Post {
    @SerializedName("_id.$oid")
    private String id;

    // 其他字段不变...

    public ObjectId getObjectId() {
        return new ObjectId(id);
    }

    // 省略其他方法
}

这种方式更简单,不需要自定义Adapter,后续如果需要ObjectId对象,再通过字符串转换即可。

方案3:调整Spring Boot服务器端序列化配置

在Spring Boot中配置MongoDB的序列化规则,直接将ObjectId序列化为字符串而非嵌套对象。例如使用Jackson的话,可以添加配置:

@Configuration
public class MongoConfig {
    @Bean
    public MappingMongoConverter mappingMongoConverter(MongoDbFactory factory, MongoMappingContext context, BeanFactory beanFactory) {
        MappingMongoConverter converter = new MappingMongoConverter(new DefaultDbRefResolver(factory), context);
        converter.setTypeMapper(new DefaultMongoTypeMapper(null));
        // 配置ObjectId序列化
        converter.getConversionService().addConverter(ObjectId.class, String.class, ObjectId::toHexString);
        return converter;
    }
}

这样服务器返回的_id就是直接的十六进制字符串,Android端模型类中直接用String或ObjectId接收即可。

内容的提问来源于stack exchange,提问作者ABAB

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 20:34:56