将BsonDocument转为指定Java类对象时解码异常的排查求助
问题排查:Mongo BsonDocument转POJO解码异常
异常信息
An exception occurred when decoding using the AutomaticPojoCodec. Decoding into a 'CustomObject' failed with the following exception: Failed to decode 'CustomObject'. Decoding 'name' errored with: readStartDocument can only be called when CurrentBSONType is DOCUMENT, not when CurrentBSONType is STRING. A custom Codec or PojoCodec may need to be explicitly configured and registered to handle this type.
对应的Mongo文档结构
{ "_id" : "6776734a086ac4106c0ca431", "custom_enum_field" : "custom_enum_1", "private_float_field" : 1.001, "private_int_field" : NumberLong(7), "int_field" : NumberLong(1), "string_field" : "text 4", "enum_field" : "ENUM2", "custom_object" : { "name" : "blah", "value" : "text", "id" : NumberInt(182761), "price" : 199.99, "discount" : 10.2, "quantity" : NumberLong(12882763230), "list" : null, "customSubObjectList" : [ { "name" : "blah", "value" : "text", "id" : NumberInt(182761), "price" : 199.99, "discount" : 10.2, "quantity" : NumberInt(1288276323) } ], "dob" : NumberLong(1735816009154) }, "bit_field" : true }
目标Java类定义
@Data @Builder @AllArgsConstructor @NoArgsConstructor public class CustomObject { private String name; private String value; private Integer id; private Float price; private Double discount; private Long quantity; private List<String> list; private List<CustomSubObject> customSubObjectList; private Date dob; } @Data @Builder @AllArgsConstructor @NoArgsConstructor class CustomSubObject { private String name; private String value; private Integer id; private Float price; private Double discount; private Long quantity; }
当前转换代码
private <T> T convertByteInDataForObject(ByteString data, Class<T> toType) { byte[] bsonBytes = data.toByteArray(); ByteBuffer byteBuffer = ByteBuffer.wrap(bsonBytes); ByteBufferBsonInput bsonInput = new ByteBufferBsonInput(new ByteBufNIO(byteBuffer)); BsonBinaryReader reader = new BsonBinaryReader(bsonInput); BsonDocumentCodec codec = new BsonDocumentCodec(); BsonDocument bsonDocument = codec.decode(reader, DecoderContext.builder().build()); CodecRegistry pojoCodecRegistry = CodecRegistries.fromRegistries(CodecRegistries.fromProviders(PojoCodecProvider.builder().automatic(true).build())); return pojoCodecRegistry.get(toType).decode(new BsonDocumentReader(bsonDocument), DecoderContext.builder().build()); }
问题根源
错误核心是:你把整个Mongo根文档直接解码为CustomObject,但根文档的_id是字符串类型,解码器试图将其映射到CustomObject的name字段时,发现类型不匹配——它期望读取一个文档结构,实际读到的是字符串,因此抛出异常。真正对应CustomObject的是根文档里的custom_object子字段。
修复方案
1. 提取正确的子文档
修改转换代码,从根BsonDocument中取出custom_object对应的子文档,再进行解码。同时加入Mongo默认CodecRegistry,增强类型支持。
2. 调整Java类的潜在问题
- 将
CustomSubObject改为public访问权限,避免反射实例化失败 - 建议把
CustomObject和CustomSubObject的price字段从Float改为Double,和Mongo中的数值类型完全匹配,避免精度损失
修复后的转换代码
private <T> T convertByteInDataForObject(ByteString data, Class<T> toType) { byte[] bsonBytes = data.toByteArray(); ByteBuffer byteBuffer = ByteBuffer.wrap(bsonBytes); ByteBufferBsonInput bsonInput = new ByteBufferBsonInput(new ByteBufNIO(byteBuffer)); BsonBinaryReader reader = new BsonBinaryReader(bsonInput); BsonDocumentCodec codec = new BsonDocumentCodec(); BsonDocument rootBsonDoc = codec.decode(reader, DecoderContext.builder().build()); // 提取对应CustomObject的子文档 BsonDocument targetBsonDoc = rootBsonDoc.getDocument("custom_object"); // 加入默认CodecRegistry,增强原生类型支持 CodecRegistry pojoCodecRegistry = CodecRegistries.fromRegistries( MongoClientSettings.getDefaultCodecRegistry(), CodecRegistries.fromProviders(PojoCodecProvider.builder().automatic(true).build()) ); return pojoCodecRegistry.get(toType).decode(new BsonDocumentReader(targetBsonDoc), DecoderContext.builder().build()); }
调整后的Java类(关键修改)
@Data @Builder @AllArgsConstructor @NoArgsConstructor public class CustomObject { private String name; private String value; private Integer id; // 改为Double匹配Mongo类型 private Double price; private Double discount; private Long quantity; private List<String> list; private List<CustomSubObject> customSubObjectList; private Date dob; } // 修改为public访问权限 @Data @Builder @AllArgsConstructor @NoArgsConstructor public class CustomSubObject { private String name; private String value; private Integer id; // 改为Double匹配Mongo类型 private Double price; private Double discount; private Long quantity; }
内容的提问来源于stack exchange,提问作者faisal pathan
相关产品推荐
相关产品推荐

