Binary JSON with bson4jackson

If you’re a Java developer and need to work with JSON, Jackson is the way to go. The library provides outstanding performance when it comes to serializing and deserializing JSON documents, and it offers a wide range of features including support for data binding (converting POJOs to JSON and back) as well as additional data formats.

One of these formats is BSON (short for Binary JSON), a compact binary representation of JSON documents, which has been designed with fast machine readability in mind. BSON has gained prominence as the main data exchange format for the document-oriented database management system MongoDB.

To extend Jackson with the capability of reading and writing BSON documents, I’ve implemented bson4jackson. I’ve been maintaining this library for many years, and this blog post provides an introduction into bson4jackson and explains how to use it to work with BSON in Java.

bson4jackson

Since bson4jackson and Jackson are fully integrated, you can use the familiar API of Jackson to serialize simple POJOs. Take the following class as an example:

public class Person {
    private String name;
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getName() {
        return this.name;
    }
}

Use the ObjectMapper to quickly serialize objects:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
 
import de.undercouch.bson4jackson.BsonFactory;
import de.undercouch.bson4jackson.BsonModule;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.json.JsonMapper;
 
public class ObjectMapperSample {
    public static void main(String[] args) {
        // create dummy POJO
        Person bob = new Person();
        bob.setName("Bob");
 
        // serialize data
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectMapper mapper = JsonMapper.builder(
                new BsonFactory()).build();
        mapper.writeValue(baos, bob);
 
        // deserialize data
        ByteArrayInputStream bais = new ByteArrayInputStream(
                baos.toByteArray());
        Person clone_of_bob = mapper.readValue(bais, Person.class);
 
        assert bob.getName().equals(clone_of_bob.getName());
    }
}

Or use Jackson’s streaming API to serialize the object manually:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
 
import de.undercouch.bson4jackson.BsonFactory;
import tools.jackson.core.JsonGenerator;
import tools.jackson.core.JsonParser;
import tools.jackson.core.JsonToken;
import tools.jackson.core.ObjectReadContext;
import tools.jackson.core.ObjectWriteContext;
 
public class ManualSample {
    public static void main(String[] args) {
        // create dummy POJO
        Person bob = new Person();
        bob.setName("Bob");
 
        // create factory
        BsonFactory factory = new BsonFactory();
 
        // serialize data
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        JsonGenerator gen = factory.createGenerator(
                ObjectWriteContext.empty(), baos);
        gen.writeStartObject();
        gen.writeName("name");
        gen.writeString(bob.getName());
        gen.close();
 
        // deserialize data
        ByteArrayInputStream bais = new ByteArrayInputStream(
                baos.toByteArray());
        JsonParser parser = factory.createParser(
                ObjectReadContext.empty(), bais);
        Person clone_of_bob = new Person();
        parser.nextToken();
        while (parser.nextToken() != JsonToken.END_OBJECT) {
            String fieldname = parser.currentName();
            parser.nextToken();
            if ("name".equals(fieldname)) {
                clone_of_bob.setName(parser.getString());
            }
        }
 
        assert bob.getName().equals(clone_of_bob.getName());
    }
}

Optimized streaming

One drawback of BSON is that each document has to begin with a number denoting its length. When creating a document, this length has to be known in advance, and bson4jackson is forced to buffer the whole document before it can be written to the OutputStream. bson4jackson’s parser ignores this length field, so you may leave it empty. To do so, you have to create the BsonFactory as follows:

BsonFactory fac = new BsonFactory();
fac.enable(BsonGenerator.Feature.ENABLE_STREAMING);

This trick can improve the serialization performance for large documents and reduce the memory footprint significantly. The official MongoDB Java driver also ignores the length field. Therefore, you may also use this optimization if the documents you created with bson4jackson will be read by the MongoDB driver.

Compatibility with Jackson

bson4jackson 3.x is compatible with Jackson 3.x and later.

I’m trying to keep bson4jackson up to date. If there is a compatibility issue, I usually release a fix within a couple of days after the new Jackson version has been published.

If you’re looking for a version compatible with Jackson 2.x, please use bson4jackson 2.18.0. It’s the last version for the 2.x branch.

Download

Pre-compiled binaries

Pre-compiled binary files of bson4jackson can be downloaded from Maven Central. Additionally, you will need a copy of Jackson to start right away.

Maven/Gradle

Alternatively, you can use Maven to download bson4jackson:

<dependencies>
  <dependency>
    <groupId>de.undercouch</groupId>
    <artifactId>bson4jackson</artifactId>
    <version>3.1.0</version>
  </dependency>
</dependencies>

For Gradle, you can use the following snippet:

implementation 'de.undercouch:bson4jackson:3.1.0'

License

bson4jackson is licensed under the Apache License, Version 2.0.

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

If you or your company use any of my projects or like what I’m doing, please consider sponsoring me so I can continue maintaining and developing my projects and new ones!

Thank you so much for your support!

Profile image of Michel Krämer

Sponsor Michel Krämer on GitHub Sponsors

Researcher, software developer, musician, and photographer. I love open source 🥳


Profile image of Michel Krämer

Posted by Michel Krämer on 30 January 2011.
Last updated on 28 February 2026.


Previous post

Generate citations and bibliographies faster with citeproc-java 3.0.0

A new version of the CSL processor has just been released. It is now a pure Java implementation and does not rely on JavaScript and citeproc-js any more. This improves performance and avoids compatibility issues.

Related posts

10 recipes for gradle-download-task

gradle-download-task is a Gradle plugin that allows you to download files during the build process. This post summarizes common patterns and use cases of gradle-download-task and provides useful tips and tricks.

bson4jackson 2.9.2

This blog post summarizes the changes that came with the latest bson4jackson updates. Highlights are support for Decimal128, refactored serializers and deserializers, as well as support for Jackson 2.8 and 2.9.

bson4jackson 2.11.0

This version is a maintenance release that updates Jackson and fixes some minor issues. Performance of the UTF-8 decoder has been improved and precision of BigDecimals has been increased. Updating is recommended for all users.