使用Mongo Java Driver 3.7插入含JsonNode字段的POJO至MongoDB遇异常
我来帮你梳理几个关键的解决思路,针对你遇到的JsonNode字段插入异常、自定义Codec未被调用的问题:
1. 修正自定义Codec的类型与注册方式
你提到使用了CollectibleCodec,但这个接口是专门为可作为Mongo集合文档的POJO设计的(比如带有_id字段的实体类),而JsonNode是Jackson的通用JSON节点类型,并不属于这类实体。你应该实现通用的Codec<JsonNode>接口,而不是CollectibleCodec。
示例代码框架:
public class JsonNodeCodec implements Codec<JsonNode> { private final ObjectMapper objectMapper = new ObjectMapper(); @Override public JsonNode decode(BsonReader reader, DecoderContext decoderContext) { try { String json = reader.readString(); return objectMapper.readTree(json); } catch (IOException e) { throw new RuntimeException("Failed to decode JsonNode", e); } } @Override public void encode(BsonWriter writer, JsonNode value, EncoderContext encoderContext) { try { String json = objectMapper.writeValueAsString(value); // 若要保留BSON原生结构,也可直接写入BsonDocument // BsonDocument bson = BsonDocument.parse(json); // bson.encode(writer); writer.writeString(json); } catch (IOException e) { throw new RuntimeException("Failed to encode JsonNode", e); } } @Override public Class<JsonNode> getEncoderClass() { return JsonNode.class; } }
注册时,要确保这个Codec被加入到CodecRegistry的正确位置,覆盖默认的JsonNode处理逻辑:
CodecRegistry customCodecRegistry = CodecRegistries.fromCodecs(new JsonNodeCodec()); CodecRegistry pojoCodecRegistry = CodecRegistries.fromProviders( PojoCodecProvider.builder().automatic(true).build() ); CodecRegistry combinedRegistry = CodecRegistries.fromRegistries( MongoClientSettings.getDefaultCodecRegistry(), customCodecRegistry, pojoCodecRegistry ); MongoClientSettings settings = MongoClientSettings.builder() .codecRegistry(combinedRegistry) .build(); MongoClient client = MongoClients.create(settings);
2. 尝试使用官方Jackson集成依赖
Mongo Java Driver 3.7提供了Jackson集成的扩展包,可能比自己写Codec更可靠。你可以引入mongodb-driver-jackson3依赖(注意版本要和你的Driver 3.7匹配),然后直接使用JacksonCodecProvider来处理JsonNode:
CodecRegistry jacksonRegistry = CodecRegistries.fromProviders(new JacksonCodecProvider()); CodecRegistry combinedRegistry = CodecRegistries.fromRegistries( MongoClientSettings.getDefaultCodecRegistry(), jacksonRegistry, PojoCodecProvider.builder().automatic(true).build() );
这样Driver会自动用Jackson来序列化/反序列化JsonNode,无需自定义Codec。
3. 检查POJO Codec的配置优先级
如果你同时使用了POJO Codec,要确保JsonNode的处理逻辑优先级高于默认的POJO处理。比如,把自定义Codec或Jackson Provider放在POJO Provider之前注册,这样当处理Sample类中的JsonNode字段时,会先匹配到JsonNode对应的Codec,而不是让POJO Codec尝试处理它(这可能就是导致空数组的原因)。
4. 调试Codec的加载与调用
在自定义Codec的encode、decode方法中添加日志或断点,确认它是否被Driver加载。你可以通过打印combinedRegistry.get(JsonNode.class)来检查获取到的Codec是否是你自定义的实例,如果不是,说明注册顺序或方式有问题。
另外,也可以尝试直接在插入时手动转换JsonNode为Bson结构作为临时方案:
Sample sample = new Sample(); sample.setField1(123); sample.setField2(yourJsonNode); // 手动转换JsonNode为BsonDocument ObjectMapper mapper = new ObjectMapper(); String jsonStr = mapper.writeValueAsString(sample.getField2()); BsonDocument field2Bson = BsonDocument.parse(jsonStr); // 构造Bson文档插入 BsonDocument doc = new BsonDocument(); doc.append("field1", new BsonInt32(sample.getField1())); doc.append("field2", field2Bson); collection.insertOne(doc);
内容的提问来源于stack exchange,提问作者Naveen Santhanavel

