简体   繁体   中英

How to NOT IGNORE a null property when using MongoDB's default POJO Codec to serialize to BSON?

I have a class contains a nullable field:

public class User {
  @BsonId
  public ObjectId _id;
  // Nullable
  public String wechat_unionid;
  // Nullable
  public String wework_userid;
}

I use the default POJO Codec of the mongodb java driver, and it will ignore the null property, but I need that to be null and exists or it will not pass the schema validation.

In document: https://mongodb.github.io/mongo-java-driver/4.1/bson/pojos/

It says:

By default null values aren't serialized. This is controlled by the default implementation of the FieldSerialization interface. Custom implementations can be set on the PropertyModelBuilder which is available from the ClassModelBuilder.

But I got no clue how to customize that?

What should I do? Is there another way to work around?

Thanks!

i find one solution from the official java driver doc which works successfully. This configuration make mongo driver create the field "age" even if the value of "age" is null. Help that will help for you.

package com.alan.db.mongo;

import com.mongodb.MongoClientSettings;
import com.mongodb.client.*;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.codecs.pojo.*;

import java.util.List;
import java.util.function.Consumer;

import static org.bson.codecs.configuration.CodecRegistries.fromProviders;
import static org.bson.codecs.configuration.CodecRegistries.fromRegistries;

public class InsertTest {
    public static final String CONNECTION_STRING = "mongodb://alan:alan@127.0.0.1:27017";
    static MongoClient mongoClient;
    static MongoDatabase database;
    static MongoCollection<Student> collection;

    static {
        ClassModelBuilder classModelBuilder = ClassModel.builder(Student.class);
        classModelBuilder.getProperty("age").propertySerialization(new MyPropertySerialization<>());
        PojoCodecProvider.builder().register(classModelBuilder.build());
        mongoClient = MongoClients.create(CONNECTION_STRING);
        CodecRegistry pojoCodecRegistry = fromRegistries(MongoClientSettings.getDefaultCodecRegistry(),
                fromProviders(PojoCodecProvider.builder().register(classModelBuilder.build()).automatic(true).build()));
        database = mongoClient.getDatabase("test").withCodecRegistry(pojoCodecRegistry);
        collection = database.getCollection("student", Student.class);
    }

    public static void main(String[] args) {
        InsertTest client = new InsertTest();
        client.collection.insertOne(new Student("tony", null));
        FindIterable<Student> students = client.collection.find();
        Consumer<Student> consumer = student -> System.out.println(student);
        students.forEach(consumer);
    }
}
public class Student {
    private String name;
    private Integer age;

    public Student() { }

    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

You can control if a property of a POJO class will be serialized to Document using PropertyModelBuilder class, avaiable in ClassModelBuilder class. And the simplest way to get a ClassModelBuilder for all POJO's classes is declaring a custom Convention in your POJO CodecRegistry .

First, create NullableConvention class:

public class NullableConvention implements Convention {
    @Override
    public void apply(ClassModelBuilder<?> cmb) {
        for(PropertyModelBuilder<?> pmb : cmb.getPropertyModelBuilders()){ //edit all properties
            pmb.propertySerialization(new PropertySerialization(){
                @Override
                public boolean shouldSerialize(Object t) {
                    return true; //always serialize object even if t == null
                }
            });
        }
    }
}

Then, append your new Convention in your PojoCodecProvider :

List<Convention> conventions = new ArrayList<>(); // your customs conventions go here
conventions.add(new NullableConvention()); // custom convention
conventions.addAll(Conventions.DEFAULT_CONVENTIONS); // mongo's driver default conventions
CodecRegistry pojoCodecRegistry = CodecRegistries.fromProviders(PojoCodecProvider.builder().conventions(conventions).build()); //add your conventions to pojo provider

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM