MongoDB: Register codecs (Java)

You can do Mongo codecs for arrays using ArrayList as follows:

com.example.model.Order

This is the class representing an Order. It includes an ArrayList of items.

package com.example.model;

import java.util.ArrayList;
import org.bson.types.ObjectId;

/**
 * Class representing an Order.
 */
public class Order
{
    private ObjectId id;
    private ArrayList<Item> items;

    /**
     * Default constructor. Needed for testing.
     */
    public Order() {
        this.items = new ArrayList<>();
    }

    /**
     * Sets the id of the Order.
     * @param id The new id of the Order.
     */
    public void setId(ObjectId id) {
        this.id = id;
    }

    /**
     * Gets the id of the Order.
     * @return The id of the Order.
     */
    public ObjectId getId() {
        return this.id;
    }

    /**
     * Sets the items for the Order.
     * @param items The items for the Order.
     */
    public void setItems(ArrayList<Item> items) {
        this.items = items;
    }

    /**
     * Gets the items for the Order.
     * @return The items for the Order.
     */
    public ArrayList<Item> getItems() {
        return items;
    }

    /**
     * Adds an item to the Order.
     * @param item The new Item to add to the Order.
     */
    public void addItem(Item item) {
        this.items.add(item);
    }
}

com.example.model.Item

This is the class representing an Order Item. There can be any number of items that are a part of an order. Items are embedded within the Order document in Mongo.

package com.example.model;

import org.bson.types.ObjectId;

/**
 * Class representing an order item.
 */
public class Item
{
    private ObjectId id;
    private String name;

    /**
     * Constructor.
     */
    public Item() {
        //
    }

    /**
     * Sets the id of the Item.
     * @param id The new id of the Item.
     */
    public void setId(ObjectId id) {
        this.id = id;
    }

    /**
     * Gets the id of the Item.
     * @return The id of the Item.
     */
    public ObjectId getId() {
        return this.id;
    }

    /**
     * Sets the name of the Item.
     * @param name The new name of the Item.
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * Gets the name of the Item.
     * @return The name of the Item.
     */
    public String getName() {
        return this.name;
    }
}

com.example.mongo.ItemConverter

Simple converter class that converts Items to/from Documents.

package com.example.mongo;

import com.example.model.Item;
import org.bson.Document;

/**
 * Converts Mongo Documents to/from Items.
 */
public class ItemConverter {
    /**
     * Convert the passed Item into a Mongo Document.
     * @param item The Item that you want to convert into a Mongo Document.
     * @return Returns the Document that was created from the passed Item.
     */
    public Document convert(Item item) {
        Document document = new Document();
        document.put("_id", item.getId());
        document.put("name", item.getName());

        return document;
    }

    /**
     * Convert the passed Mongo Document into an Item.
     * @param document The Document that you want to convert into an Item.
     * @return Returns the Item that was created from the passed Mongo Document.
     */
    public Item convert(Document document) {
        Item item = new Item();
        item.setId(document.getObjectId("_id"));
        item.setName(document.getString("name"));

        return item;
    }
}

com.example.mongo.ItemCodec

Codec for encoding and decoding Items.

package com.example.mongo;

import com.example.model.Item;
import com.mongodb.MongoClient;
import org.bson.BsonReader;
import org.bson.BsonString;
import org.bson.BsonValue;
import org.bson.BsonWriter;
import org.bson.Document;
import org.bson.codecs.Codec;
import org.bson.codecs.CollectibleCodec;
import org.bson.codecs.DecoderContext;
import org.bson.codecs.EncoderContext;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.types.ObjectId;

/**
 * Mongo Decoder for Items.
 */
public class ItemCodec implements CollectibleCodec<Item> {

    private final CodecRegistry registry;
    private final Codec<Document> documentCodec;
    private final ItemConverter converter;

    /**
     * Default constructor.
     */
    public ItemCodec() {
        this.registry = MongoClient.getDefaultCodecRegistry();
        this.documentCodec = this.registry.get(Document.class);
        this.converter = new ItemConverter();
    }

    /**
     * Codec constructor.
     * @param codec The existing codec to use.
     */
    public ItemCodec(Codec<Document> codec) {
        this.documentCodec = codec;
        this.registry = MongoClient.getDefaultCodecRegistry();
        this.converter = new ItemConverter();
    }

    /**
     * Registry constructor.
     * @param registry The CodecRegistry to use.
     */
    public ItemCodec(CodecRegistry registry) {
        this.registry = registry;
        this.documentCodec = this.registry.get(Document.class);
        this.converter = new ItemConverter();
    }

    /**
     * Encode the passed Item into a Mongo/BSON document.
     * @param writer The writer to use for encoding.
     * @param item The Item to encode.
     * @param encoderContext The EncoderContext to use for encoding.
     */
    @Override
    public void encode(
                    BsonWriter writer,
                    Item item,
                    EncoderContext encoderContext
                ) {
        Document document = this.converter.convert(item);

        documentCodec.encode(writer, document, encoderContext);
    }

    /**
     * Get the class that this Codec works with.
     * @return Returns the class that this Codec works with.
     */
    @Override
    public Class<Item> getEncoderClass() {
        return Item.class;
    }

    /**
     * Decodes a Mongo/BSON document into an Item.
     * @param reader The reader containing the Document.
     * @param decoderContext The DecoderContext to use for decoding.
     * @return Returns the decoded Item.
     */
    @Override
    public Item decode(BsonReader reader, DecoderContext decoderContext) {
        Document document = documentCodec.decode(reader, decoderContext);
        Item item = this.converter.convert(document);

        return item;
    }

    /**
     * Generates a new ObjectId for the passed Item (if absent).
     * @param item The Item to work with.
     * @return Returns the passed Item with a new id added if there
     * was none.
     */
    @Override
    public Item generateIdIfAbsentFromDocument(Item item) {
        if (!documentHasId(item)) {
            item.setId(new ObjectId());
        }

        return item;
    }

    /**
     * Returns whether or not the passed Item has an id.
     * @param Item The Item that you want to check for
     * the presence of an id.
     * @return Returns whether or not the passed Item has an id.
     */
    @Override
    public boolean documentHasId(Item Item) {
        return (Item.getName() != null);
    }

    /**
     * Gets the id of the passed Item. If there is no id, it will
     * throw an IllegalStateException (RuntimeException).
     * @param Item The Item whose id you want to get.
     * @return Returns the id of the passed Item as a BsonValue.
     */
    @Override
    public BsonValue getDocumentId(Item Item)
    {
        if (!documentHasId(Item)) {
            throw new IllegalStateException("The document does not contain an _id");
        }

        return new BsonString(Item.getName());
    }

}

com.example.mongo.OrderCodec

Codec for encoding/decoding Orders.

package com.example.mongo;

import com.example.model.Item;
import com.example.model.Order;
import com.mongodb.MongoClient;
import java.util.ArrayList;
import org.bson.BsonReader;
import org.bson.BsonString;
import org.bson.BsonValue;
import org.bson.BsonWriter;
import org.bson.Document;
import org.bson.codecs.Codec;
import org.bson.codecs.CollectibleCodec;
import org.bson.codecs.DecoderContext;
import org.bson.codecs.EncoderContext;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.types.ObjectId;

/**
 * Mongo decoder for Orders.
 */
public class OrderCodec implements CollectibleCodec<Order> {

    private final CodecRegistry registry;
    private final Codec<Document> documentCodec;
    private final ItemConverter itemConverter;

    /**
     * Default constructor.
     */
    public OrderCodec() {
        this.registry = MongoClient.getDefaultCodecRegistry();
        this.documentCodec = this.registry.get(Document.class);
        this.itemConverter = new ItemConverter();
    }

    /**
     * Codec constructor.
     * @param codec The existing codec to use.
     */
    public OrderCodec(Codec<Document> codec) {
        this.registry = MongoClient.getDefaultCodecRegistry();
        this.documentCodec = codec;
        this.itemConverter = new ItemConverter();
    }

    /**
     * Registry constructor.
     * @param registry The CodecRegistry to use.
     */
    public OrderCodec(CodecRegistry registry) {
        this.registry = registry;
        this.documentCodec = this.registry.get(Document.class);
        this.itemConverter = new ItemConverter();
    }

    /**
     * Encode the passed Order into a Mongo/BSON document.
     * @param writer The writer to use for encoding.
     * @param order The Order to encode.
     * @param encoderContext The EncoderContext to use for encoding.
     */
    @Override
    public void encode(
                    BsonWriter writer,
                    Order order,
                    EncoderContext encoderContext
                ) {
        Document document = new Document();
        document.put("_id", order.getId());
        document.put("items", order.getItems());

        documentCodec.encode(writer, document, encoderContext);
    }

    /**
     * Get the class that this Codec works with.
     * @return Returns the class that this Codec works with.
     */
    @Override
    public Class<Order> getEncoderClass() {
        return Order.class;
    }

    /**
     * Decodes a Mongo/BSON document into an Order.
     * @param reader The reader containing the Document.
     * @param decoderContext The DecoderContext to use for decoding.
     * @return Returns the decoded Order.
     */
    @Override
    public Order decode(BsonReader reader, DecoderContext decoderContext) {
        Document document = documentCodec.decode(reader, decoderContext);

        Order order = new Order();

        order.setId(document.getObjectId("_id"));

        ArrayList<Document> docArr = (ArrayList) document.get("items");
        for (Document doc : docArr) {
            Item item = this.itemConverter.convert(doc);
            order.addItem(item);
        }

        return order;
    }

    /**
     * Generates a new ObjectId for the passed Order (if absent).
     * @param order The Order to work with.
     * @return Returns the passed Order with a new id added if there
     * was none.
     */
    @Override
    public Order generateIdIfAbsentFromDocument(Order order) {
        if (!documentHasId(order)) {
            order.setId(new ObjectId());
        }

        return order;
    }

    /**
     * Returns whether or not the passed Order has an id.
     * @param order The Order that you want to check for
     * the presence of an id.
     * @return Returns whether or not the passed Order has an id.
     */
    @Override
    public boolean documentHasId(Order order) {
        return (order.getId() != null);
    }

    /**
     * Gets the id of the passed Order. If there is no id, it will
     * throw an IllegalStateException (RuntimeException).
     * @param order The Order whose id you want to get.
     * @return Returns the id of the passed Order as a BsonValue.
     */
    @Override
    public BsonValue getDocumentId(Order order) {
        if (!documentHasId(order)) {
            throw new IllegalStateException("The document does not contain an _id");
        }

        return new BsonString(order.getId().toHexString());
    }
}

com.example.main.Main

Main class for the app. Here we register the codecs and create our MongoClient.

package com.example.main;

import com.example.model.Item;
import com.example.model.Order;
import com.example.mongo.ItemCodec;
import com.example.mongo.OrderCodec;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.ServerAddress;
import org.bson.Document;
import org.bson.codecs.Codec;
import org.bson.codecs.configuration.CodecRegistries;
import org.bson.codecs.configuration.CodecRegistry;

/**
 * Main class.
 */
public class Main {

    /**
     * Main function for the app.
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        CodecRegistry codecRegistry = MongoClient.getDefaultCodecRegistry();
        Codec<Document> documentCodec = codecRegistry.get(Document.class);
        Codec<Item> itemCodec = new ItemCodec(codecRegistry);
        Codec<Order> orderCodec = new OrderCodec(codecRegistry);
        codecRegistry = CodecRegistries.fromRegistries(
                                            MongoClient.getDefaultCodecRegistry(),
                                            CodecRegistries.fromCodecs(
                                                documentCodec,
                                                itemCodec,
                                                orderCodec
                                            )
                                        );

        MongoClientOptions options = MongoClientOptions.builder().codecRegistry(codecRegistry).build();
        MongoClient mongo = new MongoClient(new ServerAddress("localhost", 27018), options);

        // Your code here.
    }
}

From there you can read/write orders and items to/from Mongo.