Champollion strictly implements the Jakarta https://jakarta.ee/specifications/jsonp/2.1/ and https://jakarta.ee/specifications/jsonb/3.0/ specs. Migrating from Parsson or Yasson is essentially a Maven coordinate swap. Migrating from Jackson requires rewriting proprietary annotations into their standard Jakarta equivalents.

From Parsson (Eclipse JSON-P RI)

Parsson is the Eclipse Reference Implementation of JSON-P 2.1. The swap is mechanical.

Maven

<!-- Before -->
<dependency>
  <groupId>org.eclipse.parsson</groupId>
  <artifactId>parsson</artifactId>
  <version>1.1.7</version>
</dependency>

<!-- After -->
<dependency>
  <groupId>io.vidocq.champollion</groupId>
  <artifactId>champollion-jsonp</artifactId>
  <version>${champollion.version}</version>
</dependency>

Application code

Nothing changes. Application code only uses jakarta.json.* (the spec) — JsonProvider resolution goes through ServiceLoader.

Expected performance

Champollion is roughly 2-3× slower than Parsson on streaming (pull parser and push generator). For pure JSON-P intensive workloads, Parsson remains faster. Details in lien:https://codeberg.org/Vidocq/champollion/src/branch/main/BENCH.md[BENCH.md].

Benefits of switching

  • Zero dependency — Parsson pulls jakarta.json-api; Champollion re-exports the spec without additional external dependencies.

  • Strict JPMS, internal packages not exported.

  • Consistency with the rest of the Vidocq ecosystem (io.vidocq.*).

From Yasson (Eclipse JSON-B RI)

Yasson is the Eclipse Reference Implementation of JSON-B 3.0.

Maven

<!-- Before -->
<dependency>
  <groupId>org.eclipse</groupId>
  <artifactId>yasson</artifactId>
  <version>3.0.4</version>
</dependency>

<!-- After -->
<dependency>
  <groupId>io.vidocq.champollion</groupId>
  <artifactId>champollion-jsonb</artifactId>
  <version>${champollion.version}</version>
</dependency>

Application code

No application code change. All @Jsonb* annotations are standard Jakarta.

To benefit from reflection-free bindings (Champollion’s identity-defining strength):

  1. Add the APT in provided scope:

    <dependency>
      <groupId>io.vidocq.champollion</groupId>
      <artifactId>champollion-codegen-apt</artifactId>
      <version>${champollion.version}</version>
      <scope>provided</scope>
    </dependency>
  2. Annotate root types with @JsonbStatic (optional — the APT also scans types annotated with @JsonbProperty etc.).

  3. Recompile. The runtime auto-discovers <Type>$$Binding via ServiceLoader. Set champollion.jsonb.warn-on-fallback=true to verify no type falls back to reflection.

Expected performance

Champollion is on par with Yasson on every workload (runtime mode). In static codegen mode, the gap narrows further — see lien:https://codeberg.org/Vidocq/champollion/src/branch/main/BENCH.md[BENCH.md] §7.

Subtle differences

  • No synchronized, no ThreadLocal — better behavior under virtual threads.

  • The error on a missing key in JsonObject.getString(key) is stricter (NPE per spec §2.1.4) — Yasson tolerated silently.

From Jackson

Jackson does not implement Jakarta JSON-B — it has its own annotation universe (@JsonProperty, @JsonIgnore, @JsonFormat, @JsonInclude…​). Migration requires rewriting annotations.

Mapping table

Jackson Jakarta JSON-B (Champollion) Notes

@JsonProperty("foo")

@JsonbProperty("foo")

Direct.

@JsonIgnore

@JsonbTransient

Direct.

@JsonFormat(pattern = "yyyy-MM-dd")

@JsonbDateFormat("yyyy-MM-dd")

For java.time.* / Date types.

@JsonInclude(Include.NON_NULL) (class-level)

JsonbConfig.withNullValues(false) (global) or @JsonbNillable(false) (per property)

Different granularity.

@JsonSerialize(using = X.class)

@JsonbTypeSerializer(X.class)

Different signatures (see below).

@JsonDeserialize(using = X.class)

@JsonbTypeDeserializer(X.class)

Idem.

@JsonCreator + @JsonProperty

@JsonbCreator + @JsonbProperty

Records: no annotation required.

@JsonTypeInfo + @JsonSubTypes

@JsonbTypeInfo + @JsonbSubtype

New in JSON-B 3.0.

@JsonNaming(SnakeCaseStrategy.class)

JsonbConfig.withPropertyNamingStrategy(LOWER_CASE_WITH_UNDERSCORES)

Global config rather than annotation.

ObjectMapper

Jsonb (via JsonbBuilder.create())

Jsonb is thread-safe, created once.

objectMapper.writeValueAsString(o)

jsonb.toJson(o)

Likewise on read: readValuefromJson.

Custom serializer

// Jackson
public class MyJsonSer extends JsonSerializer<Money> {
    @Override public void serialize(Money m, JsonGenerator g, SerializerProvider p) throws IOException {
        g.writeString(m.amount() + " " + m.currency());
    }
}

// Champollion (Jakarta JSON-B)
public final class MyJsonbSer implements JsonbSerializer<Money> {
    @Override public void serialize(Money m, JsonGenerator g, SerializationContext ctx) {
        g.write(m.amount() + " " + m.currency());
    }
}

The JsonGenerator on the JSON-B side is the Jakarta JSON-P one — not Jackson’s ObjectGenerator. No checked IOException, fluent.

Expected performance

Jackson remains 3-4× faster than Champollion on binding (15 years of POJO-specific optimizations + bytecode codegen of accessors). However, Jackson is not a Jakarta JSON-B implementation — migration is only meaningful to align with the Jakarta specs and benefit from standard portability.

Benefits of switching

  • Conformance to Jakarta JSON-B 3.0 — portable across all Jakarta implementations.

  • Native AOT compatibility (GraalVM, Leyden) in static codegen mode.

  • Zero external dependency.

  • Strict JPMS, native virtual threads.

From Gson, Genson, JSON-Simple…​

Same principles as Jackson: these libs are proprietary and migration to Champollion (= Jakarta JSON-B) requires replacing their annotations with their standard equivalents. Gson, Genson, etc. do not implement Jakarta JSON-B and are not ServiceLoader-replaceable.

Validating the migration

# Differential test: emit JSON with the old lib and the new one, compare
mvn -ntp -Dtest=MigrationDifferentialTest test

For an existing project, we recommend introducing Champollion alongside the legacy lib for one test cycle, then switching definitively after output validation.