This is an automated email from the ASF dual-hosted git repository.

chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory-site.git

commit 5d3dc860cf3830f8c49c17478c9dad8fab86ebbf
Author: chaokunyang <[email protected]>
AuthorDate: Thu May 14 05:21:45 2026 +0000

    πŸ”„ synced local 'docs/guide/' with remote 'docs/guide/'
---
 docs/guide/java/field-configuration.md            |  76 ++++++----
 docs/guide/kotlin/index.md                        |   2 +
 docs/guide/kotlin/static-generated-serializers.md | 165 ++++++++++++++++++++++
 docs/guide/xlang/field-nullability.md             |   7 +-
 4 files changed, 217 insertions(+), 33 deletions(-)

diff --git a/docs/guide/java/field-configuration.md 
b/docs/guide/java/field-configuration.md
index 7992f4afc5..1d119c1fc0 100644
--- a/docs/guide/java/field-configuration.md
+++ b/docs/guide/java/field-configuration.md
@@ -25,7 +25,8 @@ This page explains how to configure field-level metadata for 
serialization in Ja
 
 Apache Foryβ„’ provides field-level configuration through annotations:
 
-- **`@ForyField`**: Configure field metadata (id, nullable, ref, dynamic)
+- **`@ForyField`**: Configure field metadata (id, ref, dynamic)
+- **`@Nullable`**: Mark a field type or nested type position as nullable
 - **`@Ignore`**: Exclude fields from serialization
 - **Integer type annotations**: Control integer encoding (varint, fixed, 
tagged, unsigned)
 
@@ -44,6 +45,7 @@ Use annotations on fields:
 
 ```java
 import org.apache.fory.annotation.ForyField;
+import org.apache.fory.annotation.Nullable;
 
 public class Person {
     @ForyField(id = 0)
@@ -52,7 +54,8 @@ public class Person {
     @ForyField(id = 1)
     private int age;
 
-    @ForyField(id = 2, nullable = true)
+    @Nullable
+    @ForyField(id = 2)
     private String nickname;
 }
 ```
@@ -69,7 +72,8 @@ public class User {
     @ForyField(id = 1)
     private String name;
 
-    @ForyField(id = 2, nullable = true)
+    @Nullable
+    @ForyField(id = 2)
     private String email;
 
     @ForyField(id = 3, ref = true)
@@ -82,12 +86,14 @@ public class User {
 
 ### Parameters
 
-| Parameter  | Type      | Default | Description                            |
-| ---------- | --------- | ------- | -------------------------------------- |
-| `id`       | `int`     | `-1`    | Non-negative field tag ID, or no ID    |
-| `nullable` | `boolean` | `false` | Whether the field can be null          |
-| `ref`      | `boolean` | `false` | Enable reference tracking              |
-| `dynamic`  | `Dynamic` | `AUTO`  | Control polymorphism for struct fields |
+| Parameter | Type      | Default | Description                            |
+| --------- | --------- | ------- | -------------------------------------- |
+| `id`      | `int`     | `-1`    | Non-negative field tag ID, or no ID    |
+| `ref`     | `boolean` | `false` | Enable reference tracking              |
+| `dynamic` | `Dynamic` | `AUTO`  | Control polymorphism for struct fields |
+
+Use `@Nullable` on the field type or nested type position for nullable schema
+metadata. `@ForyField` does not carry nullability.
 
 ## Field ID (`id`)
 
@@ -130,18 +136,20 @@ public class User {
 }
 ```
 
-## Nullable Fields (`nullable`)
+## Nullable Fields (`@Nullable`)
 
-Use `nullable = true` for fields that can be `null`:
+Use `@Nullable` for fields that can be `null`:
 
 ```java
 public class Record {
     // Nullable string field
-    @ForyField(id = 0, nullable = true)
+    @Nullable
+    @ForyField(id = 0)
     private String optionalName;
 
     // Nullable Integer field (boxed type)
-    @ForyField(id = 1, nullable = true)
+    @Nullable
+    @ForyField(id = 1)
     private Integer optionalCount;
 
     // Non-nullable field (default)
@@ -152,9 +160,9 @@ public class Record {
 
 **Notes**:
 
-- Default is `nullable = false` (non-nullable)
-- When `nullable = false`, Fory skips writing the null flag (saves 1 byte)
-- Boxed types (`Integer`, `Long`, etc.) that can be null should use `nullable 
= true`
+- Xlang fields are non-nullable by default.
+- When a field is non-nullable, Fory skips writing the null flag.
+- Boxed types (`Integer`, `Long`, etc.) that can be null should use 
`@Nullable`.
 
 ## Reference Tracking (`ref`)
 
@@ -163,10 +171,12 @@ Enable reference tracking for fields that may be shared 
or circular:
 ```java
 public class RefOuter {
     // Both fields may point to the same inner object
-    @ForyField(id = 0, ref = true, nullable = true)
+    @Nullable
+    @ForyField(id = 0, ref = true)
     private RefInner inner1;
 
-    @ForyField(id = 1, ref = true, nullable = true)
+    @Nullable
+    @ForyField(id = 1, ref = true)
     private RefInner inner2;
 }
 
@@ -175,7 +185,8 @@ public class CircularRef {
     private String name;
 
     // Self-referencing field for circular references
-    @ForyField(id = 1, ref = true, nullable = true)
+    @Nullable
+    @ForyField(id = 1, ref = true)
     private CircularRef selfRef;
 }
 ```
@@ -397,6 +408,7 @@ import org.apache.fory.Fory;
 import org.apache.fory.annotation.ForyField;
 import org.apache.fory.annotation.Ignore;
 import org.apache.fory.annotation.Int64Type;
+import org.apache.fory.annotation.Nullable;
 import org.apache.fory.annotation.UInt64Type;
 import org.apache.fory.config.Int64Encoding;
 
@@ -413,7 +425,8 @@ public class Document {
     private int version;
 
     // Nullable field
-    @ForyField(id = 2, nullable = true)
+    @Nullable
+    @ForyField(id = 2)
     private String description;
 
     // Collection fields
@@ -437,7 +450,8 @@ public class Document {
     private @UInt64Type(encoding = Int64Encoding.TAGGED) long checksum;   // 
tagged encoding
 
     // Reference-tracked field for shared/circular references
-    @ForyField(id = 9, ref = true, nullable = true)
+    @Nullable
+    @ForyField(id = 9, ref = true)
     private Document parent;
 
     // Ignored field (not serialized)
@@ -487,7 +501,8 @@ public class CrossLangData {
     @ForyField(id = 2)
     private @UInt64Type(encoding = Int64Encoding.TAGGED) long longTagged;
 
-    @ForyField(id = 3, nullable = true)
+    @Nullable
+    @ForyField(id = 3)
     private String optionalValue;
 }
 ```
@@ -514,7 +529,8 @@ public class DataV2 {
     @ForyField(id = 1)
     private String name;
 
-    @ForyField(id = 2, nullable = true)
+    @Nullable
+    @ForyField(id = 2)
     private String email;  // New field
 }
 ```
@@ -561,13 +577,13 @@ public class User {
 
 Xlang mode has **stricter default values** due to type system differences 
between languages:
 
-- **Nullable**: Fields are non-nullable by default (`nullable = false`)
+- **Nullable**: Fields are non-nullable by default
 - **Ref tracking**: Disabled by default (`ref = false`)
 - **Polymorphism**: Concrete types are non-polymorphic by default
 
 In xlang mode, you **need to configure fields** when:
 
-- A field can be null (use `nullable = true`)
+- A field can be null (use `@Nullable`)
 - A field needs reference tracking for shared/circular objects (use `ref = 
true`)
 - Integer types need specific encoding for cross-language compatibility
 - You want to reduce metadata size (use field IDs)
@@ -581,10 +597,12 @@ public class User {
     @ForyField(id = 1)
     private String name;
 
-    @ForyField(id = 2, nullable = true)  // Must declare nullable
+    @Nullable
+    @ForyField(id = 2) // Must declare @Nullable
     private String email;
 
-    @ForyField(id = 3, ref = true, nullable = true)  // Must declare ref for 
shared objects
+    @Nullable
+    @ForyField(id = 3, ref = true) // Must declare ref for shared objects
     private User friend;
 }
 ```
@@ -600,7 +618,7 @@ public class User {
 ## Best Practices
 
 1. **Configure field IDs**: Recommended for compatible mode to reduce 
serialization cost
-2. **Use `nullable = true` for nullable fields**: Required for fields that can 
be null
+2. **Use `@Nullable` for nullable fields**: Required for fields that can be 
null
 3. **Enable ref tracking for shared objects**: Use `ref = true` when objects 
are shared or circular
 4. **Use `@Ignore` or `transient` for sensitive data**: Passwords, tokens, 
internal state
 5. **Choose appropriate encoding**: `varint` for small values, `fixed` for 
full-range values
@@ -612,7 +630,7 @@ public class User {
 | Annotation                    | Description                            |
 | ----------------------------- | -------------------------------------- |
 | `@ForyField(id = N)`          | Field tag ID to reduce metadata size   |
-| `@ForyField(nullable = true)` | Mark field as nullable                 |
+| `@Nullable`                   | Mark field or nested type as nullable  |
 | `@ForyField(ref = true)`      | Enable reference tracking              |
 | `@ForyField(dynamic = ...)`   | Control polymorphism for struct fields |
 | `@Ignore`                     | Exclude field from serialization       |
diff --git a/docs/guide/kotlin/index.md b/docs/guide/kotlin/index.md
index 1fcee0430b..af8f6bf106 100644
--- a/docs/guide/kotlin/index.md
+++ b/docs/guide/kotlin/index.md
@@ -38,6 +38,7 @@ Fory Kotlin inherits all features from Fory Java, plus 
Kotlin-specific optimizat
 - **High Performance**: JIT code generation, zero-copy, 20-170x faster than 
traditional serialization
 - **Kotlin Type Support**: Optimized serializers for data classes, unsigned 
types, ranges, and stdlib types
 - **Default Value Support**: Automatic handling of Kotlin data class default 
parameters during schema evolution
+- **Static Xlang Serializers**: KSP-generated schema serializers for 
Kotlin/JVM and Android xlang mode
 - **Schema Evolution**: Forward/backward compatibility for class schema changes
 
 See [Java Features](../java/index.md#features) for complete feature list.
@@ -106,3 +107,4 @@ Fory Kotlin is built on top of Fory Java. Most 
configuration options, features,
 - [Fory Creation](fory-creation.md) - Kotlin-specific Fory setup requirements
 - [Type Serialization](type-serialization.md) - Serializing Kotlin types
 - [Default Values](default-values.md) - Kotlin data class default values 
support
+- [Static Generated Serializers](static-generated-serializers.md) - KSP 
xlang/schema serializer generation
diff --git a/docs/guide/kotlin/static-generated-serializers.md 
b/docs/guide/kotlin/static-generated-serializers.md
new file mode 100644
index 0000000000..f2a2e48b1b
--- /dev/null
+++ b/docs/guide/kotlin/static-generated-serializers.md
@@ -0,0 +1,165 @@
+---
+title: Static Generated Serializers
+sidebar_position: 4
+id: static_generated_serializers
+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.
+---
+
+`fory-kotlin-ksp` generates Kotlin source serializers for Fory xlang/schema
+mode. The generated serializers use the existing Java runtime
+`WriteContext`, `ReadContext`, and `MemoryBuffer`; they do not define a
+Kotlin-only protocol or buffer abstraction.
+
+## Scope
+
+KSP generation is only for xlang/schema mode. It does not generate Fory Java
+native object serializers and does not preserve concrete JVM runtime identity
+for object graphs. Collection declarations are schema carriers: `List<T>` is
+encoded as `list<T>`, `Map<K, V>` as `map<K, V>`, and deserialization only
+guarantees a value assignable to the declared Kotlin field type.
+
+Mutable collection interface fields are supported by reconstructing a mutable
+implementation assignable to the declared type. Sorted collection declarations
+without an explicit comparator, such as `TreeSet` and `ConcurrentSkipListSet`,
+are accepted only for non-null scalar or string elements. Concurrent map
+declarations are accepted only with non-null values because the JVM concurrent
+map implementations reject null entries.
+
+Kotlin/JVM and Android are supported. Kotlin/Native and Kotlin/JS are not
+supported by this module.
+
+Phase 1 KSP generation emits serializers for public concrete, non-generic
+`@ForyStruct` classes with primary-constructor fields and a public or internal
+primary constructor. Kotlin `private` and `internal` struct classes are 
rejected
+because generated serializers and SPI providers are public runtime entry
+points. Kotlin `object` declarations, annotated interfaces, abstract classes,
+sealed declarations, and generic struct targets are rejected during KSP
+processing because they are not single constructor-field serializer targets for
+the phase 1 generator. Dense primitive and unsigned array types, such as
+`IntArray` and `UIntArray`, are supported as top-level fields. Nested dense
+arrays, such as `List<UIntArray>` or `Map<String, IntArray>`, are rejected in
+phase 1. Fory Java `@ArrayType` is also supported on top-level `List<T>` fields
+when `T` is a non-null bool or numeric dense-array element domain. The 
generated
+serializer writes the field as dense `array<T>` schema and converts decoded JVM
+list elements back to the declared Kotlin carrier, such as `Int`, `UInt`, or
+`Double`.
+
+## Annotations
+
+Reuse Java annotations for Fory concepts:
+
+```kotlin
+import org.apache.fory.annotation.ForyField
+import org.apache.fory.annotation.ForyStruct
+import org.apache.fory.kotlin.Fixed
+import org.apache.fory.kotlin.VarInt
+
+@ForyStruct
+data class User(
+  @ForyField(id = 1)
+  val id: @Fixed UInt,
+
+  @ForyField(id = 2)
+  val score: @VarInt Long,
+)
+```
+
+Use `@ForyField(id = 1)`. `@field:ForyField(id = 1)` is also accepted for
+field-backed properties. `@get:ForyField` and `@set:ForyField` are rejected
+because accessors are not schema fields.
+
+Kotlin nullability is expressed with `?`, including nested positions such as
+`List<String?>?`. Do not use Fory `@Nullable` in Kotlin source.
+
+`@ForyField(ref = true)` is rejected by the KSP xlang processor. Phase 1
+generated serializers construct Kotlin values through primary constructors and
+therefore cannot publish partially constructed objects for cyclic
+back-references. Use non-cyclic xlang schemas for Kotlin KSP serializers.
+
+Kotlin default constructor arguments are supported for compatible reads by
+generating constructor calls that omit missing defaulted arguments. Because
+Kotlin source generation must emit these calls statically, the first KSP
+implementation supports at most 12 defaulted constructor fields per struct.
+
+## Encoding
+
+Kotlin type-use encoding annotations map to Fory xlang integer encodings:
+
+| Annotation | Valid Kotlin types             |
+| ---------- | ------------------------------ |
+| `@Fixed`   | `Int`, `Long`, `UInt`, `ULong` |
+| `@VarInt`  | `Int`, `Long`, `UInt`, `ULong` |
+| `@Tagged`  | `Long`, `ULong`                |
+
+Without an annotation, xlang `Int`, `Long`, `UInt`, and `ULong` use varint
+encoding. This is required by xlang mode and is not controlled by Java native
+mode numeric compression options.
+
+## Registration
+
+Users register Kotlin struct classes with the normal Fory Java registration
+APIs. Do not reference generated serializer class names.
+
+```kotlin
+val fory = Fory.builder()
+  .withXlang(true)
+  .requireClassRegistration(true)
+  .build()
+
+KotlinSerializers.registerSerializers(fory)
+fory.register(User::class.java, "example", "User")
+```
+
+The KSP processor emits a service provider that maps target classes to their
+generated serializers. On Android and for Kotlin classes, the Java runtime
+requires this provider mapping for xlang structs. Missing KSP/SPI metadata is a
+configuration error, not a reflective fallback path. This avoids generated-name
+`Class.forName` lookups and works as an R8-compatible discovery path when the
+generated service resource and serializer classes are kept by the application
+build.
+
+Android builds that run R8 must preserve the generated SPI resource and the
+generated provider/serializer classes. Application shrinker rules should keep:
+
+```text
+-keep class * implements 
org.apache.fory.resolver.StaticGeneratedSerializerProvider { *; }
+-keep class * implements 
org.apache.fory.resolver.StaticGeneratedSerializerProvider$KotlinSymbolProcessor
 { *; }
+-keep class * implements 
org.apache.fory.resolver.StaticGeneratedSerializerProvider$JavaAnnotationProcessor
 { *; }
+-keep class **.*__ForySerializer__ { *; }
+-keep class org.apache.fory.resolver.StaticGeneratedSerializerProvider { *; }
+-keep class org.apache.fory.resolver.StaticGeneratedSerializerProvider$* { *; }
+```
+
+The Kotlin KSP generated service resource
+`META-INF/services/org.apache.fory.resolver.StaticGeneratedSerializerProvider$KotlinSymbolProcessor`
+must also be packaged. Java annotation-processor generated serializers use the
+separate
+`META-INF/services/org.apache.fory.resolver.StaticGeneratedSerializerProvider$JavaAnnotationProcessor`
+resource so mixed Java/Kotlin artifacts do not overwrite one another's provider
+lists. If a build plugin strips service resources, configure the application
+packaging step to retain these files.
+
+The KSP processor emits one aggregate provider for the compilation module in
+`org.apache.fory.kotlin.generated`. The provider class name includes a stable
+hash of the generated target mappings, so multiple Kotlin artifacts can publish
+providers on the same classpath without binary-name collisions. That provider
+contains mappings for all annotated Kotlin structs in the module, across source
+packages.
+
+Applications must package the generated service resource. There is no manual
+provider registration API; users still register only target classes and their
+IDs or xlang names through the normal Fory Java registration APIs.
diff --git a/docs/guide/xlang/field-nullability.md 
b/docs/guide/xlang/field-nullability.md
index 5b9d645768..bea7f3f092 100644
--- a/docs/guide/xlang/field-nullability.md
+++ b/docs/guide/xlang/field-nullability.md
@@ -99,7 +99,7 @@ public class Person {
     List<String> tags;    // Must not be null
 
     // Explicitly nullable
-    @ForyField(nullable = true)
+    @Nullable
     String nickname;      // Can be null
 
     // Optional wrapper - nullable by default
@@ -188,14 +188,13 @@ FORY_STRUCT(Person, name, age, tags, nickname, bio);
 
 ## Customizing Nullability
 
-### Java: @ForyField Annotation
+### Java: @Nullable Annotation
 
 ```java
 public class Config {
-    @ForyField(nullable = true)
+    @Nullable
     String optionalSetting;  // Explicitly nullable
 
-    @ForyField(nullable = false)
     String requiredSetting;  // Explicitly non-nullable (default)
 }
 ```


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to