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


The following commit(s) were added to refs/heads/main by this push:
     new 988327994 πŸ”„ synced local 'docs/guide/' with remote 'docs/guide/'
988327994 is described below

commit 98832799426d4edcec432da5f94ca93ac50ccf19
Author: chaokunyang <[email protected]>
AuthorDate: Wed Jan 14 03:56:53 2026 +0000

    πŸ”„ synced local 'docs/guide/' with remote 'docs/guide/'
---
 docs/guide/cpp/custom-serializers.md     | 371 +++++++++++++++++++
 docs/guide/cpp/field-configuration.md    |  40 ++
 docs/guide/go/struct-tags.md             |  33 +-
 docs/guide/java/field-configuration.md   | 605 +++++++++++++++++++++++++++++++
 docs/guide/python/field-configuration.md | 492 +++++++++++++++++++++++++
 docs/guide/python/numpy-integration.md   |   4 +-
 docs/guide/rust/field-configuration.md   | 462 +++++++++++++++++++++++
 docs/guide/xlang/field-type-meta.md      | 301 +++++++++++++++
 8 files changed, 2305 insertions(+), 3 deletions(-)

diff --git a/docs/guide/cpp/custom-serializers.md 
b/docs/guide/cpp/custom-serializers.md
new file mode 100644
index 000000000..187105f50
--- /dev/null
+++ b/docs/guide/cpp/custom-serializers.md
@@ -0,0 +1,371 @@
+---
+title: Custom Serializers
+sidebar_position: 4
+id: custom_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.
+---
+
+For types that don't support `FORY_STRUCT`, implement a `Serializer` template 
specialization manually.
+
+## When to Use Custom Serializers
+
+- External types from third-party libraries
+- Types with special serialization requirements
+- Legacy data format compatibility
+- Performance-critical custom encoding
+- Cross-language interoperability with custom protocols
+
+## Implementing the Serializer Template
+
+To create a custom serializer, specialize the `Serializer` template for your 
type within the `fory::serialization` namespace:
+
+```cpp
+#include "fory/serialization/fory.h"
+
+using namespace fory::serialization;
+
+// Define your custom type
+struct MyExt {
+  int32_t id;
+  bool operator==(const MyExt &other) const { return id == other.id; }
+};
+
+namespace fory {
+namespace serialization {
+
+template <>
+struct Serializer<MyExt> {
+  // Declare as extension type for custom serialization
+  static constexpr TypeId type_id = TypeId::EXT;
+
+  // Main write method - handles null checking and type info
+  static void write(const MyExt &value, WriteContext &ctx, RefMode ref_mode,
+                    bool write_type, bool has_generics = false) {
+    (void)has_generics;
+    write_not_null_ref_flag(ctx, ref_mode);
+    if (write_type) {
+      auto result = ctx.write_any_typeinfo(
+          static_cast<uint32_t>(TypeId::UNKNOWN),
+          std::type_index(typeid(MyExt)));
+      if (!result.ok()) {
+        ctx.set_error(std::move(result).error());
+        return;
+      }
+    }
+    write_data(value, ctx);
+  }
+
+  // Write only the data (no type info)
+  static void write_data(const MyExt &value, WriteContext &ctx) {
+    Serializer<int32_t>::write_data(value.id, ctx);
+  }
+
+  // Write data with generics support
+  static void write_data_generic(const MyExt &value, WriteContext &ctx,
+                                 bool has_generics) {
+    (void)has_generics;
+    write_data(value, ctx);
+  }
+
+  // Main read method - handles null checking and type info
+  static MyExt read(ReadContext &ctx, RefMode ref_mode, bool read_type) {
+    bool has_value = read_null_only_flag(ctx, ref_mode);
+    if (ctx.has_error() || !has_value) {
+      return MyExt{};
+    }
+    if (read_type) {
+      const TypeInfo *type_info = ctx.read_any_typeinfo(ctx.error());
+      if (ctx.has_error()) {
+        return MyExt{};
+      }
+      if (!type_info) {
+        ctx.set_error(Error::type_error("TypeInfo for MyExt not found"));
+        return MyExt{};
+      }
+    }
+    return read_data(ctx);
+  }
+
+  // Read only the data (no type info)
+  static MyExt read_data(ReadContext &ctx) {
+    MyExt value;
+    value.id = Serializer<int32_t>::read_data(ctx);
+    return value;
+  }
+
+  // Read data with generics support
+  static MyExt read_data_generic(ReadContext &ctx, bool has_generics) {
+    (void)has_generics;
+    return read_data(ctx);
+  }
+
+  // Read with pre-resolved type info
+  static MyExt read_with_type_info(ReadContext &ctx, RefMode ref_mode,
+                                   const TypeInfo &type_info) {
+    (void)type_info;
+    return read(ctx, ref_mode, false);
+  }
+};
+
+} // namespace serialization
+} // namespace fory
+```
+
+## Required Methods
+
+A custom serializer must implement these static methods:
+
+| Method                | Purpose                                         |
+| --------------------- | ----------------------------------------------- |
+| `write`               | Main serialization entry point with type info   |
+| `write_data`          | Serialize data only (no type info)              |
+| `write_data_generic`  | Serialize data with generics support            |
+| `read`                | Main deserialization entry point with type info |
+| `read_data`           | Deserialize data only (no type info)            |
+| `read_data_generic`   | Deserialize data with generics support          |
+| `read_with_type_info` | Deserialize with pre-resolved TypeInfo          |
+
+The `type_id` constant should be set to `TypeId::EXT` for custom extension 
types.
+
+## Registering Custom Serializers
+
+Register your custom serializer with Fory before use:
+
+```cpp
+auto fory = Fory::builder().xlang(true).build();
+
+// Register with numeric type ID (must match across languages)
+auto result = fory.register_extension_type<MyExt>(103);
+if (!result.ok()) {
+  std::cerr << "Failed to register: " << result.error().to_string() << 
std::endl;
+}
+
+// Or register with type name for named type systems
+fory.register_extension_type<MyExt>("my_ext");
+
+// Or with namespace and type name
+fory.register_extension_type<MyExt>("com.example", "MyExt");
+```
+
+## Complete Example
+
+```cpp
+#include "fory/serialization/fory.h"
+#include <iostream>
+
+using namespace fory::serialization;
+
+struct CustomType {
+  int32_t value;
+  std::string name;
+
+  bool operator==(const CustomType &other) const {
+    return value == other.value && name == other.name;
+  }
+};
+
+namespace fory {
+namespace serialization {
+
+template <>
+struct Serializer<CustomType> {
+  static constexpr TypeId type_id = TypeId::EXT;
+
+  static void write(const CustomType &value, WriteContext &ctx,
+                    RefMode ref_mode, bool write_type, bool has_generics = 
false) {
+    (void)has_generics;
+    write_not_null_ref_flag(ctx, ref_mode);
+    if (write_type) {
+      auto result = ctx.write_any_typeinfo(
+          static_cast<uint32_t>(TypeId::UNKNOWN),
+          std::type_index(typeid(CustomType)));
+      if (!result.ok()) {
+        ctx.set_error(std::move(result).error());
+        return;
+      }
+    }
+    write_data(value, ctx);
+  }
+
+  static void write_data(const CustomType &value, WriteContext &ctx) {
+    // Write value as varint for compact encoding
+    Serializer<int32_t>::write_data(value.value, ctx);
+    // Delegate string serialization to built-in serializer
+    Serializer<std::string>::write_data(value.name, ctx);
+  }
+
+  static void write_data_generic(const CustomType &value, WriteContext &ctx,
+                                 bool has_generics) {
+    (void)has_generics;
+    write_data(value, ctx);
+  }
+
+  static CustomType read(ReadContext &ctx, RefMode ref_mode, bool read_type) {
+    bool has_value = read_null_only_flag(ctx, ref_mode);
+    if (ctx.has_error() || !has_value) {
+      return CustomType{};
+    }
+    if (read_type) {
+      const TypeInfo *type_info = ctx.read_any_typeinfo(ctx.error());
+      if (ctx.has_error()) {
+        return CustomType{};
+      }
+      if (!type_info) {
+        ctx.set_error(Error::type_error("TypeInfo for CustomType not found"));
+        return CustomType{};
+      }
+    }
+    return read_data(ctx);
+  }
+
+  static CustomType read_data(ReadContext &ctx) {
+    CustomType value;
+    value.value = Serializer<int32_t>::read_data(ctx);
+    value.name = Serializer<std::string>::read_data(ctx);
+    return value;
+  }
+
+  static CustomType read_data_generic(ReadContext &ctx, bool has_generics) {
+    (void)has_generics;
+    return read_data(ctx);
+  }
+
+  static CustomType read_with_type_info(ReadContext &ctx, RefMode ref_mode,
+                                        const TypeInfo &type_info) {
+    (void)type_info;
+    return read(ctx, ref_mode, false);
+  }
+};
+
+} // namespace serialization
+} // namespace fory
+
+int main() {
+  auto fory = Fory::builder().xlang(true).build();
+  fory.register_extension_type<CustomType>(100);
+
+  CustomType original{42, "test"};
+
+  auto serialized = fory.serialize(original);
+  if (!serialized.ok()) {
+    std::cerr << "Serialization failed" << std::endl;
+    return 1;
+  }
+
+  auto deserialized = fory.deserialize<CustomType>(serialized.value());
+  if (!deserialized.ok()) {
+    std::cerr << "Deserialization failed" << std::endl;
+    return 1;
+  }
+
+  assert(original == deserialized.value());
+  std::cout << "Custom serializer works!" << std::endl;
+  return 0;
+}
+```
+
+## WriteContext Methods
+
+The `WriteContext` provides methods for writing data:
+
+```cpp
+// Primitive types
+ctx.write_uint8(value);
+ctx.write_int8(value);
+ctx.write_uint16(value);
+
+// Variable-length integers (compact encoding)
+ctx.write_varuint32(value);   // Unsigned varint
+ctx.write_varint32(value);    // Signed zigzag varint
+ctx.write_varuint64(value);   // Unsigned varint
+ctx.write_varint64(value);    // Signed zigzag varint
+
+// Tagged integers (for mixed-size encoding)
+ctx.write_tagged_uint64(value);
+ctx.write_tagged_int64(value);
+
+// Raw bytes
+ctx.write_bytes(data_ptr, length);
+
+// Access underlying buffer for advanced operations
+ctx.buffer().WriteInt32(value);
+ctx.buffer().WriteFloat(value);
+ctx.buffer().WriteDouble(value);
+```
+
+## ReadContext Methods
+
+The `ReadContext` provides methods for reading data:
+
+```cpp
+// Primitive types (use error reference pattern)
+uint8_t u8 = ctx.read_uint8(ctx.error());
+int8_t i8 = ctx.read_int8(ctx.error());
+
+// Variable-length integers
+uint32_t u32 = ctx.read_varuint32(ctx.error());
+int32_t i32 = ctx.read_varint32(ctx.error());
+uint64_t u64 = ctx.read_varuint64(ctx.error());
+int64_t i64 = ctx.read_varint64(ctx.error());
+
+// Check for errors after read operations
+if (ctx.has_error()) {
+  return MyType{};  // Return default on error
+}
+
+// Access underlying buffer for advanced operations
+int32_t value = ctx.buffer().ReadInt32(ctx.error());
+float f = ctx.buffer().ReadFloat(ctx.error());
+double d = ctx.buffer().ReadDouble(ctx.error());
+```
+
+## Delegating to Built-in Serializers
+
+Reuse existing serializers for nested types:
+
+```cpp
+static void write_data(const MyType &value, WriteContext &ctx) {
+  // Delegate to built-in serializers
+  Serializer<int32_t>::write_data(value.int_field, ctx);
+  Serializer<std::string>::write_data(value.string_field, ctx);
+  Serializer<std::vector<int32_t>>::write_data(value.vec_field, ctx);
+}
+
+static MyType read_data(ReadContext &ctx) {
+  MyType value;
+  value.int_field = Serializer<int32_t>::read_data(ctx);
+  value.string_field = Serializer<std::string>::read_data(ctx);
+  value.vec_field = Serializer<std::vector<int32_t>>::read_data(ctx);
+  return value;
+}
+```
+
+## Best Practices
+
+1. **Use variable-length encoding** for integers that may be small
+2. **Check errors after read operations** using `ctx.has_error()`
+3. **Return default values on error** to maintain consistent behavior
+4. **Delegate to built-in serializers** for standard types
+5. **Match type IDs across languages** for cross-language compatibility
+6. **Use `(void)param`** to suppress unused parameter warnings
+
+## Related Topics
+
+- [Type Registration](type-registration.md) - Registering serializers
+- [Basic Serialization](basic-serialization.md) - Using FORY_STRUCT macro
+- [Schema Evolution](schema-evolution.md) - Compatible mode
+- [Cross-Language](cross-language.md) - Cross-language serialization
diff --git a/docs/guide/cpp/field-configuration.md 
b/docs/guide/cpp/field-configuration.md
index a70b1ead8..2daf62499 100644
--- a/docs/guide/cpp/field-configuration.md
+++ b/docs/guide/cpp/field-configuration.md
@@ -527,6 +527,46 @@ FORY_FIELD_CONFIG(DataV2,
 | **Cross-lang compat**   | Limited               | Limited           | Full   
                   |
 | **Recommended for**     | Simple structs        | Third-party types | 
Complex/xlang structs     |
 
+## Xlang Mode Defaults
+
+C++ only supports xlang (cross-language) mode. The defaults are strict due to 
type system differences between languages:
+
+- **Nullable**: Fields are non-nullable by default
+- **Ref tracking**: Disabled by default (except `std::shared_ptr` which tracks 
refs by default)
+
+You **need to configure fields** when:
+
+- A field can be null (use `std::optional<T>` or mark with `nullable()`)
+- A field needs reference tracking for shared/circular objects (use `ref()`)
+- Integer types need specific encoding for cross-language compatibility
+- You want to reduce metadata size (use field IDs)
+
+```cpp
+// Xlang mode: explicit configuration required
+struct User {
+    std::string name;                              // Non-nullable by default
+    std::optional<std::string> email;              // Nullable (std::optional)
+    std::shared_ptr<User> friend_ptr;              // Ref tracking by default
+};
+
+FORY_STRUCT(User, name, email, friend_ptr);
+
+FORY_FIELD_CONFIG(User,
+    (name, fory::F(0)),
+    (email, fory::F(1)),                           // nullable implicit for 
optional
+    (friend_ptr, fory::F(2).nullable().ref())      // explicit nullable + ref
+);
+```
+
+### Default Values Summary
+
+| Type                 | Default Nullable | Default Ref Tracking |
+| -------------------- | ---------------- | -------------------- |
+| Primitives, `string` | `false`          | `false`              |
+| `std::optional<T>`   | `true`           | `false`              |
+| `std::shared_ptr<T>` | `false`          | `true`               |
+| `std::unique_ptr<T>` | `false`          | `false`              |
+
 ## Related Topics
 
 - [Type Registration](type-registration.md) - Registering types with 
FORY_STRUCT
diff --git a/docs/guide/go/struct-tags.md b/docs/guide/go/struct-tags.md
index 91617e1eb..25d09b42c 100644
--- a/docs/guide/go/struct-tags.md
+++ b/docs/guide/go/struct-tags.md
@@ -1,5 +1,5 @@
 ---
-title: Struct Tags
+title: Field Configuration
 sidebar_position: 60
 id: struct_tags
 license: |
@@ -282,6 +282,37 @@ err := f.RegisterStruct(BadStruct{}, 1)
 // Error: ErrKindInvalidTag
 ```
 
+## Xlang Mode Defaults
+
+Go only supports xlang (cross-language) mode. The defaults are strict due to 
type system differences between languages:
+
+- **Nullable**: Fields are non-nullable by default
+- **Ref tracking**: Disabled by default (`ref` tag not set)
+
+You **need to configure fields** when:
+
+- A field can be nil (use pointer types like `*string`, `*int32`)
+- A field needs reference tracking for shared/circular objects (use 
`fory:"ref"`)
+- You want to reduce metadata size (use field IDs with `fory:"id=N"`)
+
+```go
+// Xlang mode: explicit configuration required
+type User struct {
+    ID    int64   `fory:"id=0"`
+    Name  string  `fory:"id=1"`
+    Email *string `fory:"id=2"`           // Pointer type for nullable
+    Friend *User  `fory:"id=3,ref"`       // Must declare ref for shared 
objects
+}
+```
+
+### Default Values Summary
+
+| Option     | Default | How to Enable            |
+| ---------- | ------- | ------------------------ |
+| `nullable` | `false` | Use pointer types (`*T`) |
+| `ref`      | `false` | Add `fory:"ref"` tag     |
+| `id`       | `-1`    | Add `fory:"id=N"` tag    |
+
 ## Best Practices
 
 1. **Use `-` for sensitive data**: Passwords, tokens, internal state
diff --git a/docs/guide/java/field-configuration.md 
b/docs/guide/java/field-configuration.md
new file mode 100644
index 000000000..20a82c197
--- /dev/null
+++ b/docs/guide/java/field-configuration.md
@@ -0,0 +1,605 @@
+---
+title: Field Configuration
+sidebar_position: 5
+id: field_configuration
+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.
+---
+
+This page explains how to configure field-level metadata for serialization in 
Java.
+
+## Overview
+
+Apache Foryβ„’ provides field-level configuration through annotations:
+
+- **`@ForyField`**: Configure field metadata (id, nullable, ref, dynamic)
+- **`@Ignore`**: Exclude fields from serialization
+- **Integer type annotations**: Control integer encoding (varint, fixed, 
tagged, unsigned)
+
+This enables:
+
+- **Tag IDs**: Assign compact numeric IDs to reduce struct field meta size 
overhead for compatible mode
+- **Nullability**: Control whether fields can be null
+- **Reference Tracking**: Enable reference tracking for shared objects
+- **Field Skipping**: Exclude fields from serialization
+- **Encoding Control**: Specify how integers are encoded
+- **Polymorphism Control**: Control type info writing for struct fields
+
+## Basic Syntax
+
+Use annotations on fields:
+
+```java
+import org.apache.fory.annotation.ForyField;
+
+public class Person {
+    @ForyField(id = 0)
+    private String name;
+
+    @ForyField(id = 1)
+    private int age;
+
+    @ForyField(id = 2, nullable = true)
+    private String nickname;
+}
+```
+
+## The `@ForyField` Annotation
+
+Use `@ForyField` to configure field-level metadata:
+
+```java
+public class User {
+    @ForyField(id = 0)
+    private long id;
+
+    @ForyField(id = 1)
+    private String name;
+
+    @ForyField(id = 2, nullable = true)
+    private String email;
+
+    @ForyField(id = 3, ref = true)
+    private List<User> friends;
+
+    @ForyField(id = 4, dynamic = ForyField.Dynamic.TRUE)
+    private Object data;
+}
+```
+
+### Parameters
+
+| Parameter  | Type      | Default | Description                            |
+| ---------- | --------- | ------- | -------------------------------------- |
+| `id`       | `int`     | `-1`    | Field tag ID (-1 = use field name)     |
+| `nullable` | `boolean` | `false` | Whether the field can be null          |
+| `ref`      | `boolean` | `false` | Enable reference tracking              |
+| `dynamic`  | `Dynamic` | `AUTO`  | Control polymorphism for struct fields |
+
+## Field ID (`id`)
+
+Assigns a numeric ID to a field to minimize struct field meta size overhead 
for compatible mode:
+
+```java
+public class User {
+    @ForyField(id = 0)
+    private long id;
+
+    @ForyField(id = 1)
+    private String name;
+
+    @ForyField(id = 2)
+    private int age;
+}
+```
+
+**Benefits**:
+
+- Smaller serialized size (numeric IDs vs field names in metadata)
+- Reduced struct field meta overhead
+- Allows renaming fields without breaking binary compatibility
+
+**Recommendation**: It is recommended to configure field IDs for compatible 
mode since it reduces serialization cost.
+
+**Notes**:
+
+- IDs must be unique within a class
+- IDs must be >= 0 (use -1 to use field name encoding, which is the default)
+- If not specified, field name is used in metadata (larger overhead)
+
+**Without field IDs** (field names used in metadata):
+
+```java
+public class User {
+    private long id;
+    private String name;
+}
+```
+
+## Nullable Fields (`nullable`)
+
+Use `nullable = true` for fields that can be `null`:
+
+```java
+public class Record {
+    // Nullable string field
+    @ForyField(id = 0, nullable = true)
+    private String optionalName;
+
+    // Nullable Integer field (boxed type)
+    @ForyField(id = 1, nullable = true)
+    private Integer optionalCount;
+
+    // Non-nullable field (default)
+    @ForyField(id = 2)
+    private String requiredName;
+}
+```
+
+**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`
+
+## Reference Tracking (`ref`)
+
+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)
+    private RefInner inner1;
+
+    @ForyField(id = 1, ref = true, nullable = true)
+    private RefInner inner2;
+}
+
+public class CircularRef {
+    @ForyField(id = 0)
+    private String name;
+
+    // Self-referencing field for circular references
+    @ForyField(id = 1, ref = true, nullable = true)
+    private CircularRef selfRef;
+}
+```
+
+**Use Cases**:
+
+- Enable for fields that may be circular or shared
+- When the same object is referenced from multiple fields
+
+**Notes**:
+
+- Default is `ref = false` (no reference tracking)
+- When `ref = false`, avoids IdentityMap overhead and skips ref tracking flag
+- Reference tracking only takes effect when global ref tracking is enabled
+
+## Dynamic (Polymorphism Control)
+
+Controls polymorphism behavior for struct fields in cross-language 
serialization:
+
+```java
+public class Container {
+    // AUTO: Interface/abstract types are dynamic, concrete types are not
+    @ForyField(id = 0, dynamic = ForyField.Dynamic.AUTO)
+    private Animal animal;  // Interface - type info written
+
+    // FALSE: No type info written, uses declared type's serializer
+    @ForyField(id = 1, dynamic = ForyField.Dynamic.FALSE)
+    private Dog dog;  // Concrete - no type info
+
+    // TRUE: Type info written to support runtime subtypes
+    @ForyField(id = 2, dynamic = ForyField.Dynamic.TRUE)
+    private Object data;  // Force polymorphic
+}
+```
+
+**Options**:
+
+| Value   | Description                                                        
 |
+| ------- | 
------------------------------------------------------------------- |
+| `AUTO`  | Auto-detect: interface/abstract are dynamic, concrete types are 
not |
+| `FALSE` | No type info written, uses declared type's serializer directly     
 |
+| `TRUE`  | Type info written to support subtypes at runtime                   
 |
+
+## Skipping Fields
+
+### Using `@Ignore`
+
+Exclude fields from serialization:
+
+```java
+import org.apache.fory.annotation.Ignore;
+
+public class User {
+    @ForyField(id = 0)
+    private long id;
+
+    @ForyField(id = 1)
+    private String name;
+
+    @Ignore
+    private String password;  // Not serialized
+
+    @Ignore
+    private Object internalState;  // Not serialized
+}
+```
+
+### Using `transient`
+
+Java's `transient` keyword also excludes fields:
+
+```java
+public class User {
+    @ForyField(id = 0)
+    private long id;
+
+    private transient String password;  // Not serialized
+    private transient Object cache;     // Not serialized
+}
+```
+
+## Integer Type Annotations
+
+Fory provides annotations to control integer encoding for cross-language 
compatibility.
+
+### Signed 32-bit Integer (`@Int32Type`)
+
+```java
+import org.apache.fory.annotation.Int32Type;
+
+public class MyStruct {
+    // Variable-length encoding (default) - compact for small values
+    @Int32Type(compress = true)
+    private int compactId;
+
+    // Fixed 4-byte encoding - consistent size
+    @Int32Type(compress = false)
+    private int fixedId;
+}
+```
+
+### Signed 64-bit Integer (`@Int64Type`)
+
+```java
+import org.apache.fory.annotation.Int64Type;
+import org.apache.fory.config.LongEncoding;
+
+public class MyStruct {
+    // Variable-length encoding (default)
+    @Int64Type(encoding = LongEncoding.VARINT)
+    private long compactId;
+
+    // Fixed 8-byte encoding
+    @Int64Type(encoding = LongEncoding.FIXED)
+    private long fixedTimestamp;
+
+    // Tagged encoding (4 bytes for small values, 9 bytes otherwise)
+    @Int64Type(encoding = LongEncoding.TAGGED)
+    private long taggedValue;
+}
+```
+
+### Unsigned Integers
+
+```java
+import org.apache.fory.annotation.Uint8Type;
+import org.apache.fory.annotation.Uint16Type;
+import org.apache.fory.annotation.Uint32Type;
+import org.apache.fory.annotation.Uint64Type;
+import org.apache.fory.config.LongEncoding;
+
+public class UnsignedStruct {
+    // Unsigned 8-bit [0, 255]
+    @Uint8Type
+    private short flags;
+
+    // Unsigned 16-bit [0, 65535]
+    @Uint16Type
+    private int port;
+
+    // Unsigned 32-bit with varint encoding (default)
+    @Uint32Type(compress = true)
+    private long compactCount;
+
+    // Unsigned 32-bit with fixed encoding
+    @Uint32Type(compress = false)
+    private long fixedCount;
+
+    // Unsigned 64-bit with various encodings
+    @Uint64Type(encoding = LongEncoding.VARINT)
+    private long varintU64;
+
+    @Uint64Type(encoding = LongEncoding.FIXED)
+    private long fixedU64;
+
+    @Uint64Type(encoding = LongEncoding.TAGGED)
+    private long taggedU64;
+}
+```
+
+### Encoding Summary
+
+| Annotation                       | Type ID | Encoding | Size         |
+| -------------------------------- | ------- | -------- | ------------ |
+| `@Int32Type(compress = true)`    | 5       | varint   | 1-5 bytes    |
+| `@Int32Type(compress = false)`   | 4       | fixed    | 4 bytes      |
+| `@Int64Type(encoding = VARINT)`  | 7       | varint   | 1-10 bytes   |
+| `@Int64Type(encoding = FIXED)`   | 6       | fixed    | 8 bytes      |
+| `@Int64Type(encoding = TAGGED)`  | 8       | tagged   | 4 or 9 bytes |
+| `@Uint8Type`                     | 9       | fixed    | 1 byte       |
+| `@Uint16Type`                    | 10      | fixed    | 2 bytes      |
+| `@Uint32Type(compress = true)`   | 12      | varint   | 1-5 bytes    |
+| `@Uint32Type(compress = false)`  | 11      | fixed    | 4 bytes      |
+| `@Uint64Type(encoding = VARINT)` | 14      | varint   | 1-10 bytes   |
+| `@Uint64Type(encoding = FIXED)`  | 13      | fixed    | 8 bytes      |
+| `@Uint64Type(encoding = TAGGED)` | 15      | tagged   | 4 or 9 bytes |
+
+**When to Use**:
+
+- `varint`: Best for values that are often small (default)
+- `fixed`: Best for values that use full range (e.g., timestamps, hashes)
+- `tagged`: Good balance between size and performance
+- Unsigned types: For cross-language compatibility with Rust, Go, C++
+
+## Complete Example
+
+```java
+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.Uint64Type;
+import org.apache.fory.config.LongEncoding;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class Document {
+    // Fields with tag IDs (recommended for compatible mode)
+    @ForyField(id = 0)
+    private String title;
+
+    @ForyField(id = 1)
+    private int version;
+
+    // Nullable field
+    @ForyField(id = 2, nullable = true)
+    private String description;
+
+    // Collection fields
+    @ForyField(id = 3)
+    private List<String> tags;
+
+    @ForyField(id = 4)
+    private Map<String, String> metadata;
+
+    @ForyField(id = 5)
+    private Set<String> categories;
+
+    // Integer with different encodings
+    @ForyField(id = 6)
+    @Uint64Type(encoding = LongEncoding.VARINT)
+    private long viewCount;  // varint encoding
+
+    @ForyField(id = 7)
+    @Uint64Type(encoding = LongEncoding.FIXED)
+    private long fileSize;   // fixed encoding
+
+    @ForyField(id = 8)
+    @Uint64Type(encoding = LongEncoding.TAGGED)
+    private long checksum;   // tagged encoding
+
+    // Reference-tracked field for shared/circular references
+    @ForyField(id = 9, ref = true, nullable = true)
+    private Document parent;
+
+    // Ignored field (not serialized)
+    private transient Object cache;
+
+    // Getters and setters...
+}
+
+// Usage
+public class Main {
+    public static void main(String[] args) {
+        Fory fory = Fory.builder()
+            .withXlang(true)
+            .withCompatible(true)
+            .withRefTracking(true)
+            .build();
+
+        fory.register(Document.class, 100);
+
+        Document doc = new Document();
+        doc.setTitle("My Document");
+        doc.setVersion(1);
+        doc.setDescription("A sample document");
+
+        // Serialize
+        byte[] data = fory.serialize(doc);
+
+        // Deserialize
+        Document decoded = (Document) fory.deserialize(data);
+    }
+}
+```
+
+## Cross-Language Compatibility
+
+When serializing data to be read by other languages (Python, Rust, C++, Go), 
use field IDs and matching type annotations:
+
+```java
+public class CrossLangData {
+    // Use field IDs for cross-language compatibility
+    @ForyField(id = 0)
+    @Int32Type(compress = true)
+    private int intVar;
+
+    @ForyField(id = 1)
+    @Uint64Type(encoding = LongEncoding.FIXED)
+    private long longFixed;
+
+    @ForyField(id = 2)
+    @Uint64Type(encoding = LongEncoding.TAGGED)
+    private long longTagged;
+
+    @ForyField(id = 3, nullable = true)
+    private String optionalValue;
+}
+```
+
+## Schema Evolution
+
+Compatible mode supports schema evolution. It is recommended to configure 
field IDs to reduce serialization cost:
+
+```java
+// Version 1
+public class DataV1 {
+    @ForyField(id = 0)
+    private long id;
+
+    @ForyField(id = 1)
+    private String name;
+}
+
+// Version 2: Added new field
+public class DataV2 {
+    @ForyField(id = 0)
+    private long id;
+
+    @ForyField(id = 1)
+    private String name;
+
+    @ForyField(id = 2, nullable = true)
+    private String email;  // New field
+}
+```
+
+Data serialized with V1 can be deserialized with V2 (new field will be `null`).
+
+Alternatively, field IDs can be omitted (field names will be used in metadata 
with larger overhead):
+
+```java
+public class Data {
+    private long id;
+    private String name;
+}
+```
+
+## Native Mode vs Xlang Mode
+
+Field configuration behaves differently depending on the serialization mode:
+
+### Native Mode (Java-only)
+
+Native mode has **relaxed default values** for maximum compatibility:
+
+- **Nullable**: Reference types are nullable by default
+- **Ref tracking**: Enabled by default for object references (except `String`, 
boxed types, and time types)
+- **Polymorphism**: All non-final classes support polymorphism by default
+
+In native mode, you typically **don't need to configure field annotations** 
unless you want to:
+
+- Reduce serialized size by using field IDs
+- Optimize performance by disabling unnecessary ref tracking
+- Control integer encoding for specific fields
+
+```java
+// Native mode: works without any annotations
+public class User {
+    private long id;
+    private String name;
+    private List<String> tags;  // Nullable and ref-tracked by default
+}
+```
+
+### Xlang Mode (Cross-language)
+
+Xlang mode has **stricter default values** due to type system differences 
between languages:
+
+- **Nullable**: Fields are non-nullable by default (`nullable = false`)
+- **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 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)
+
+```java
+// Xlang mode: explicit configuration required for nullable/ref fields
+public class User {
+    @ForyField(id = 0)
+    private long id;
+
+    @ForyField(id = 1)
+    private String name;
+
+    @ForyField(id = 2, nullable = true)  // Must declare nullable
+    private String email;
+
+    @ForyField(id = 3, ref = true, nullable = true)  // Must declare ref for 
shared objects
+    private User friend;
+}
+```
+
+### Default Values Summary
+
+| Option     | Native Mode Default      | Xlang Mode Default                |
+| ---------- | ------------------------ | --------------------------------- |
+| `nullable` | `true` (reference types) | `false`                           |
+| `ref`      | `true`                   | `false`                           |
+| `dynamic`  | `true` (non-final)       | `AUTO` (concrete types are final) |
+
+## 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
+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
+6. **Keep IDs stable**: Once assigned, don't change field IDs
+7. **Configure unsigned types for cross-language compatibility**: When 
interoperating with unsigned numbers in Rust, Go, C++
+
+## Annotations Reference
+
+| Annotation                    | Description                            |
+| ----------------------------- | -------------------------------------- |
+| `@ForyField(id = N)`          | Field tag ID to reduce metadata size   |
+| `@ForyField(nullable = true)` | Mark field as nullable                 |
+| `@ForyField(ref = true)`      | Enable reference tracking              |
+| `@ForyField(dynamic = ...)`   | Control polymorphism for struct fields |
+| `@Ignore`                     | Exclude field from serialization       |
+| `@Int32Type(compress = ...)`  | 32-bit signed integer encoding         |
+| `@Int64Type(encoding = ...)`  | 64-bit signed integer encoding         |
+| `@Uint8Type`                  | Unsigned 8-bit integer                 |
+| `@Uint16Type`                 | Unsigned 16-bit integer                |
+| `@Uint32Type(compress = ...)` | Unsigned 32-bit integer encoding       |
+| `@Uint64Type(encoding = ...)` | Unsigned 64-bit integer encoding       |
+
+## Related Topics
+
+- [Basic Serialization](basic-serialization.md) - Getting started with Fory 
serialization
+- [Schema Evolution](schema-evolution.md) - Compatible mode and schema 
evolution
+- [Cross-Language](cross-language.md) - Interoperability with Python, Rust, 
C++, Go
diff --git a/docs/guide/python/field-configuration.md 
b/docs/guide/python/field-configuration.md
new file mode 100644
index 000000000..487913306
--- /dev/null
+++ b/docs/guide/python/field-configuration.md
@@ -0,0 +1,492 @@
+---
+title: Field Configuration
+sidebar_position: 5
+id: field_configuration
+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.
+---
+
+This page explains how to configure field-level metadata for serialization in 
Python.
+
+## Overview
+
+Apache Foryβ„’ provides field-level configuration through:
+
+- **`pyfory.field()`**: Configure field metadata (id, nullable, ref, ignore, 
dynamic)
+- **Type annotations**: Control integer encoding (varint, fixed, tagged)
+- **`Optional[T]`**: Mark fields as nullable
+
+This enables:
+
+- **Tag IDs**: Assign compact numeric IDs to reduce struct field meta size 
overhead
+- **Nullability**: Control whether fields can be null
+- **Reference Tracking**: Enable reference tracking for shared objects
+- **Field Skipping**: Exclude fields from serialization
+- **Encoding Control**: Specify how integers are encoded (varint, fixed, 
tagged)
+- **Polymorphism**: Control whether type info is written for struct fields
+
+## Basic Syntax
+
+Use `@dataclass` decorator with type annotations and `pyfory.field()`:
+
+```python
+from dataclasses import dataclass
+from typing import Optional
+import pyfory
+
+@dataclass
+class Person:
+    name: str = pyfory.field(id=0)
+    age: pyfory.int32 = pyfory.field(id=1, default=0)
+    nickname: Optional[str] = pyfory.field(id=2, nullable=True, default=None)
+```
+
+## The `pyfory.field()` Function
+
+Use `pyfory.field()` to configure field-level metadata:
+
+```python
+@dataclass
+class User:
+    id: pyfory.int64 = pyfory.field(id=0, default=0)
+    name: str = pyfory.field(id=1, default="")
+    email: Optional[str] = pyfory.field(id=2, nullable=True, default=None)
+    friends: List["User"] = pyfory.field(id=3, ref=True, default_factory=list)
+    _cache: dict = pyfory.field(ignore=True, default_factory=dict)
+```
+
+### Parameters
+
+| Parameter         | Type     | Default   | Description                       
   |
+| ----------------- | -------- | --------- | 
------------------------------------ |
+| `id`              | `int`    | `-1`      | Field tag ID (-1 = use field 
name)   |
+| `nullable`        | `bool`   | `False`   | Whether the field can be null     
   |
+| `ref`             | `bool`   | `False`   | Enable reference tracking         
   |
+| `ignore`          | `bool`   | `False`   | Exclude field from serialization  
   |
+| `dynamic`         | `bool`   | `None`    | Control whether type info is 
written |
+| `default`         | Any      | `MISSING` | Default value for the field       
   |
+| `default_factory` | Callable | `MISSING` | Factory function for default 
value   |
+
+## Field ID (`id`)
+
+Assigns a numeric ID to a field to minimize struct field meta size overhead:
+
+```python
+@dataclass
+class User:
+    id: pyfory.int64 = pyfory.field(id=0, default=0)
+    name: str = pyfory.field(id=1, default="")
+    age: pyfory.int32 = pyfory.field(id=2, default=0)
+```
+
+**Benefits**:
+
+- Smaller serialized size (numeric IDs vs field names in metadata)
+- Reduced struct field meta overhead
+- Allows renaming fields without breaking binary compatibility
+
+**Recommendation**: It is recommended to configure field IDs for compatible 
mode since it reduces serialization cost.
+
+**Notes**:
+
+- IDs must be unique within a class
+- IDs must be >= 0 (use -1 to use field name encoding, which is the default)
+- If not specified, field name is used in metadata (larger overhead)
+
+**Without field IDs** (field names used in metadata):
+
+```python
+@dataclass
+class User:
+    id: pyfory.int64 = 0
+    name: str = ""
+```
+
+## Nullable Fields (`nullable`)
+
+Use `nullable=True` for fields that can be `None`:
+
+```python
+from typing import Optional
+
+@dataclass
+class Record:
+    # Nullable string field
+    optional_name: Optional[str] = pyfory.field(id=0, nullable=True, 
default=None)
+
+    # Nullable integer field
+    optional_count: Optional[pyfory.int32] = pyfory.field(id=1, nullable=True, 
default=None)
+```
+
+**Notes**:
+
+- `Optional[T]` fields must have `nullable=True`
+- Non-optional fields default to `nullable=False`
+
+## Reference Tracking (`ref`)
+
+Enable reference tracking for fields that may be shared or circular:
+
+```python
+@dataclass
+class RefOuter:
+    # Both fields may point to the same inner object
+    inner1: Optional[RefInner] = pyfory.field(id=0, ref=True, nullable=True, 
default=None)
+    inner2: Optional[RefInner] = pyfory.field(id=1, ref=True, nullable=True, 
default=None)
+
+
+@dataclass
+class CircularRef:
+    name: str = pyfory.field(id=0, default="")
+    # Self-referencing field for circular references
+    self_ref: Optional["CircularRef"] = pyfory.field(id=1, ref=True, 
nullable=True, default=None)
+```
+
+**Use Cases**:
+
+- Enable for fields that may be circular or shared
+- When the same object is referenced from multiple fields
+
+**Notes**:
+
+- Reference tracking only takes effect when `Fory(ref=True)` is set globally
+- Field-level `ref=True` AND global `ref=True` must both be enabled
+
+## Skipping Fields (`ignore`)
+
+Exclude fields from serialization:
+
+```python
+@dataclass
+class User:
+    id: pyfory.int64 = pyfory.field(id=0, default=0)
+    name: str = pyfory.field(id=1, default="")
+    # Not serialized
+    _cache: dict = pyfory.field(ignore=True, default_factory=dict)
+    _internal_state: str = pyfory.field(ignore=True, default="")
+```
+
+## Dynamic Fields (`dynamic`)
+
+Control whether type information is written for struct fields. This is 
essential for polymorphism support:
+
+```python
+from abc import ABC, abstractmethod
+
+class Shape(ABC):
+    @abstractmethod
+    def area(self) -> float:
+        pass
+
+@dataclass
+class Circle(Shape):
+    radius: float = 0.0
+
+    def area(self) -> float:
+        return 3.14159 * self.radius * self.radius
+
+@dataclass
+class Container:
+    # Abstract class: dynamic is always True (type info written)
+    shape: Shape = pyfory.field(id=0)
+
+    # Force type info for concrete type (support runtime subtypes)
+    circle: Circle = pyfory.field(id=1, dynamic=True)
+
+    # Skip type info for concrete type (use declared type directly)
+    fixed_circle: Circle = pyfory.field(id=2, dynamic=False)
+```
+
+**Default Behavior**:
+
+| Mode        | Abstract Class | Concrete Object Types | Numeric/str/time 
Types |
+| ----------- | -------------- | --------------------- | 
---------------------- |
+| Native mode | `True`         | `True`                | `False`               
 |
+| Xlang mode  | `True`         | `False`               | `False`               
 |
+
+**Notes**:
+
+- **Abstract classes**: `dynamic` is always `True` (type info must be written)
+- **Native mode**: `dynamic` defaults to `True` for object types, `False` for 
numeric/str/time types
+- **Xlang mode**: `dynamic` defaults to `False` for concrete types
+- Use `dynamic=True` when a concrete field may hold subclass instances
+- Use `dynamic=False` for performance optimization when type is known
+
+## Integer Type Annotations
+
+Fory provides type annotations to control integer encoding:
+
+### Signed Integers
+
+```python
+@dataclass
+class SignedIntegers:
+    byte_val: pyfory.int8 = 0      # 8-bit signed
+    short_val: pyfory.int16 = 0    # 16-bit signed
+    int_val: pyfory.int32 = 0      # 32-bit signed (varint encoding)
+    long_val: pyfory.int64 = 0     # 64-bit signed (varint encoding)
+```
+
+### Unsigned Integers
+
+```python
+@dataclass
+class UnsignedIntegers:
+    # Fixed-size encoding
+    u8_val: pyfory.uint8 = 0       # 8-bit unsigned (fixed)
+    u16_val: pyfory.uint16 = 0     # 16-bit unsigned (fixed)
+
+    # Variable-length encoding (default for u32/u64)
+    u32_var: pyfory.uint32 = 0     # 32-bit unsigned (varint)
+    u64_var: pyfory.uint64 = 0     # 64-bit unsigned (varint)
+
+    # Explicit fixed-size encoding
+    u32_fixed: pyfory.fixed_uint32 = 0   # 32-bit unsigned (fixed 4 bytes)
+    u64_fixed: pyfory.fixed_uint64 = 0   # 64-bit unsigned (fixed 8 bytes)
+
+    # Tagged encoding (includes type tag)
+    u64_tagged: pyfory.tagged_uint64 = 0  # 64-bit unsigned (tagged)
+```
+
+### Floating Point
+
+```python
+@dataclass
+class FloatingPoint:
+    float_val: pyfory.float32 = 0.0   # 32-bit float
+    double_val: pyfory.float64 = 0.0  # 64-bit double
+```
+
+### Encoding Summary
+
+| Type                   | Encoding | Size       |
+| ---------------------- | -------- | ---------- |
+| `pyfory.int8`          | fixed    | 1 byte     |
+| `pyfory.int16`         | fixed    | 2 bytes    |
+| `pyfory.int32`         | varint   | 1-5 bytes  |
+| `pyfory.int64`         | varint   | 1-10 bytes |
+| `pyfory.uint8`         | fixed    | 1 byte     |
+| `pyfory.uint16`        | fixed    | 2 bytes    |
+| `pyfory.uint32`        | varint   | 1-5 bytes  |
+| `pyfory.uint64`        | varint   | 1-10 bytes |
+| `pyfory.fixed_uint32`  | fixed    | 4 bytes    |
+| `pyfory.fixed_uint64`  | fixed    | 8 bytes    |
+| `pyfory.tagged_uint64` | tagged   | 1-9 bytes  |
+| `pyfory.float32`       | fixed    | 4 bytes    |
+| `pyfory.float64`       | fixed    | 8 bytes    |
+
+**When to Use**:
+
+- `varint`: Best for values that are often small (default for 
int32/int64/uint32/uint64)
+- `fixed`: Best for values that use full range (e.g., timestamps, hashes)
+- `tagged`: When type information needs to be preserved (uint64 only)
+
+## Complete Example
+
+```python
+from dataclasses import dataclass
+from typing import Optional, List, Dict, Set
+import pyfory
+
+
+@dataclass
+class Document:
+    # Fields with tag IDs (recommended for compatible mode)
+    title: str = pyfory.field(id=0, default="")
+    version: pyfory.int32 = pyfory.field(id=1, default=0)
+
+    # Nullable field
+    description: Optional[str] = pyfory.field(id=2, nullable=True, 
default=None)
+
+    # Collection fields
+    tags: List[str] = pyfory.field(id=3, default_factory=list)
+    metadata: Dict[str, str] = pyfory.field(id=4, default_factory=dict)
+    categories: Set[str] = pyfory.field(id=5, default_factory=set)
+
+    # Unsigned integers with different encodings
+    view_count: pyfory.uint64 = pyfory.field(id=6, default=0)           # 
varint encoding
+    file_size: pyfory.fixed_uint64 = pyfory.field(id=7, default=0)      # 
fixed encoding
+    checksum: pyfory.tagged_uint64 = pyfory.field(id=8, default=0)      # 
tagged encoding
+
+    # Reference-tracked field for shared/circular references
+    parent: Optional["Document"] = pyfory.field(id=9, ref=True, nullable=True, 
default=None)
+
+    # Ignored field (not serialized)
+    _cache: dict = pyfory.field(ignore=True, default_factory=dict)
+
+
+def main():
+    fory = pyfory.Fory(xlang=True, compatible=True, ref=True)
+    fory.register_type(Document, type_id=100)
+
+    doc = Document(
+        title="My Document",
+        version=1,
+        description="A sample document",
+        tags=["tag1", "tag2"],
+        metadata={"key": "value"},
+        categories={"cat1"},
+        view_count=42,
+        file_size=1024,
+        checksum=123456789,
+        parent=None,
+    )
+
+    # Serialize
+    data = fory.serialize(doc)
+
+    # Deserialize
+    decoded = fory.deserialize(data)
+    assert decoded.title == doc.title
+    assert decoded.version == doc.version
+
+
+if __name__ == "__main__":
+    main()
+```
+
+## Cross-Language Compatibility
+
+When serializing data to be read by other languages (Java, Rust, C++, Go), use 
field IDs and matching type annotations:
+
+```python
+@dataclass
+class CrossLangData:
+    # Use field IDs for cross-language compatibility
+    int_var: pyfory.int32 = pyfory.field(id=0, default=0)
+    long_fixed: pyfory.fixed_uint64 = pyfory.field(id=1, default=0)
+    long_tagged: pyfory.tagged_uint64 = pyfory.field(id=2, default=0)
+    optional_value: Optional[str] = pyfory.field(id=3, nullable=True, 
default=None)
+```
+
+## Schema Evolution
+
+Compatible mode supports schema evolution. It is recommended to configure 
field IDs to reduce serialization cost:
+
+```python
+# Version 1
+@dataclass
+class DataV1:
+    id: pyfory.int64 = pyfory.field(id=0, default=0)
+    name: str = pyfory.field(id=1, default="")
+
+
+# Version 2: Added new field
+@dataclass
+class DataV2:
+    id: pyfory.int64 = pyfory.field(id=0, default=0)
+    name: str = pyfory.field(id=1, default="")
+    email: Optional[str] = pyfory.field(id=2, nullable=True, default=None)  # 
New field
+```
+
+Data serialized with V1 can be deserialized with V2 (new field will be `None`).
+
+Alternatively, field IDs can be omitted (field names will be used in metadata 
with larger overhead):
+
+```python
+@dataclass
+class Data:
+    id: pyfory.int64 = 0
+    name: str = ""
+```
+
+## Native Mode vs Xlang Mode
+
+Field configuration behaves differently depending on the serialization mode:
+
+### Native Mode (Python-only)
+
+Native mode has **relaxed default values** for maximum compatibility:
+
+- **Nullable**: `str` and numeric types are non-nullable by default unless 
`Optional` is used
+- **Ref tracking**: Enabled by default for object references (except `str` and 
numeric types)
+
+In native mode, you typically **don't need to configure field annotations** 
unless you want to:
+
+- Reduce serialized size by using field IDs
+- Optimize performance by disabling unnecessary ref tracking
+
+```python
+# Native mode: works without field configuration
+@dataclass
+class User:
+    id: int = 0
+    name: str = ""
+    tags: List[str] = None
+```
+
+### Xlang Mode (Cross-language)
+
+Xlang mode has **stricter default values** due to type system differences 
between languages:
+
+- **Nullable**: Fields are non-nullable by default (`nullable=False`)
+- **Ref tracking**: Disabled by default (`ref=False`)
+
+In xlang mode, you **need to configure fields** when:
+
+- A field can be None (use `Optional[T]` with `nullable=True`)
+- 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)
+
+```python
+# Xlang mode: explicit configuration required for nullable/ref fields
+@dataclass
+class User:
+    id: pyfory.int64 = pyfory.field(id=0, default=0)
+    name: str = pyfory.field(id=1, default="")
+    email: Optional[str] = pyfory.field(id=2, nullable=True, default=None)  # 
Must declare nullable
+    friend: Optional["User"] = pyfory.field(id=3, ref=True, nullable=True, 
default=None)  # Must declare ref
+```
+
+### Default Values Summary
+
+| Option     | Native Mode Default                                   | Xlang 
Mode Default |
+| ---------- | ----------------------------------------------------- | 
------------------ |
+| `nullable` | `False` for `str`/numeric; others nullable by default | `False` 
           |
+| `ref`      | `True` (except `str` and numeric types)               | `False` 
           |
+| `dynamic`  | `True` (except numeric/str/time types)                | `False` 
(concrete) |
+
+## Best Practices
+
+1. **Configure field IDs**: Recommended for compatible mode to reduce 
serialization cost
+2. **Use `Optional[T]` with `nullable=True`**: Required for nullable fields in 
xlang mode
+3. **Enable ref tracking for shared objects**: Use `ref=True` when objects are 
shared or circular
+4. **Use `ignore=True` for sensitive data**: Passwords, tokens, internal state
+5. **Choose appropriate encoding**: `varint` for small values, `fixed` for 
full-range values
+6. **Keep IDs stable**: Once assigned, don't change field IDs
+
+## Options Reference
+
+| Configuration                                | Description                   
       |
+| -------------------------------------------- | 
------------------------------------ |
+| `pyfory.field(id=N)`                         | Field tag ID to reduce 
metadata size |
+| `pyfory.field(nullable=True)`                | Mark field as nullable        
       |
+| `pyfory.field(ref=True)`                     | Enable reference tracking     
       |
+| `pyfory.field(ignore=True)`                  | Exclude field from 
serialization     |
+| `pyfory.field(dynamic=True)`                 | Force type info to be written 
       |
+| `pyfory.field(dynamic=False)`                | Skip type info (use declared 
type)   |
+| `Optional[T]`                                | Type hint for nullable fields 
       |
+| `pyfory.int32`, `pyfory.int64`               | Signed integers (varint 
encoding)    |
+| `pyfory.uint32`, `pyfory.uint64`             | Unsigned integers (varint 
encoding)  |
+| `pyfory.fixed_uint32`, `pyfory.fixed_uint64` | Fixed-size unsigned           
       |
+| `pyfory.tagged_uint64`                       | Tagged encoding for uint64    
       |
+
+## Related Topics
+
+- [Basic Serialization](basic-serialization.md) - Getting started with Fory 
serialization
+- [Schema Evolution](schema-evolution.md) - Compatible mode and schema 
evolution
+- [Cross-Language](cross-language.md) - Interoperability with Java, Rust, C++, 
Go
diff --git a/docs/guide/python/numpy-integration.md 
b/docs/guide/python/numpy-integration.md
index 3f3b98926..ea20fd893 100644
--- a/docs/guide/python/numpy-integration.md
+++ b/docs/guide/python/numpy-integration.md
@@ -1,5 +1,5 @@
 ---
-title: NumPy & Scientific Computing
+title: NumPy & Pandas
 sidebar_position: 8
 id: numpy_integration
 license: |
@@ -19,7 +19,7 @@ license: |
   limitations under the License.
 ---
 
-Fory natively supports numpy arrays with optimized serialization.
+Fory natively supports numpy arrays and pandas DataFrame with optimized 
serialization.
 
 ## NumPy Array Serialization
 
diff --git a/docs/guide/rust/field-configuration.md 
b/docs/guide/rust/field-configuration.md
new file mode 100644
index 000000000..235986579
--- /dev/null
+++ b/docs/guide/rust/field-configuration.md
@@ -0,0 +1,462 @@
+---
+title: Field Configuration
+sidebar_position: 5
+id: field_configuration
+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.
+---
+
+This page explains how to configure field-level metadata for serialization in 
Rust.
+
+## Overview
+
+Apache Foryβ„’ provides the `#[fory(...)]` attribute macro to specify optional 
field-level metadata at compile time. This enables:
+
+- **Tag IDs**: Assign compact numeric IDs to minimize struct field meta size 
overhead
+- **Nullability**: Control whether fields can be null
+- **Reference Tracking**: Enable reference tracking for shared ownership types
+- **Field Skipping**: Exclude fields from serialization
+- **Encoding Control**: Specify how integers are encoded (varint, fixed, 
tagged)
+
+## Basic Syntax
+
+The `#[fory(...)]` attribute is placed on individual struct fields:
+
+```rust
+use fory::Fory;
+
+#[derive(Fory)]
+struct Person {
+    #[fory(id = 0)]
+    name: String,
+
+    #[fory(id = 1)]
+    age: i32,
+
+    #[fory(id = 2, nullable)]
+    nickname: Option<String>,
+}
+```
+
+Multiple options are separated by commas.
+
+## Available Options
+
+### Field ID (`id = N`)
+
+Assigns a numeric ID to a field to minimize struct field meta size overhead:
+
+```rust
+#[derive(Fory)]
+struct User {
+    #[fory(id = 0)]
+    id: i64,
+
+    #[fory(id = 1)]
+    name: String,
+
+    #[fory(id = 2)]
+    age: i32,
+}
+```
+
+**Benefits**:
+
+- Smaller serialized size (numeric IDs vs field names in metadata)
+- Allows renaming fields without breaking binary compatibility
+
+**Recommendation**: It is recommended to configure field IDs for compatible 
mode since it reduces serialization cost.
+
+**Notes**:
+
+- IDs must be unique within a struct
+- IDs must be >= 0 (use -1 to explicitly opt-out of tag ID encoding)
+- If not specified, field name is used in metadata (larger overhead)
+
+### Skipping Fields (`skip`)
+
+Excludes a field from serialization:
+
+```rust
+#[derive(Fory)]
+struct User {
+    #[fory(id = 0)]
+    id: i64,
+
+    #[fory(id = 1)]
+    name: String,
+
+    #[fory(skip)]
+    password: String, // Not serialized
+}
+```
+
+The `password` field will not be included in serialized output and will remain 
at its default value after deserialization.
+
+### Nullable (`nullable`)
+
+Controls whether null flags are written for fields:
+
+```rust
+use fory::{Fory, RcWeak};
+
+#[derive(Fory)]
+struct Record {
+    // RcWeak is nullable by default, override to non-nullable
+    #[fory(id = 0, nullable = false)]
+    required_ref: RcWeak<Data>,
+}
+```
+
+**Default Behavior**:
+
+| Type                      | Default Nullable |
+| ------------------------- | ---------------- |
+| `Option<T>`               | `true`           |
+| `RcWeak<T>`, `ArcWeak<T>` | `true`           |
+| All other types           | `false`          |
+
+**Notes**:
+
+- For `Option<T>`, `RcWeak<T>`, `ArcWeak<T>`, nullable defaults to true
+- For all other types, nullable defaults to false
+- Use `nullable = false` to override defaults for types that are nullable by 
default
+
+### Reference Tracking (`ref`)
+
+Controls per-field reference tracking for shared ownership types:
+
+```rust
+use std::rc::Rc;
+use std::sync::Arc;
+
+#[derive(Fory)]
+struct Container {
+    // Enable reference tracking (default for Rc/Arc)
+    #[fory(id = 0, ref = true)]
+    shared_data: Rc<Data>,
+
+    // Disable reference tracking
+    #[fory(id = 1, ref = false)]
+    unique_data: Rc<Data>,
+}
+```
+
+**Default Behavior**:
+
+| Type                              | Default Ref Tracking |
+| --------------------------------- | -------------------- |
+| `Rc<T>`, `Arc<T>`                 | `true`               |
+| `RcWeak<T>`, `ArcWeak<T>`         | `true`               |
+| `Option<Rc<T>>`, `Option<Arc<T>>` | `true` (inherited)   |
+| All other types                   | `false`              |
+
+**Use Cases**:
+
+- Enable for fields that may be circular or shared
+- Disable for fields that are always unique (optimization)
+
+### Encoding (`encoding`)
+
+Controls how integer fields are encoded:
+
+```rust
+#[derive(Fory)]
+struct Metrics {
+    // Variable-length encoding (smaller for small values)
+    #[fory(id = 0, encoding = "varint")]
+    count: i64,
+
+    // Fixed-length encoding (consistent size)
+    #[fory(id = 1, encoding = "fixed")]
+    timestamp: i64,
+
+    // Tagged encoding (includes type tag, u64 only)
+    #[fory(id = 2, encoding = "tagged")]
+    value: u64,
+}
+```
+
+**Supported Encodings**:
+
+| Type         | Options                     | Default  |
+| ------------ | --------------------------- | -------- |
+| `i32`, `u32` | `varint`, `fixed`           | `varint` |
+| `i64`, `u64` | `varint`, `fixed`, `tagged` | `varint` |
+
+**When to Use**:
+
+- `varint`: Best for values that are often small (default)
+- `fixed`: Best for values that use full range (e.g., timestamps, hashes)
+- `tagged`: When type information needs to be preserved (u64 only)
+
+### Compress (`compress`)
+
+A convenience shorthand for controlling integer encoding:
+
+```rust
+#[derive(Fory)]
+struct Data {
+    // compress = true -> varint encoding (default)
+    #[fory(id = 0, compress)]
+    small_value: i32,
+
+    // compress = false -> fixed encoding
+    #[fory(id = 1, compress = false)]
+    fixed_value: u32,
+}
+```
+
+**Notes**:
+
+- `compress` or `compress = true` is equivalent to `encoding = "varint"`
+- `compress = false` is equivalent to `encoding = "fixed"`
+- If both `compress` and `encoding` are specified, they must not conflict
+
+## Type Classification
+
+Fory classifies field types to determine default behavior:
+
+| Type Class | Examples                       | Default Nullable | Default Ref 
|
+| ---------- | ------------------------------ | ---------------- | ----------- 
|
+| Primitive  | `i8`, `i32`, `f64`, `bool`     | `false`          | `false`     
|
+| Option     | `Option<T>`                    | `true`           | `false`     
|
+| Rc         | `Rc<T>`                        | `false`          | `true`      
|
+| Arc        | `Arc<T>`                       | `false`          | `true`      
|
+| RcWeak     | `RcWeak<T>` (fory type)        | `true`           | `true`      
|
+| ArcWeak    | `ArcWeak<T>` (fory type)       | `true`           | `true`      
|
+| Other      | `String`, `Vec<T>`, user types | `false`          | `false`     
|
+
+**Special Case**: `Option<Rc<T>>` and `Option<Arc<T>>` inherit the inner 
type's ref tracking behavior.
+
+## Complete Example
+
+```rust
+use fory::Fory;
+use std::rc::Rc;
+
+#[derive(Fory, Default)]
+struct Document {
+    // Required fields with tag IDs
+    #[fory(id = 0)]
+    title: String,
+
+    #[fory(id = 1)]
+    version: i32,
+
+    // Optional field (nullable by default for Option)
+    #[fory(id = 2)]
+    description: Option<String>,
+
+    // Reference-tracked shared pointer
+    #[fory(id = 3)]
+    parent: Rc<Document>,
+
+    // Nullable + reference-tracked
+    #[fory(id = 4, nullable)]
+    related: Option<Rc<Document>>,
+
+    // Counter with varint encoding (small values)
+    #[fory(id = 5, encoding = "varint")]
+    view_count: u64,
+
+    // Timestamp with fixed encoding (full range values)
+    #[fory(id = 6, encoding = "fixed")]
+    created_at: i64,
+
+    // Skip sensitive field
+    #[fory(skip)]
+    internal_state: String,
+}
+
+fn main() {
+    let fory = fory::Fory::default();
+
+    let doc = Document {
+        title: "My Document".to_string(),
+        version: 1,
+        description: Some("A sample document".to_string()),
+        parent: Rc::new(Document::default()),
+        related: None, // Allowed because nullable
+        view_count: 42,
+        created_at: 1704067200,
+        internal_state: "secret".to_string(), // Will be skipped
+    };
+
+    let bytes = fory.serialize(&doc);
+    let decoded: Document = fory.deserialize(&bytes).unwrap();
+}
+```
+
+## Compile-Time Validation
+
+Invalid configurations are caught at compile time:
+
+```rust
+// Error: duplicate field IDs
+#[derive(Fory)]
+struct Bad {
+    #[fory(id = 0)]
+    field1: String,
+
+    #[fory(id = 0)]  // Compile error: duplicate id
+    field2: String,
+}
+
+// Error: invalid id value
+#[derive(Fory)]
+struct Bad2 {
+    #[fory(id = -2)]  // Compile error: id must be >= -1
+    field: String,
+}
+
+// Error: conflicting encoding attributes
+#[derive(Fory)]
+struct Bad3 {
+    #[fory(compress = true, encoding = "fixed")]  // Compile error: conflict
+    field: i32,
+}
+```
+
+## Cross-Language Compatibility
+
+When serializing data to be read by other languages (Java, C++, Go, Python), 
use field configuration to match encoding expectations:
+
+```rust
+#[derive(Fory)]
+struct CrossLangData {
+    // Matches Java Integer with varint
+    #[fory(id = 0, encoding = "varint")]
+    int_var: i32,
+
+    // Matches Java Integer with fixed
+    #[fory(id = 1, encoding = "fixed")]
+    int_fixed: i32,
+
+    // Matches Java Long with tagged encoding
+    #[fory(id = 2, encoding = "tagged")]
+    long_tagged: u64,
+
+    // Nullable pointer matches Java nullable reference
+    #[fory(id = 3, nullable)]
+    optional: Option<String>,
+}
+```
+
+## Schema Evolution
+
+Compatible mode supports schema evolution. It is recommended to configure 
field IDs to reduce serialization cost:
+
+```rust
+// Version 1
+#[derive(Fory)]
+struct DataV1 {
+    #[fory(id = 0)]
+    id: i64,
+
+    #[fory(id = 1)]
+    name: String,
+}
+
+// Version 2: Added new field
+#[derive(Fory)]
+struct DataV2 {
+    #[fory(id = 0)]
+    id: i64,
+
+    #[fory(id = 1)]
+    name: String,
+
+    #[fory(id = 2)]
+    email: Option<String>,  // New nullable field
+}
+```
+
+Data serialized with V1 can be deserialized with V2 (new field will be `None`).
+
+Alternatively, field IDs can be omitted (field names will be used in metadata 
with larger overhead):
+
+```rust
+#[derive(Fory)]
+struct Data {
+    id: i64,
+    name: String,
+}
+```
+
+## Xlang Mode Defaults
+
+Rust only supports xlang (cross-language) mode. The defaults are strict due to 
type system differences between languages:
+
+- **Nullable**: Fields are non-nullable by default (except `Option<T>`)
+- **Ref tracking**: Disabled by default (except `Rc<T>`, `Arc<T>`, weak types)
+
+You **need to configure fields** when:
+
+- A field can be None (use `Option<T>`)
+- 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)
+
+```rust
+// Xlang mode: explicit configuration required
+#[derive(Fory)]
+struct User {
+    #[fory(id = 0)]
+    name: String,                    // Non-nullable by default
+
+    #[fory(id = 1)]
+    email: Option<String>,           // Nullable (Option<T>)
+
+    #[fory(id = 2, ref = true)]
+    friend: Rc<User>,                // Ref tracking (default for Rc)
+}
+```
+
+### Default Values Summary
+
+| Type                      | Default Nullable | Default Ref Tracking |
+| ------------------------- | ---------------- | -------------------- |
+| Primitives, `String`      | `false`          | `false`              |
+| `Option<T>`               | `true`           | `false`              |
+| `Rc<T>`, `Arc<T>`         | `false`          | `true`               |
+| `RcWeak<T>`, `ArcWeak<T>` | `true`           | `true`               |
+
+## Best Practices
+
+1. **Configure field IDs**: Recommended for compatible mode to reduce 
serialization cost
+2. **Use `skip` for sensitive data**: Passwords, tokens, internal state
+3. **Enable ref tracking for shared objects**: When the same pointer appears 
multiple times
+4. **Disable ref tracking for unique fields**: Optimization when you know the 
field is unique
+5. **Choose appropriate encoding**: `varint` for small values, `fixed` for 
full-range values
+6. **Keep IDs stable**: Once assigned, don't change field IDs
+
+## Options Reference
+
+| Option     | Syntax                             | Description                
          | Valid For                  |
+| ---------- | ---------------------------------- | 
------------------------------------ | -------------------------- |
+| `id`       | `id = N`                           | Field tag ID to reduce 
metadata size | All fields                 |
+| `skip`     | `skip`                             | Exclude field from 
serialization     | All fields                 |
+| `nullable` | `nullable` or `nullable = bool`    | Control null flag writing  
          | All fields                 |
+| `ref`      | `ref` or `ref = bool`              | Control reference tracking 
          | `Rc`, `Arc`, weak types    |
+| `encoding` | `encoding = "varint/fixed/tagged"` | Integer encoding method    
          | `i32`, `u32`, `i64`, `u64` |
+| `compress` | `compress` or `compress = bool`    | Shorthand for varint/fixed 
          | `i32`, `u32`               |
+
+## Related Topics
+
+- [Basic Serialization](basic-serialization.md) - Getting started with Fory 
serialization
+- [Schema Evolution](schema-evolution.md) - Compatible mode and schema 
evolution
+- [Cross-Language](cross-language.md) - Interoperability with Java, C++, Go, 
Python
diff --git a/docs/guide/xlang/field-type-meta.md 
b/docs/guide/xlang/field-type-meta.md
new file mode 100644
index 000000000..c5a76b99f
--- /dev/null
+++ b/docs/guide/xlang/field-type-meta.md
@@ -0,0 +1,301 @@
+---
+title: Field Type Meta
+sidebar_position: 4
+id: field_type_meta
+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.
+---
+
+Field type meta configuration controls whether type information is written 
during serialization for struct fields. This is essential for supporting 
polymorphism where the actual runtime type may differ from the declared field 
type.
+
+## Overview
+
+When serializing a struct field, Fory needs to determine whether to write type 
metadata:
+
+- **Static typing**: Use the declared field type's serializer directly (no 
type info written)
+- **Dynamic typing**: Write type information to support runtime subtypes
+
+## When Type Meta Is Needed
+
+Type metadata is required when:
+
+1. **Interface/abstract fields**: The declared type is abstract, so concrete 
type must be recorded
+2. **Polymorphic fields**: The runtime type may be a subclass of the declared 
type
+3. **Cross-language compatibility**: When the receiver needs type information 
to deserialize correctly
+
+Type metadata is NOT needed when:
+
+1. **Final/concrete types**: The declared type is final/sealed and cannot be 
subclassed
+2. **Primitive types**: Type is known at compile time
+3. **Performance optimization**: When you know the runtime type always matches 
the declared type
+
+## Language-Specific Configuration
+
+### Java
+
+Java requires explicit configuration because concrete classes can be 
subclassed unless marked `final`.
+
+Use the `@ForyField` annotation with the `dynamic` parameter:
+
+```java
+import org.apache.fory.annotation.ForyField;
+import org.apache.fory.annotation.ForyField.Dynamic;
+
+public class Container {
+    // AUTO (default): Interface types write type info, concrete types don't
+    @ForyField(id = 0)
+    private Shape shape;  // Interface - type info written
+
+    // FALSE: Never write type info (use declared type's serializer)
+    @ForyField(id = 1, dynamic = Dynamic.FALSE)
+    private Circle circle;  // Always treated as Circle
+
+    // TRUE: Always write type info (support runtime subtypes)
+    @ForyField(id = 2, dynamic = Dynamic.TRUE)
+    private Shape concreteShape;  // Type info written even if concrete
+}
+```
+
+**Dynamic Options**:
+
+| Value   | Behavior                                               |
+| ------- | ------------------------------------------------------ |
+| `AUTO`  | Interface/abstract types are dynamic, concrete are not |
+| `FALSE` | Never write type info, use declared type's serializer  |
+| `TRUE`  | Always write type info to support runtime subtypes     |
+
+**Use Cases**:
+
+- `AUTO`: Default behavior, suitable for most cases
+- `FALSE`: Performance optimization when you know the exact type
+- `TRUE`: When a concrete field may hold subclass instances
+
+### C++
+
+C++ uses the `fory::dynamic<V>` template tag or `.dynamic(bool)` builder 
method:
+
+**Using `fory::field<>` template**:
+
+```cpp
+#include "fory/serialization/fory.h"
+
+// Abstract base class with pure virtual methods
+struct Animal {
+    virtual ~Animal() = default;
+    virtual std::string speak() const = 0;
+};
+
+struct Zoo {
+    // Auto: type info written because Animal is polymorphic 
(std::is_polymorphic)
+    fory::field<std::shared_ptr<Animal>, 0, fory::nullable> animal;
+
+    // Force non-dynamic: skip type info even though Animal is polymorphic
+    fory::field<std::shared_ptr<Animal>, 1, fory::nullable, 
fory::dynamic<false>> fixed_animal;
+
+    // Force dynamic: write type info even for non-polymorphic types
+    fory::field<std::shared_ptr<Data>, 2, fory::dynamic<true>> 
polymorphic_data;
+};
+FORY_STRUCT(Zoo, animal, fixed_animal, polymorphic_data);
+```
+
+**Using `FORY_FIELD_CONFIG` macro**:
+
+```cpp
+struct Zoo {
+    std::shared_ptr<Animal> animal;
+    std::shared_ptr<Animal> fixed_animal;
+    std::shared_ptr<Data> polymorphic_data;
+};
+
+FORY_STRUCT(Zoo, animal, fixed_animal, polymorphic_data);
+
+FORY_FIELD_CONFIG(Zoo,
+    (animal, fory::F(0).nullable()),                    // Auto-detect 
polymorphism
+    (fixed_animal, fory::F(1).nullable().dynamic(false)), // Skip type info
+    (polymorphic_data, fory::F(2).dynamic(true))        // Force type info
+);
+```
+
+**Default Behavior**: Fory auto-detects polymorphism via 
`std::is_polymorphic<T>`. Types with pure virtual methods are treated as 
dynamic by default.
+
+### Go and Rust
+
+Go and Rust do **not** require explicit dynamic configuration because:
+
+- **Go**: Interface types are inherently dynamic - Fory can determine from the 
type whether it's an interface
+- **Rust**: Trait objects (`dyn Trait`) are explicitly marked in the type 
system
+
+The type system in these languages already indicates whether a field is 
polymorphic:
+
+```go
+// Go: interface types are automatically dynamic
+type Container struct {
+    Shape  Shape       // Interface - type info written automatically
+    Circle Circle      // Concrete struct - no type info needed
+}
+```
+
+```rust
+// Rust: trait objects are explicitly marked
+struct Container {
+    shape: Box<dyn Shape>,  // Trait object - type info written automatically
+    circle: Circle,         // Concrete type - no type info needed
+}
+```
+
+### Python
+
+Use `pyfory.field()` with the `dynamic` parameter:
+
+```python
+from dataclasses import dataclass
+from abc import ABC, abstractmethod
+import pyfory
+
+class Shape(ABC):
+    @abstractmethod
+    def area(self) -> float:
+        pass
+
+@dataclass
+class Circle(Shape):
+    radius: float = 0.0
+
+    def area(self) -> float:
+        return 3.14159 * self.radius * self.radius
+
+@dataclass
+class Container:
+    # Abstract class: dynamic is always True (type info written)
+    shape: Shape = pyfory.field(id=0)
+
+    # Concrete type with explicit dynamic=True (force type info)
+    circle: Circle = pyfory.field(id=1, dynamic=True)
+
+    # Concrete type with explicit dynamic=False (skip type info)
+    fixed_circle: Circle = pyfory.field(id=2, dynamic=False)
+```
+
+**Default Behavior**:
+
+| Mode        | Abstract Class | Concrete Object Types | Numeric/str/time 
Types |
+| ----------- | -------------- | --------------------- | 
---------------------- |
+| Native mode | `True`         | `True`                | `False`               
 |
+| Xlang mode  | `True`         | `False`               | `False`               
 |
+
+- **Abstract classes**: `dynamic` is always `True` (type info must be written)
+- **Native mode**: `dynamic` defaults to `True` for object types, `False` for 
numeric/str/time types
+- **Xlang mode**: `dynamic` defaults to `False` for concrete types
+
+## Default Behavior
+
+| Language | Interface/Abstract Types | Concrete Types   |
+| -------- | ------------------------ | ---------------- |
+| Java     | Dynamic (write type)     | Static (no type) |
+| C++      | Dynamic (virtual)        | Static           |
+| Go       | Dynamic (interface)      | Static (struct)  |
+| Rust     | Dynamic (dyn Trait)      | Static           |
+| Python   | Dynamic (all objects)    | Dynamic          |
+
+## Performance Considerations
+
+Writing type metadata has overhead:
+
+- **Space**: Type information adds bytes to serialized output
+- **Time**: Type resolution during serialization/deserialization
+
+Use `dynamic = FALSE` (Java) or `dynamic(false)` (C++) when:
+
+- You're certain the runtime type matches the declared type
+- Performance is critical and polymorphism is not needed
+- The field type is effectively final
+
+## Cross-Language Compatibility
+
+When serializing data for cross-language consumption:
+
+1. **Use consistent type registration**: Register types with the same ID 
across languages
+2. **Prefer explicit configuration**: Use `dynamic = TRUE` when unsure about 
receiver's expectations
+3. **Document polymorphic fields**: Make it clear which fields may contain 
subtypes
+
+## Example: Polymorphic Container
+
+### Java
+
+```java
+public interface Animal {
+    String speak();
+}
+
+public class Dog implements Animal {
+    private String name;
+
+    @Override
+    public String speak() { return "Woof!"; }
+}
+
+public class Cat implements Animal {
+    private String name;
+
+    @Override
+    public String speak() { return "Meow!"; }
+}
+
+public class Zoo {
+    // Type info written because Animal is an interface
+    @ForyField(id = 0)
+    private Animal animal;
+
+    // Force type info for concrete type that may hold subtypes
+    @ForyField(id = 1, dynamic = Dynamic.TRUE)
+    private Dog maybeMixedBreed;
+}
+```
+
+### C++
+
+```cpp
+// Abstract base class with pure virtual methods
+class Animal {
+public:
+    virtual std::string speak() const = 0;
+    virtual ~Animal() = default;
+};
+
+class Dog : public Animal {
+public:
+    std::string name;
+    std::string speak() const override { return "Woof!"; }
+};
+
+struct Zoo {
+    std::shared_ptr<Animal> animal;
+    std::shared_ptr<Dog> maybe_mixed_breed;
+};
+
+FORY_STRUCT(Zoo, animal, maybe_mixed_breed);
+
+FORY_FIELD_CONFIG(Zoo,
+    (animal, fory::F(0).nullable()),              // Auto-detect (Animal is 
polymorphic)
+    (maybe_mixed_breed, fory::F(1).dynamic(true)) // Force dynamic for 
concrete type
+);
+```
+
+## Related Topics
+
+- [Field Nullability](field-nullability.md) - Controlling null handling for 
fields
+- [Field Reference Tracking](field-reference-tracking.md) - Managing 
shared/circular references
+- [Type Mapping](xlang_type_mapping.md) - Cross-language type compatibility


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

Reply via email to