Persistence top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
Java Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

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 BsonToJsonIntegrationTest {
    
    private static final String DB_NAME = "library";
    private static Datastore datastore;

    @BeforeClass
    public static void setUp() {
        Morphia morphia = new Morphia();
        morphia.mapPackage("com.baeldung.morphia");
        datastore = morphia.createDatastore(new MongoClient(), DB_NAME);
        datastore.ensureIndexes();
        
        datastore.save(new Book()
          .setIsbn("isbn")
          .setCost(3.95)
          .setPublisher(new Publisher(new ObjectId("fffffffffffffffffffffffa"),"publisher"))
          .setPublishDate(LocalDateTime.parse("2020-01-01T18:13:32Z", DateTimeFormatter.ISO_DATE_TIME)));
    }
}

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 = null;
     try (MongoClient mongoClient = new MongoClient()) {
         MongoDatabase mongoDatabase = mongoClient.getDatabase(DB_NAME);
         Document bson = mongoDatabase.getCollection("Books").find().first();
         assertEquals(expectedJson, bson.toJson());
     }
}

The expectedJson value is:

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

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.

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.

Persistence bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
Java bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
Comments are closed on this article!