Skip to content

SentryMan/avaje-jsonb

 
 

Repository files navigation

Build Maven Central javadoc License Discord

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.Import for types we "don't own" or can't annotate)
  • avaje-jsonb-generator annotation 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 Type Adapters

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)

Quick Start

Step 1 - Add dependencies

<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>

Step 2 - Add @Json

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.

Step 3 - Use

// 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);

Step 4 - Use Json views

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);

Generated Code

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; } }

Inspired by Moshi

avaje-jsonb was based on Moshi with some changes as summarised below:

Changes from Moshi

  • Generates Java source code (rather than Kotlin)
  • Uses custom parser inspired by dsl-json (with option of using jackson-core's JsonParser and JsonGenerator as 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.Import to 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

Optional extensions

Optional support for Jackson, GSON and Jakarta annotations

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

Optional support for Spring Web

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>

About

java json binding library (ObjectMapper) using source code generation

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Java 100.0%