1. Overview

In this previous article, we’ve seen how to retrieve BSON documents as Java objects from MongoDB.

This is a very common way to develop a REST API, as we may want to modify these objects before converting them to JSON (using Jackson for example).

However, we might not want to change anything to our documents. To save us the trouble of coding verbose Java objects mapping, we can use direct BSON to JSON document conversion.

Let’s see how MongoDB BSON API works for this use case.

2. BSON Document Creation in MongoDB with Morphia

First of all, let’s set up our dependencies using Morphia as described in this article.

Here is our example entity which includes various attribute types:

@Entity("Books")
public class Book {
    @Id
    private String isbn;

    @Embedded
    private Publisher publisher;

    @Property("price")
    private double cost;

    @Property
    private LocalDateTime publishDate;

    // Getters and setters ...
}

Then let’s create a new BSON entity for our test and save it to MongoDB:

public class BsonToJsonLiveTest {

    private static final String DB_NAME = "library";
    private static Datastore datastore;

    @BeforeClass
    public static void setUp() {
        datastore = Morphia.createDatastore(MongoClients.create(), DB_NAME);
        datastore.getMapper().mapPackage("com.baeldung.bsontojson");
        datastore.ensureIndexes();

        datastore.save(new Book()
            .setIsbn("isbn")
            .setTitle("title")
            .setAuthor("author")
            .setCost(3.95)
            .setPublisher(new Publisher(new ObjectId("fffffffffffffffffffffffa"),"publisher"))
            .setPublishDate(LocalDateTime.parse("2020-01-01T17:13:32Z", DateTimeFormatter.ISO_DATE_TIME))
            .addCompanionBooks(new Book().setIsbn("isbn2")));
    }
}

3. Default BSON to JSON Document Conversion

Now let’s test the default conversion which is very simple: simply call toJson method from the BSON Document class:

@Test
    public void givenBsonDocument_whenUsingStandardJsonTransformation_thenJsonDateIsObjectEpochTime() {

        String json;
        try (MongoClient mongoClient = MongoClients.create()) {
            MongoDatabase mongoDatabase = mongoClient.getDatabase(DB_NAME);
            Document bson = mongoDatabase.getCollection("Books").find().first();
            json = bson.toJson(JsonWriterSettings
                    .builder()
                    .dateTimeConverter(new JSONDateFormatEpochTime())
                    .build());
        }

        String expectedJson = "{\"_id\": \"isbn\", " +
            "\"_t\": \"Book\", " +
            "\"title\": \"title\", " +
            "\"author\": \"author\", " +
            "\"publisher\": {\"_id\": {\"$oid\": \"fffffffffffffffffffffffa\"}, " +
            "\"_t\": \"Publisher\", \"name\": \"publisher\"}, " +
            "\"price\": 3.95, " +
            "\"publishDate\": {\"$date\": 1577898812000}}";

        assertNotNull(json);

        assertEquals(expectedJson, json);
    }

The expectedJson value is:

{
    "_id": "isbn",
    "className": "com.baeldung.morphia.domain.Book",
    "publisher": {
        "_id": {
            "$oid": "fffffffffffffffffffffffa"
        },
        "name": "publisher"
    },
    "price": 3.95,
    "publishDate": {
        "$date": 1577898812000
    }
}

To convert to epoch time I created a class that convert that value, that class is called: JSONDateFormatEpochTime.

This seems to correspond to a standard JSON mapping.

However, we can see that the date was converted by default as an object with a $date field in epoch time format. Let’s see now how we can change this date format.

3.1. Custom BSON to JSON Data Conversion

This is just a simple class tht extends Convertor where in the overwrite method we write in the JSON the epoch time received to the Published data object.

public class JSONDateFormatEpochTime implements Converter<Long> {

    @Override
    public void convert(Long value, StrictJsonWriter writer) {
        writer.writeStartObject();
        writer.writeName("$date");
        writer.writeNumber(String.valueOf(value));
        writer.writeEndObject();
    }
}

4. Relaxed BSON to JSON Date Conversion

For instance, if we want a more classic ISO date representation (such as for a JavaScript client), we can pass the relaxed* JSON mode to the toJson method, using *JsonWriterSettings.builder:

bson.toJson(JsonWriterSettings
  .builder()
  .outputMode(JsonMode.RELAXED)
  .build());

As a result, we can see the publishDate field’s “relaxed” conversion:

{
    ...
    "publishDate": {
        "$date": "2020-01-01T17:13:32Z"
    }
    ...
}

This format seems correct, but we still have the $date field — let’s see how to get rid of it using a custom converter.

5. Custom BSON to JSON Date Conversion

First, we have to implement the BSON Converter interface for type Long, as date values are expressed in milliseconds since epoch time. We’re using DateTimeFormatter.ISO_INSTANT to get the expected output format:

public class JsonDateTimeConverter implements Converter<Long> {

    private static final Logger LOGGER = LoggerFactory.getLogger(JsonDateTimeConverter.class);
    static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ISO_INSTANT
        .withZone(ZoneId.of("UTC"));

    @Override
    public void convert(Long value, StrictJsonWriter writer) {
        try {
            Instant instant = new Date(value).toInstant();
            String s = DATE_TIME_FORMATTER.format(instant);
            writer.writeString(s);
        } catch (Exception e) {
            LOGGER.error(String.format("Fail to convert offset %d to JSON date", value), e);
        }
    }
}

Then, we can pass an instance of this class as a DateTime converter to the JsonWriterSettings builder:

bson.toJson(JsonWriterSettings
  .builder()
  .dateTimeConverter(new JsonDateTimeConverter())
  .build());

Finally, we get a plain JSON ISO date format:

{
    ...
    "publishDate": "2020-01-01T17:13:32Z"
    ...
}

6. Conclusion

In this article, we’ve seen the default behavior of BSON to JSON document conversion.

We highlighted how to customize the date format, which is a common issue, using BSON Converter.

Of course, we can proceed the same way to convert other data types: number, boolean, null value, or object id, for example.

As always, the code can be found over on GitHub.