Library / SDK
ngs-doo/dsl-json avatar
ngs-doo/dsl-json

DSL-JSON avoids runtime reflection by generating the encoder before you run

High performance JVM JSON library

1,073 stars110 forksJavaBSD-3-Clause

At a glance

What is it?
DSL-JSON is a BSD licensed JSON library for the JVM, used from Java, Android, Scala and Kotlin, whose central idea is an annotation processor that analyses your classes and writes the encoding and decoding code at compile time. It also has a reflection mode, support for Jackson and JsonB annotations, custom converters, an array format that omits property names, and byte level deserialisation with no intermediate character representation.
Who is it for?
DSL-JSON suits a Java or Android team with a hot serialisation path, a fixed object model and a willingness to run an annotation processor, and it is a good fit where the same payload is serialised in a tight loop or on a device with little memory to spare. It does not suit a project that needs to serialise arbitrary shapes at runtime, or a team that cannot add a build step.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The idea is to move the work to compile time

DSL-JSON is presented as the fastest JVM JSON library for Java, Android, Scala and Kotlin, with advanced compile time databinding support, and it describes itself as a Java JSON library designed for performance that was originally built for invasive software composition with the DSL Platform compiler.

The mechanism is the @CompiledJson annotation and the annotation processor that goes with it. The processor works by analysing Java classes and their explicit or implicit references, and it outputs encoding and decoding code and descriptions at compile time. The consequences it lists are that this avoids the need for reflection, provides compile time safety, and allows for some advanced configurations.

The performance claim in the README is the project's own, and it is stated as faster than any other Java JSON library and on par with the fastest binary JVM codecs, with a benchmark image in the README. Treat that as a claim about a particular benchmark rather than a measurement you can rely on, but the design rationale is sound on its own terms. A reflection based mapper has to rediscover the shape of a class on every run, and a code generated mapper has already done that work, which is why the generated approach can avoid the repeated lookups, the boxing and the string handling that reflection forces.

The rest of the feature list is the same idea applied to the details. Deserialization can work on byte[] or an InputStream and does not need an intermediate character representation. Converters avoid producing garbage. The library does not rely on Java UNSAFE or internal methods. The runtime dependency weight is around 450KB. And a conversion that could fail at runtime can be made to fail at compile time instead.

Generated code is found two ways, and you can make it work on code you did not annotate

There are two main ways generated code and manual services are detected, and the first is the ordinary one.

The first is lookups from META-INF/services through the ServiceLoader during DslJson initialisation. The second is probing for name conventions, specifically package._NAME_DslJsonConverter, when a converter is required. The convention route is what lets the library find a converter for a type whose name the framework already knows how to derive, rather than one that had to be registered by hand.

The more useful behaviour is the fallback. By default the library only searches for the @CompiledJson annotation, but it can be configured to search for @JacksonCreator and @JsonbCreator as well, and converters will be created even for dependent objects that do not carry the relevant annotation. The README states plainly that this can be used to create serializers for pre-existing classes without annotating them.

That is the difference between the annotation processor mode and the reflection mode, and it is the central trade in the library. The processor gives you generated code, compile time safety and the ability to work on classes you do not own, at the cost of a build step. The reflection mode gives you the same conversions with a runtime cost and none of the compile time checks, at the cost of a much smaller change to your build. The README describes this as customizable runtime overheads, working in reflection mode or annotation processor mode, with annotation based POJOs prepared at compile time.

The examples directory in the repository includes a Reflection example, which is the quickest way to see the difference between the two, alongside examples for ArrayFormat, AutoValue, Jackson, Jsonb, Kotlin, Lombok, Polymorphism, Scala, AndroidJava, AndroidKotlin and MavenJava.

Custom types have three routes, and the processor checks them

A type without a built in mapping can be supported in three ways, and the choice is between annotation, reference and implementation.

The first is to define a custom conversion class and annotate it with @JsonConverter. The second is to define a custom conversion class and reference it from a property with a converter through @JsonAttribute. The third is to implement JsonObject and the appropriate JSON_READER, which is the escape hatch for a type you cannot annotate and cannot adapt.

The processor checks that custom type implementations have the appropriate signatures, so a converter written against the wrong shape is a compile error rather than a runtime failure. There is also a convenience detail: a @JsonConverter which implements Configuration is also registered in META-INF/services, which makes the initialisation setup convenient.

A converter itself is a class with two static methods, one to read and one to write. The example given is a converter for java.time.LocalTime:

java
public static abstract class LocalTimeConverter {
    public static LocalTime read(JsonReader reader) throws IOException {
        return LocalTime.parse(reader.readSimpleString());
    }
    public static void write(JsonWriter writer, LocalTime value) {
        writer.writeString(value.toString());
    }
}

For mutable objects a third optional method is supported, for binding, and an optional default value can be specified in converters, which is used for missing properties during deserialization and for the omit check during serialization. The README gives the reason that feature is useful: introducing new properties into classes which might not be sent yet, as a way to specify the expected value for such new fields.

Array format, streaming and the allocation argument

Three of the features are about how the bytes are shaped rather than about the object model, and they are worth separating from the rest.

The first is the array format. The library supports POJO to object and or array format, and the array format avoids serializing names, while the object format can be used in a minimal serialization mode. For a fixed schema where both ends agree on the field order, dropping the property names from the payload is a large reduction in size, and it is the sort of thing that only works if the type is known at compile time, which is the same reason the annotation processor exists.

The second is streaming. Large JSON lists are supported with streaming and minimal memory usage, which means a list of a hundred thousand objects does not have to be materialised as a hundred thousand Java objects at once. This is the feature that makes the library usable for payloads that do not fit in a sensible heap.

The third is the allocation argument. The README says the converters avoid producing garbage, and separately that a binding to an existing instance is supported during deserialization, so an existing instance can be provided to reduce GC. For a library whose selling point is speed, the interesting question is not only how fast the encoding is but how much it costs the collector afterwards, and these two features are the answers to that.

Alongside them sit two constraints that say something about the engineering. The library does not rely on Java UNSAFE or internal methods, so it stays portable and does not depend on a specific JVM's internals. And the runtime dependency weight is around 450KB, which is a real constraint on Android, where method count and dex size are.

It tries to fit the libraries you already use

The compatibility story is what makes this library usable in an existing codebase rather than only in a new one, and it runs in both directions.

On the annotation side, the library supports other libraries' annotations, and the Jackson and JsonB annotations will be used, with the compile time analysis extendable in various ways. It also provides high level support for the JsonB String and Stream API, with only minimal support for configuration, and there are examples for Jackson and Jsonb in the repository.

On the language side, the Scala support covers Scala collections, primitives and boxed primitives, working without any extra annotations or configuration, and there is a Scala example. The Kotlin support means the annotation processor can be used from Kotlin, and the NonNull annotation is supported, with a Kotlin example and a separate tests-kotlin module.

There is also a legacy name mapping feature, where multiple versions of JSON property names can be mapped into a single POJO using the alternativeNames annotation, which is a migration tool for a payload format that has changed over time, and the @JsonAttribute annotation itself supports a name option to define a custom serialization name, among other customisations.

The generalisation of the initialisation and the generics story is worth a mention too. Generics, builder pattern, factory pattern and constructor with arguments are all supported as relevant initialisation methods, and package private classes and factory methods can be used.

The v1 to v2 break and the state of the release record

There is an explicit upgrade section in the README, and it is short enough to be worth reading before anything else.

The v1 core library was targeting Java 6, while v2 targets Java 8, so there is no need for the dsl-json-java8 project any more and the dsl-json project should be referenced instead. From v2 only a single annotation processor is supported, which previously lived in the java8 project. Joda and some other specific libraries were removed, so if you still need converters for those types you have to include them in your own project. And a few methods that were previously marked as deprecated are now removed, mostly around creating JsonWriter and JsonReader, which should be created through DslJson instead of instantiated directly.

That last point is a small but characteristic kind of change. Removing a deprecated constructor is a compile error for anyone who used it, and it is a signal that the project treats its own API as something it is allowed to change in a major version.

The dependency snippets in the README show what a Maven consumer writes:

xml
<dependency>
  <groupId>com.dslplatform</groupId>
  <artifactId>dsl-json</artifactId>
  <version>2.0.2</version>
</dependency>

and for Android the same artifact is added both as a compile dependency and as an annotation processor, with the JSON binding API provided at compile time only:

groovy
android {
  compileOptions {
    sourceCompatibility 1.8
    targetCompatibility 1.8
  }
}
dependencies {
  compile 'com.dslplatform:dsl-json:2.0.2'
  annotationProcessor 'com.dslplatform:dsl-json:2.0.2'
  provided 'javax.json.bind:javax.json.bind-api:1.0'
}

One thing to check before you depend on it. The repository's release record shows a single published version, 1.4.2 from May 2017, while the README's dependency snippets reference 2.0.2, and the last push was on 2026-10-18 with the repository not archived. The GitHub release list and the Maven coordinates are therefore not obviously in step, and the version you resolve from Maven Central is the one that matters. The licence is BSD-3-Clause, and the repository also carries a DSL.md describing its compatibility with the DSL Platform.

Editorial conclusion

DSL-JSON suits a Java or Android team with a hot serialisation path, a fixed object model and a willingness to run an annotation processor, and it is a good fit where the same payload is serialised in a tight loop or on a device with little memory to spare. It does not suit a project that needs to serialise arbitrary shapes at runtime, or a team that cannot add a build step. Verify first whether you want the annotation processor mode or the reflection mode, because the README is explicit that the runtime overhead is a choice you make, and check the v1 to v2 upgrade notes, since the library dropped the separate java8 artifact and removed several methods that had been marked deprecated.

Frequently asked questions

What is DSL-JSON?

It is a high performance JSON library for the JVM, used from Java, Android, Scala and Kotlin, that works on existing POJO classes through an annotation processor which generates the encoding and decoding code at compile time, avoiding reflection.

How do I add DSL-JSON to a Maven project?

Reference the com.dslplatform:dsl-json artifact, with the annotation processor enabled so the code is generated at compile time. The README's example uses version 2.0.2, and Android projects add the same artifact as both a compile dependency and an annotationProcessor.

Can DSL-JSON work with classes I do not want to annotate?

Yes. Beyond the default @CompiledJson search, the processor can be configured to look for @JacksonCreator and @JsonbCreator, and converters are created even for dependent objects without the relevant annotation, which the README describes as a way to create serializers for pre-existing classes without annotating them.

How do I support a type that DSL-JSON does not know?

In three ways: define a conversion class and annotate it with @JsonConverter, reference a conversion class from a property with a converter through @JsonAttribute, or implement JsonObject with the appropriate JSON_READER. A converter is a class with two static methods, read and write, and the annotation processor checks that the signatures are right.

What changed between version 1 and version 2 of DSL-JSON?

Version 1 targeted Java 6 and version 2 targets Java 8, so the separate dsl-json-java8 project is no longer needed and only a single annotation processor is supported. Joda and some other libraries were removed, so converters for those types have to be supplied by the project, and previously deprecated methods around creating JsonWriter and JsonReader were removed, since they should now be created through DslJson.

Official sources

  1. License: BSD-3-Clause
  2. ngs-doo/dsl-json on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ngs-doo-dsl-json.svg)](https://hysenlabs.com/projects/ngs-doo-dsl-json)

Was this useful?