title: GraalVM Native Image sidebar_position: 10 id: graalvm license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Reachable Models

Fory JSON has one Native Image Feature. Java models are discovered from reachable annotations; the Feature does not use the Java annotation processor. Add @JsonType to each reachable concrete Java object model that the native executable reads or writes:

import org.apache.fory.json.ForyJson;
import org.apache.fory.json.annotation.JsonType;
import org.apache.fory.json.annotation.JsonValidator;

@JsonType
public final class User {
  public long id;
  public String name;

  @JsonValidator
  public void validate() {
    if (id < 0) {
      throw new IllegalArgumentException("id must not be negative");
    }
  }
}

public class JsonExample {
  public static void main(String[] args) {
    ForyJson json = ForyJson.builder().build();
    User user = json.fromJson("{\"id\":1,\"name\":\"Ada\"}", User.class);
    System.out.println(json.toJson(user));
  }
}

This is sufficient for correct native execution. During image construction, Fory JSON retains the model metadata and prepares its field, property, creator, record, and JsonAnySetter access. At runtime, ForyJson.builder().build() can therefore use interpreted codecs without application reflection configuration, package exports or opens, or build-time initialization.

An application class configured for build-time initialization may retain a static ForyJson in the image heap. Set withConcurrencyLevel explicitly when the runtime may have a different processor count from the build machine. Any custom codec or module instance retained by that ForyJson must also be safe to create and store at build time.

If a custom configuration should be instantiated only at runtime, return an equivalent temporary configuration from a reachable @ForyJsonProvider for hosted code generation, then build the application's actual ForyJson after image startup. The provider itself runs during image analysis; it does not create the runtime instance.

Generated Codecs

To include generated codecs for a configuration, return that completed configuration from a reachable @ForyJsonProvider:

import org.apache.fory.json.ForyJson;
import org.apache.fory.json.PropertyNamingStrategy;
import org.apache.fory.json.annotation.ForyJsonProvider;

@ForyJsonProvider
public final class JsonConfigs {
  private final ForyJson api =
      ForyJson.builder()
          .writeNullFields(true)
          .withPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)
          .registerCodec(Money.class, new MoneyCodec())
          .build();

  public JsonConfigs() {}

  public ForyJson api() {
    return api;
  }
}

The provider class must be public and concrete and have a public no-argument constructor. Provider members are public, non-static, zero-argument instance methods whose exact return type is ForyJson. Inherited superclass methods and public interface default methods are included. A provider may return multiple configurations, and multiple providers may be reachable. Equivalent configurations are generated once.

Provider objects exist only while the image is built. Prefer a dedicated configuration class with instance fields and methods as shown above; no application native-image.properties entry is needed, and the provider package does not need to be exported or opened to Fory. Static provider methods and fields are not supported.

Only configurations returned by a provider receive generated codecs. The default configuration is not generated implicitly. If a codegen-enabled ForyJson configuration was not included, ordinary Java models and complete value codecs use their prepared interpreted codecs, and Fory JSON logs one process-wide warning recommending a reachable @ForyJsonProvider. Language-module object models that require hosted capabilities fail before reading or writing a value. withCodegen(false) explicitly selects interpreted codecs and does not request generated-codec lookup. Asynchronous compilation is disabled in a native executable.

Kotlin configurations

Kotlin Native Image support uses the same Feature and provider API. Add the Kotlin runtime, then return a codegen-enabled configuration that installs ForyJsonKotlin:

import org.apache.fory.json.ForyJson
import org.apache.fory.json.annotation.ForyJsonProvider
import org.apache.fory.json.kotlin.ForyJsonKotlin

@ForyJsonProvider
class JsonConfigs {
  fun api(): ForyJson = ForyJsonKotlin.builder().build()
}

Annotate each reachable concrete Kotlin model with @JsonType, or register an exact reachable Mixin for a third-party target. Fory reads and validates Kotlin metadata while building the image, then generates the provider-selected codecs. A provider configuration with disabled code generation or an unsupported metadata ABI fails image construction. A Kotlin-enabled runtime configuration that was not returned by a provider fails before it reads or writes a Kotlin object; it never falls back to reflective construction.

An exact generic Kotlin root is available only when its complete binding is reached through a property, constructor argument, container/map child, or closed subtype of a provider-selected concrete root. Keep using jsonTypeRef<T>() at the direct root call; no public root registry or reflection configuration is needed.

Mixins

Use Fory JSON Mixins for models that cannot be modified:

import org.apache.fory.json.ForyJson;
import org.apache.fory.json.annotation.JsonMixin;
import org.apache.fory.json.annotation.JsonProperty;

@JsonMixin(target = ThirdPartyUser.class)
public abstract class ThirdPartyUserMixin {
  @JsonProperty("user_id")
  long id;
}

public class JsonExample {
  public static void main(String[] args) {
    ForyJson json =
        ForyJson.builder().registerMixin(ThirdPartyUserMixin.class).build();
    ThirdPartyUser user = json.fromJson("{\"user_id\":1}", ThirdPartyUser.class);
    System.out.println(json.toJson(user));
  }
}

JsonMixin is a build-time entry point for its exact declared target, so the target does not need JsonType solely to use the Mixin. The registered Mixin class literal must be reachable from the application. The Native Image Feature retains the target metadata and prepares the same access as it does for a direct JsonType model. A provider configuration generates the Mixin target only when that exact Mixin is registered in the returned ForyJson.

Only one source is enabled for an exact target in a built ForyJson. Later registration replaces an earlier source for subsequent build() calls; each built ForyJson instance keeps the immutable snapshot it was built with.

Type Discovery and Construction

The fory-json artifact activates its Native Image Feature automatically. @JsonType is not inherited, so annotate every concrete application model. An annotated base with a class-literal @JsonSubTypes table registers its listed subtypes automatically. Dedicated supported containers, including EnumMap and EnumSet, use their built-in factories. Other reachable concrete Collection and Map root types require a public no-argument constructor. A class referenced only by a class name resolved at runtime is not reachable; JsonSubTypes.Type.className is therefore unsupported in a native image.

Do not add application reflection configuration as a replacement for the generated configuration. The native executable resolves the same effective annotations as the JVM. Kotlin applications use the provider workflow above and must also avoid package-wide opens or reflection configuration.

Annotations and Custom Codecs

Effective JsonValidator methods must be public instance methods with no arguments and a void return type. A model with a directly declared validator must use JsonType. A validator contributed by a registered Mixin uses that exact Mixin-target pair, so the target does not also need JsonType. The Native Image Feature prepares validator access for interpreted configurations and provider-generated codecs invoke the same effective validators. Do not add reflection configuration for validators. Complete custom codecs, complete JsonValue representations, and creators that enforce validation themselves perform their own validation.

Type, field, effective ordinary getter, setter value parameter, and JsonCreator parameter @JsonCodec annotations are supported. The Feature retains every selected complete-value, element, content, Map-key, and Map-value codec constructor. This is the same annotation model used on the JVM and Android.

JsonValue fields and effective public zero-argument methods are supported, including matching one-String JsonCreator constructors and public static factories. Fixed JsonRawValue fields and getters support trusted raw String values, and fixed JsonBase64 fields and getters support Base64 byte[] values as on the JVM. JsonFormat date/time fields use the same direct-field, one-wrapper-level, and timezone behavior as on the JVM. For direct target annotations, annotate each reachable owning model with JsonType so Native Image retains these members and the Base64 codec constructor. A directly annotated JsonValue Record uses its generated component accessor and canonical constructor operations. An effective declaration supplied by a Mixin uses the Mixin workflow above instead.

JsonAnyProperty and JsonAnyGetter flatten their Map into the enclosing object. Use @JsonCodec(valueCodec = ...) on that field or getter to customize each dynamic value. A second JsonAnySetter parameter may use the normal configuration for its own value shape.

JsonUnwrapped uses the same behavior as on the JVM. For direct target annotations, annotate the containing model and every unwrapped child or intermediate object with JsonType. A Mixin retains the unwrapped models reached by its effective schema; register a separate exact Mixin for a child only when that child's annotations also need an overlay.

Child codecs act on one direct level. elementCodec supports Collection, Java arrays, and AtomicReferenceArray; contentCodec supports Optional and AtomicReference; keyCodec and valueCodec support Map keys and values. A complete value codec cannot be combined with a child codec.

An annotation codec must have a public no-argument constructor. Fory prepares that constructor during Native Image construction, so application modules do not need to export or open the codec package. A codec instance supplied through registerCodec is constructed by the application and needs no annotation-constructor metadata.