Fast, reflection-free Json binding via apt source code generation. A light (~200kb + generated code) source code generation style alternative to Jackson's ObjectMapper or Gson. (code generation vs reflection)
- Annotate java classes with
@Json(or use@Json.Importfor types we "don't own" or can't annotate) avaje-jsonb-generatorannotation processor generates Java source code to convert to/from json- No need to manually register generated adapters. (Uses ServiceLoader to auto-register)
- Constructors and accessors/getters/setters of any style "just work" (records, constructors, 'fluid setters')
- Jackson-like annotations:
@Json.Raw,@Json.Property,@Json.Ignore,@Json.Alias, etc. - Support Imports and Mixins (adding jsonb features to types we can't directly annotate).
- Supports Generic Types.
- Provides support for dynamic json views (similar in style to that presented by LinkedIn at java one in 2009
- One of the top three fastest Java JSON libraries
Built-in support for reading and writing Java’s core data types:
- Primitives (int, float, char...) and their boxed counterparts (Integer, Float, Character...).
- BigInteger and BigDecimal
- java.time classes (Instant, LocalDate, LocalDateTime...)
- Arrays, Collections, Streams, Lists, Sets, and Maps
- Optionals (will unwrap and serialize the contained value)
- Strings
- Enums
- Other miscellaneous types (UUID, URL, URI)
<dependency> <groupId>io.avaje</groupId> <artifactId>avaje-jsonb</artifactId> <version>${avaje-jsonb-version}</version> </dependency>And add avaje-jsonb-generator as an annotation processor.
<dependency> <groupId>io.avaje</groupId> <artifactId>avaje-jsonb-generator</artifactId> <version>${avaje-jsonb-version}</version> <scope>provided</scope> </dependency>Add @Json to the types we want to serialize.
The avaje-jsonb-generator annotation processor will generate a JsonAdapter as java source code for each type annotated with @Json. These will be automatically registered with Jsonb when it is started using a service loader mechanism.
@Json public class Address { private String street; private String suburb; private String city; // object fields will automatically have adapters generated, no @Json required // (though you can add @Json anyway to modify the generated adapter how you wish) private OtherClass other; //add getters/setters }This also works with records:
@Json public record Address(String street, String suburb, String city) { }For types we cannot annotate with @Json we can place @Json.Import(TypeToimport.class) on any class/package-info to generate the adapters.
// build using defaults Jsonb jsonb = Jsonb.instance(); JsonType<Customer> customerType = jsonb.type(Customer.class); Customer customer = ...; // serialize to json String asJson = customerType.toJson(customer); // deserialize from json Customer customer = customerType.fromJson(asJson);avaje-jsonb supports dynamic json views. This allows us to specify which specific properties to include when serializing to json.
For example:
Jsonb jsonb = Jsonb.instance(); JsonType<Customer> customerType = jsonb.type(Customer.class); // only including the id and name JsonView<Customer> idAndNameView = customerType.view("(id, name)"); String asJson = idAndNameView.toJson(customer); JsonView<Customer> myView = customerType.view("(id, name, billingAddress(*), contacts(lastName, email))"); // serialise to json the above specified properties only String asJson = myView.toJson(customer);Given the class:
@Json public class Address { private String street; private City city; private Suburb suburb; //getters/setters ommited for brevity }The following code will be generated and used for serialization/deserialization.
@Generated public final class AddressJsonAdapter implements JsonAdapter<Address>, ViewBuilderAware { private final JsonAdapter<String> stringJsonAdapter; private final JsonAdapter<City> cityJsonAdapter; private final JsonAdapter<Suburb> suburbJsonAdapter; private final PropertyNames names; public AddressJsonAdapter(Jsonb jsonb) { this.stringJsonAdapter = jsonb.adapter(String.class); this.cityJsonAdapter = jsonb.adapter(City.class); this.suburbJsonAdapter = jsonb.adapter(Suburb.class); this.names = jsonb.properties("street", "city", "suburb"); } @Override public boolean isViewBuilderAware() { return true; } @Override public ViewBuilderAware viewBuild() { return this; } @Override public void build(ViewBuilder builder, String name, MethodHandle handle) { builder.beginObject(name, handle); builder.add("street", stringJsonAdapter, builder.method(Address.class, "getStreet", java.lang.String.class)); builder.add("city", cityJsonAdapter, builder.method(Address.class, "getCity", City.class)); builder.add("suburb", suburbJsonAdapter, builder.method(Address.class, "getSuburb", Suburb.class)); builder.endObject(); } @Override public void toJson(JsonWriter writer, Address address) { writer.beginObject(names); writer.names(names); writer.name(0); stringJsonAdapter.toJson(writer, address.getStreet()); writer.name(1); cityJsonAdapter.toJson(writer, address.getCity()); writer.name(2); suburbJsonAdapter.toJson(writer, address.getSuburb()); writer.endObject(); } @Override public Address fromJson(JsonReader reader) { Address _$address = new Address(); // read json reader.beginObject(names); while (reader.hasNextField()) { final String fieldName = reader.nextField(); switch (fieldName) { case "street": { _$address.setStreet(stringJsonAdapter.fromJson(reader)); break; } case "city": { _$address.setCity(cityJsonAdapter.fromJson(reader)); break; } case "suburb": { _$address.setSuburb(suburbJsonAdapter.fromJson(reader)); break; } default: { reader.unmappedField(fieldName); reader.skipValue(); } } } reader.endObject(); return _$address; } }avaje-jsonb was based on Moshi with some changes as summarised below:
- Generates Java source code (rather than Kotlin)
- Uses custom parser inspired by
dsl-json(with option of usingjackson-core'sJsonParserandJsonGeneratoras parsers) - Has no fallback to reflection - jsonb is code generation or bust.
- JsonReader - Make JsonReader an interface, default implementation using
Jsonb JsonReadAdapter - JsonWriter - Make JsonWriter an interface, default implementation using
Jsonb JsonWriteAdapter - JsonAdapter -> Make JsonAdapter an interface.
- Moshi -> Jsonb - Rename Moshi to Jsonb and make it an interface
- Moshi.Builder -> Jsonb.Builder - Basically the same but Jsonb.Builder as interface plus added Component and AdapterBuilder
- Add JsonType for a more friendly API to use rather than the underlying JsonAdapter
- Add Jsonb.Component interface - allows easy service loading of adapters
- Additionally, it generates a Jsonb.Component and uses service loading to auto-register all generated adapters. This means there is no need to register the generated adapters manually.
- Add fromObject() as a "covert from object" feature like Jackson ObjectMapper
- Add naming convention support
- Add
@Json.Importto generate adapters for types that we can't put the annotation on (types we might not 'own') - Add support for generating adapters (for
@Json.Imported types) with annotations from Jackson, GSON and Jakarta - Add Mixin feature similar to Jackson Mixins
- Add Types.listOf(), Types.setOf(), Types.mapOf() helper methods
- Adds more common Java types with default built-in support - java.time types, java.util.UUID
- Adds support for json views
When using @Json.Import for types we "don't own", we provide basic support for annotations from other popular libraries.
Simply add either jackson-annotations, gson or jakarta.json.bind-api, and use @Json.Import to generate an adapter.
| Avaje Jsonb | Jackson | Gson | Jakarta JSON-B |
|---|---|---|---|
@Json.Alias | @JsonAlias | @SerializedName(alternate=…) | — |
@Json.Creator | @JsonCreator | — | @JsonbCreator |
@Json.Ignore | @JsonIgnore | @Expose(serialize = false) | @JsonbTransient |
@Json.Property | @JsonProperty | @SerializedName | @JsonbProperty |
@Json.Raw | @JsonRawValue | — | — |
@Json.Value | — | — | — |
When using Spring Web, you can use the following dependency to use avaje-jsonb for HTTP serialization:
<dependency> <groupId>io.avaje</groupId> <artifactId>avaje-jsonb-spring-starter</artifactId> <version>${avaje-jsonb-version}</version> </dependency>