Skip to content

JSON Migration Helper

A compatibility layer for JSON handling that abstracts away the breaking changes introduced in Vaadin 25, so the same code can run unchanged on Vaadin 14, 23, 24, and 25.

Overview

Vaadin 25 changed how the Flow client–server bridge handles JSON values (event data, executeJs arguments, @ClientCallable arguments and return types). Code written against the old API does not compile or run on Vaadin 25, and code written against the new API does not run on Vaadin 14/23/24. The Json Migration Helper provides drop-in replacements that detect the running Vaadin version at runtime and dispatch to the right implementation, letting libraries and add-ons support all four versions from a single source tree.

Features

  • Zero-effort migration — write once, run on Vaadin 14, 23, 24, and 25.
  • Automatic version detection — picks the right JSON-handling strategy from the runtime Vaadin version.
  • Drop-in replacement — simple static methods replacing version-specific APIs.
  • @ClientCallable compatibility — helpers for JSON arguments and return types on @ClientCallable methods.
  • JsonSerializer and JsonCodec — versions of these utilities for serializing and deserializing Elemental JSON values.

Installation

From the Vaadin Directory

Available in the Vaadin Directory.

Maven dependency

Release versions are published to Maven Central:

<dependency>
    <groupId>com.flowingcode.vaadin</groupId>
    <artifactId>json-migration-helper</artifactId>
    <version>X.Y.Z</version>
</dependency>

Replace X.Y.Z with the latest released version. Snapshot builds are available from maven.flowingcode.com/snapshots.

JavaDoc is published at javadoc.flowingcode.com.

Usage

The helper exposes three usage patterns, in roughly increasing order of intrusiveness:

  1. Lombok @ExtensionMethod — the cleanest, treats helpers as if they were methods on Element / DomEvent.
  2. Direct calls to the JsonMigration static methods.
  3. Instrumentation — for @ClientCallable methods that receive JsonValue arguments.

Using Lombok @ExtensionMethod

The JsonMigration class is designed to be used with Lombok's @ExtensionMethod annotation, which makes its static methods callable as if they were instance methods of Element or DomEvent:

import com.flowingcode.vaadin.jsonmigration.JsonMigration;
import lombok.experimental.ExtensionMethod;

@ExtensionMethod(JsonMigration.class)
public class MyComponent extends Div {

    public MyComponent() {
        getElement().setPropertyJson("property", jsonValue);

        getElement().executeJs("console.log($0)", jsonValue)
            .then(json -> { ... });

        getElement().addEventListener("click", event -> {
            JsonObject eventData = event.getEventData();
        });
    }
}

Returning JSON from @ClientCallable methods

When a @ClientCallable method returns a JSON value, wrap the return value in convertToClientCallableResult so it is shaped correctly for the running Vaadin version:

@ClientCallable
public JsonValue getJsonData() {
    JsonValue json = ...;
    return JsonMigration.convertToClientCallableResult(json);
}

Receiving JSON in @ClientCallable methods

If a method receives JsonValue as an argument, it cannot be annotated with @ClientCallable directly because of compatibility issues. Use @LegacyClientCallable instead and enable instrumentation:

@InstrumentedRoute("legacy-view")
public class ViewWithElementalCallables extends Div {
    @LegacyClientCallable
    public void receiveJson(JsonValue json) {
        // ...
    }
}

// Register via META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener,
// or use `@SpringComponent` with Spring.
public class ViewInitializerImpl extends InstrumentationViewInitializer {
    @Override
    public void serviceInit(ServiceInitEvent event) {
        registerInstrumentedRoute(ViewWithElementalCallables.class);
    }
}

Instrumentation requires the ASM dependency (not provided out-of-the-box in Vaadin 14–23):

<dependency>
    <groupId>org.ow2.asm</groupId>
    <artifactId>asm</artifactId>
    <version>9.8</version>
</dependency>

Instrumentation is intrusive

Instrumentation is a complex mechanism. While the affected code might be worth rewriting to avoid it, this path exists to preserve compatibility with existing implementations.

Direct usage

The helper methods can also be called directly from the JsonMigration class without Lombok or instrumentation:

import com.flowingcode.vaadin.jsonmigration.JsonMigration;
import elemental.json.Json;
import elemental.json.JsonValue;

// 1. Setting a JSON property
JsonValue json = Json.createObject();
// ... populate json
JsonMigration.setPropertyJson(element, "property", json);

// 2. Executing JavaScript
JsonMigration.executeJs(element, "console.log($0)", "Hello World");

// 3. Getting event data
element.addEventListener("click", event -> {
    JsonObject eventData = JsonMigration.getEventData(event);
    // ...
}).addEventData("event.detail");

API

Key public types exposed by the library:

  • JsonMigration — the central entry point. Provides static helpers such as setPropertyJson, executeJs, getEventData, and convertToClientCallableResult.
  • @LegacyClientCallable — replacement for @ClientCallable on methods that receive JsonValue arguments. Requires instrumentation.
  • @InstrumentedRoute — declarative way to enable instrumentation on a view.
  • InstrumentationViewInitializer — programmatic way to register instrumented routes from a VaadinServiceInitListener.
  • JsonSerializer / JsonCodec — utilities for serializing and deserializing Elemental JSON values across Vaadin versions.

For full signatures and behavior, consult the published JavaDoc.

Source code

JsonMigrationHelper on GitHub — distributed under Apache License 2.0.