GraalVM Native Image
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. It
also generates codecs for reachable models under the default configuration. At runtime,
ForyJson.builder().build() uses those generated codecs and falls back to interpreted codecs when
no matching generated codec is available, 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
The default configuration is generated automatically. To add generated codecs for a custom
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.
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.
Default-configuration codecs remain available when providers are present, and every reachable provider adds codecs for its configuration. A codegen-enabled runtime uses an interpreted codec whenever no matching generated codec is available. Reflection metadata remains available in either case.
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 codecs for each reachable Kotlin-enabled provider configuration. A provider
configuration with disabled code generation or an unsupported metadata ABI fails image
construction. A Kotlin-enabled runtime configuration with no matching generated codec uses its
prepared interpreted codec.
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 reachable 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
No extra Native Image feature configuration is required. @JsonType is not inherited, so annotate
every concrete application model. An annotated base with a class-literal @JsonSubTypes table
registers its explicit or inferred subtypes automatically. Empty tables are supported for reachable
Java, Kotlin, and Scala 3 sealed schemas. 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. The native executable uses the same effective annotations as the JVM. Kotlin applications use the provider workflow above and must also avoid package-wide opens.
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 JsonByteArray fields and getters select Base64 strings,
Base16 strings, or numeric byte arrays as on the JVM. The builder's byteArrayFormat controls the
default representation; include that setting in a provider when using generated object codecs.
The escapeNonAscii(true) setting applies to the same string values and names as on the JVM;
include it in a provider when using generated object codecs. Raw JSON remains verbatim.
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 selected byte-array
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.