Mongo java driver cannot find public constructor for interface

user1145925 :

I am using https://mongodb.github.io/mongo-java-driver-reactivestreams/1.11/. It seems to be using https://mongodb.github.io/mongo-java-driver/3.10/. I have a bunch of other registered classes that are working fine. I am using the suggestions at https://mongodb.github.io/mongo-java-driver/3.5/bson/pojos/ (and Save List of interface objects using mongo driver for java) for dealing with fields that have interfaces. However, I get the below error. For other classes for which I get this error, I can simply add an empty constructor to the class, but I cannot do so for an interface. Any help would be appreciated.

Caused by: org.bson.codecs.configuration.CodecConfigurationException: Failed to decode 'SearchCriteria'. Decoding 'filters' errored with: Cannot find a public constructor for 'FilterInterface'.
    at org.bson.codecs.pojo.PojoCodecImpl.decodePropertyModel(PojoCodecImpl.java:222)
    at org.bson.codecs.pojo.PojoCodecImpl.decodeProperties(PojoCodecImpl.java:197)
    at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:121)
    at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:125)
    at org.bson.codecs.pojo.LazyPojoCodec.decode(LazyPojoCodec.java:57)
    at org.bson.codecs.DecoderContext.decodeWithChildContext(DecoderContext.java:93)
    at org.bson.codecs.pojo.PojoCodecImpl.decodePropertyModel(PojoCodecImpl.java:213)
    ... 36 common frames omitted
Caused by: org.bson.codecs.configuration.CodecConfigurationException: Cannot find a public constructor for 'FilterInterface'.
    at org.bson.codecs.pojo.CreatorExecutable.checkHasAnExecutable(CreatorExecutable.java:140)
    at org.bson.codecs.pojo.CreatorExecutable.getInstance(CreatorExecutable.java:107)
    at org.bson.codecs.pojo.InstanceCreatorImpl.<init>(InstanceCreatorImpl.java:40)
    at org.bson.codecs.pojo.InstanceCreatorFactoryImpl.create(InstanceCreatorFactoryImpl.java:28)
    at org.bson.codecs.pojo.ClassModel.getInstanceCreator(ClassModel.java:71)
    at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:120)
    at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:125)
    at org.bson.codecs.pojo.CollectionPropertyCodecProvider$CollectionCodec.decode(CollectionPropertyCodecProvider.java:74)
    at org.bson.codecs.pojo.CollectionPropertyCodecProvider$CollectionCodec.decode(CollectionPropertyCodecProvider.java:43)
    at org.bson.codecs.DecoderContext.decodeWithChildContext(DecoderContext.java:93)
    at org.bson.codecs.pojo.PojoCodecImpl.decodePropertyModel(PojoCodecImpl.java:213)
    ... 42 common frames omitted

Below are snippets of my code:

@BsonDiscriminator
public interface FilterInterface<T> {
    boolean applyOn(T value);

    T getValue();

    ...
}

public abstract class Filter<T> implements FilterInterface<T> {

    public Filter() { }

    public abstract boolean applyOn(T value);

    public abstract T getValue();

    ...
}

public class AddressFilter extends Filter<Address> {

    public AddressFilter() { }

    public boolean applyOn(Address value) {
        return true;
    }

    public Address getValue() {
        return new Address();
    }

    ...
}

public class SearchCriteria {

    public SearchCriteria() { }

    private List<FilterInterface> filters;
}

public static void init() {
    String url = <hidden>;
    MongoClient mongoClient = MongoClients.create(new ConnectionString(url));
    // For POJOs here
    // For interface classes.
    PojoCodecProvider pojoCodecProvider = PojoCodecProvider.builder()
        .conventions(ImmutableList.of(CLASS_AND_PROPERTY_CONVENTION, ANNOTATION_CONVENTION))
        .register(SearchCriteria.class)
        .register(
            ClassModel.builder(FilterInterface.class).enableDiscriminator(true).build(),
            ClassModel.builder(Filter.class).enableDiscriminator(true).build(),
            ClassModel.builder(AddressFilter.class).enableDiscriminator(true).build())
        .automatic(true)
        .build();
    CodecRegistry codecRegistry = CodecRegistries.fromRegistries(
        MongoClientSettings.getDefaultCodecRegistry(),
        CodecRegistries.fromProviders(pojoCodecProvider));
    String dbName = <hidden>;
    mongoDb = mongoClient.getDatabase(dbName).withCodecRegistry(codecRegistry);
}
chuckskull :

The example provided in the link works perfectly fine. Much credit goes to that user for this answer.

You have probably inserted the records when FilterInterface was a class or before using the discriminators.

Solution: Dropping the collection and re-populating will work smoothly.

If it's production scenario, you might wanna add the field _t manually to each document.

Tip: Always use the same code for serialization and deserialization.

Explanation:

Referring to the documentation of the c-sharp driver.

The default discriminator conventions both use an element named _t to store the discriminator value in the BSON document.

If you have inserted the documents before enabling the discriminators, there would be no field _t in the document. When the driver starts decoding, it won't find and fallback to default decoder for the interface FilterInterface.

On the other hand, if you have inserted the documents when FilterInterface was a class, the value of _t will be the fully qualified name of the class. When the decoder starts to decode, it will get the ClassModel and try to create an instance of FilterInterface. Since it is now an interface, the decoder won't find the constructor.

Here is some additional info: you can change the field _t to any other name and you can specify the discriminator value by using over the classes.

@BsonDiscriminator(key = "<field_id>", value = "<value>")

Here is the modified version of the example of that answer. Please run it with discriminators disabled and then run it with discriminators enabled. You will face the same error as yours. Then clean the collection and then try again.

package org.bson.codecs.chng;

import com.google.common.collect.Lists;
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import org.bson.codecs.configuration.CodecRegistries;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.codecs.pojo.ClassModel;
import org.bson.codecs.pojo.PojoCodecProvider;
import org.bson.conversions.Bson;

import java.util.Arrays;
import java.util.List;

public class MongoInterfaceTest {

    private static MongoClient mongoClient;

    static {
        init();
    }

    public static void init() {
        try {
            ClassModel<User> userClassModel = ClassModel.builder(User.class).enableDiscriminator(false).build();
            ClassModel<JavaUser> javaUserClassModel = ClassModel.builder(JavaUser.class).enableDiscriminator(false).build();
            ClassModel<PythonUser> pythonUserClassModel = ClassModel.builder(PythonUser.class).enableDiscriminator(false).build();
            ClassModel<TestUser> testUserClassModel = ClassModel.builder(TestUser.class).enableDiscriminator(false).build();

            CodecRegistry pojoCodecRegistry = CodecRegistries.fromRegistries(
                    MongoClientSettings.getDefaultCodecRegistry(),
                    CodecRegistries.fromProviders(
                            PojoCodecProvider.builder()
                                    .register(
                                            userClassModel,
                                            javaUserClassModel,
                                            pythonUserClassModel,
                                            testUserClassModel
                                    )
                                    .build()
                    )
            );

            mongoClient = MongoClients.create(
                    MongoClientSettings.builder()
                            .codecRegistry(pojoCodecRegistry)
                            .applyConnectionString(new ConnectionString(ApplictaionConfig.MONGODB_URL))
                            .applyToConnectionPoolSettings(builder -> {
                                builder.minSize(10);
                            })
                            .build()
            );
        } catch (Exception e) {
            System.out.println("Connection mongodb failed");
            throw new RuntimeException();
        }
    }

    public static void main(String[] args) {
        MongoCollection<TestUser> collection = getMongoCollection("TestUser", TestUser.class);

        JavaUser javaUser = new JavaUser<Integer>("a");
        PythonUser pythonUser = new PythonUser<String>("b", "1");

        TestUser testUser = new TestUser(javaUser.name, javaUser);
        insertOne(collection, testUser);

        testUser = new TestUser(pythonUser.name, pythonUser);
        insertOne(collection, testUser);


        Bson bson = Filters.and(Filters.eq("name", "a"));
        TestUser testUser1 = findFirst(collection, bson);
        System.out.println(testUser1);
        testUser1.users.forEach(x -> System.out.println(x.dev()));

        bson = Filters.and(Filters.eq("name", "b"));
        testUser1 = findFirst(collection, bson);
        System.out.println(testUser1);
        testUser1.users.forEach(x -> System.out.println(x.dev()));

    }

    /**
     * 获得collection对象
     */
    public static <T> MongoCollection<T> getMongoCollection(String collectionName, Class<T> tClass) {
        MongoDatabase mongoDatabase = mongoClient.getDatabase("kikuu");
        MongoCollection<T> collection = mongoDatabase.getCollection(collectionName, tClass);
        return collection;
    }

    public static <T> void insertOne(MongoCollection<T> collection, T document) {
        insertMany(collection, Lists.newArrayList(document));
    }

    public static <T> void insertMany(MongoCollection<T> collection, List<T> documents) {
        collection.insertMany(documents);
    }

    public static <T> T findFirst(MongoCollection<T> collection) {
        return (T) collection.find().first();
    }

    public static <T> T findFirst(MongoCollection<T> collection, Bson bson) {
        return (T) collection.find(bson).first();
    }

    public static interface User<T> {
        String dev();

        T foo();
    }

    public static class JavaUser<T> implements User<T> {
        public String name;


        public JavaUser() {
        }

        public JavaUser(String name) {
            this.name = name;
        }

        @Override
        public String dev() {
            return "java";
        }

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

        @Override
        public T foo() {
            return null;
        }
    }

    public static class PythonUser<T> implements User<T> {
        public String name;
        public String age;

        public PythonUser() {
        }

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

        @Override
        public String dev() {
            return "python";
        }

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

        @Override
        public T foo() {
            return null;
        }
    }

    public static class TestUser {
        public String name;
        public List<User> users;

        public TestUser() {
        }

        public TestUser(String name, User... users) {
            this.name = name;
            this.users = Arrays.asList(users);
        }

        @Override
        public String toString() {
            return "TestUser{" +
                    "name='" + name + '\'' +
                    ", user=" + users +
                    '}';
        }
    }
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=141225&siteId=1