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.git


The following commit(s) were added to refs/heads/main by this push:
     new 9a032a1d6 feat(Rust): Support automatic conversion between T and 
Option<T> when deserialize (#2563)
9a032a1d6 is described below

commit 9a032a1d67fcc759cc7b423fbc3bf4025489945a
Author: urlyy <[email protected]>
AuthorDate: Thu Sep 4 00:01:11 2025 +0800

    feat(Rust): Support automatic conversion between T and Option<T> when 
deserialize (#2563)
    
    ## Why?
    Fory needs to support the following conversions when `deserialize`
    (Option means nullable type)(left is sender_field_type, right is
    receiver_field_type):
    - value -> value
    - value -> Option(value)
    - Option(value) -> value
    - Option(value) -> Option(value)
    - Option(None) -> Option(None)
    - Option(None) -> value_default
    
    ## What does this PR do?
    - Support automatic conversion between `T` and `Option<T>` when
    deserialize. The usage can be seen in
    `test_complex_struct.rs::nullable()`.
    - Add NullableFieldType to represent `T{nullable}`, while the original
    FieldType exactly matches the data structure of a struct, such as
    `Option{T}`. Comparison of `NullableFieldType` ignores the `nullable`
    attribute.
    - Add `read_var_uint32()` && `write_var_uint32()`.
    - Changed the type of `type_id` from `i16` to `u32`, and updated the
    read/write operations from `i16()` to `var_uint32()`
    - When registering a struct, add `types::STRUCT` to its id in addition
    to the original type_id
    - Removed the sending of `meta_index`.
    - For reuse, extract the `read_compatible` function.
    
    
    
    ## Detail
    1. The processing approach is as follows:
    - At compile time, parse and obtain the `FieldType` instance. The
    `FieldType` (e.g., `Option{T}`) can be converted into a
    `NullableFieldType` (e.g., `T{nullable=true}`).
    - At compile time, based on the `NullableFieldType`, generate a
    `_deserialize_nullable_{field_name}` function for each field. This
    function defines the read process constrained by the
    `local_nullable_field_type` (compile-time instance, obtained through
    macro parsing) and determines the actual data read according to the
    `remote_nullable_field_type` (runtime instance, obtained from the
    received type_meta).
       - Deserialization flow:
         ```rust
         if local_field_name_f1 == remote_field_name {
             if local_field_type_f1 == remote_field_type {
                 f1 = <f1_T>::deserialize();
             } else {
    if nullable_local_field_type_f1 == nullable_remote_field_type {
    f1 = Self::_deserialize_nullable_f1(remote_nullable_field_type);
                 } else {
                     skip_field_value(remote_field_type);
                     f1 = default();
                 }
             }
         } else if local_field_name_f2 == remote_field_name {
             ...
         } else if ... {
             ...
         } else {
             skip_field_value(remote_field_type);
         }
         ```
    2. Adjacent `Option`s like `Option<Option<T>>` are not supported. If
    such a field is defined, a compile-time error will be raised: `adjacent
    Options are not supported`.
    
    ## Related issues
    #2529
    
    ## Does this PR introduce any user-facing change?
    
    - [ ] Does this PR introduce any public API change?
    - [x] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
---
 rust/fory-core/src/buffer.rs                    |  56 +++++
 rust/fory-core/src/fory.rs                      |   7 +-
 rust/fory-core/src/meta/mod.rs                  |   2 +-
 rust/fory-core/src/meta/type_meta.rs            |  76 ++++--
 rust/fory-core/src/resolver/context.rs          |  10 +
 rust/fory-core/src/resolver/type_resolver.rs    |  14 +-
 rust/fory-core/src/serializer/any.rs            |  77 +++---
 rust/fory-core/src/serializer/bool.rs           |   4 +-
 rust/fory-core/src/serializer/datetime.rs       |   8 +-
 rust/fory-core/src/serializer/list.rs           |   6 +-
 rust/fory-core/src/serializer/map.rs            |   4 +-
 rust/fory-core/src/serializer/mod.rs            |  17 +-
 rust/fory-core/src/serializer/nonexistent.rs    | 108 ---------
 rust/fory-core/src/serializer/number.rs         |   4 +-
 rust/fory-core/src/serializer/option.rs         |   9 +-
 rust/fory-core/src/serializer/primitive_list.rs |   8 +-
 rust/fory-core/src/serializer/set.rs            |   6 +-
 rust/fory-core/src/serializer/skip.rs           | 106 ++++++++
 rust/fory-core/src/serializer/string.rs         |   4 +-
 rust/fory-core/src/types.rs                     |  15 ++
 rust/fory-derive/src/object/derive_enum.rs      |   2 +-
 rust/fory-derive/src/object/misc.rs             |   8 +-
 rust/fory-derive/src/object/read.rs             | 123 +++++++---
 rust/fory-derive/src/object/serializer.rs       |  18 +-
 rust/fory-derive/src/object/util.rs             | 306 +++++++++++++++++++++++-
 rust/fory-derive/src/object/write.rs            |   9 +-
 rust/tests/tests/test_buffer.rs                 |   8 +
 rust/tests/tests/test_compatible.rs             | 250 ++++++++++++++++++-
 rust/tests/tests/test_complex_struct.rs         |   2 +-
 29 files changed, 1005 insertions(+), 262 deletions(-)

diff --git a/rust/fory-core/src/buffer.rs b/rust/fory-core/src/buffer.rs
index 2cc6b2744..9e1715745 100644
--- a/rust/fory-core/src/buffer.rs
+++ b/rust/fory-core/src/buffer.rs
@@ -115,6 +115,40 @@ impl Writer {
         }
     }
 
+    pub fn var_uint32(&mut self, value: u32) {
+        if value >> 7 == 0 {
+            self.u8(value as u8);
+        } else if value >> 14 == 0 {
+            let u1 = (value & 0x7F) | 0x80;
+            let u2 = value >> 7;
+            self.u16(((u2 << 8) | u1) as u16);
+        } else if value >> 21 == 0 {
+            let u1 = (value & 0x7F) | 0x80;
+            let u2 = ((value >> 7) & 0x7F) | 0x80;
+            let u3 = value >> 14;
+            self.u8(u1 as u8);
+            self.u8(u2 as u8);
+            self.u8(u3 as u8);
+        } else if value >> 28 == 0 {
+            let u1 = (value & 0x7F) | 0x80;
+            let u2 = ((value >> 7) & 0x7F) | 0x80;
+            let u3 = ((value >> 14) & 0x7F) | 0x80;
+            let u4 = value >> 21;
+            self.u32((u4 << 24) | (u3 << 16) | (u2 << 8) | u1);
+        } else {
+            let u1 = (value & 0x7F) | 0x80;
+            let u2 = ((value >> 7) & 0x7F) | 0x80;
+            let u3 = ((value >> 14) & 0x7F) | 0x80;
+            let u4 = ((value >> 21) & 0x7F) | 0x80;
+            let u5 = value >> 28;
+            self.u8(u1 as u8);
+            self.u8(u2 as u8);
+            self.u8(u3 as u8);
+            self.u8(u4 as u8);
+            self.u8(u5 as u8);
+        }
+    }
+
     pub fn bytes(&mut self, v: &[u8]) {
         self.reserve(v.len());
         self.bf.extend_from_slice(v);
@@ -228,6 +262,28 @@ impl<'bf> Reader<'bf> {
         result
     }
 
+    pub fn var_uint32(&mut self) -> u32 {
+        let mut byte_ = self.i8() as u32;
+        let mut result = byte_ & 0x7F;
+        if (byte_ & 0x80) != 0 {
+            byte_ = self.i8() as u32;
+            result |= (byte_ & 0x7F) << 7;
+            if (byte_ & 0x80) != 0 {
+                byte_ = self.i8() as u32;
+                result |= (byte_ & 0x7F) << 14;
+                if (byte_ & 0x80) != 0 {
+                    byte_ = self.i8() as u32;
+                    result |= (byte_ & 0x7F) << 21;
+                    if (byte_ & 0x80) != 0 {
+                        byte_ = self.i8() as u32;
+                        result |= (byte_ & 0x7F) << 28;
+                    }
+                }
+            }
+        }
+        result
+    }
+
     pub fn string(&mut self, len: usize) -> String {
         let result = String::from_utf8_lossy(&self.bf[self.cursor..self.cursor 
+ len]).to_string();
         self.move_next(len);
diff --git a/rust/fory-core/src/fory.rs b/rust/fory-core/src/fory.rs
index 42c4492e1..ed83f6418 100644
--- a/rust/fory-core/src/fory.rs
+++ b/rust/fory-core/src/fory.rs
@@ -90,8 +90,9 @@ impl Fory {
         &self.type_resolver
     }
 
-    pub fn register<T: 'static + StructSerializer>(&mut self, id: i16) {
-        let type_info = TypeInfo::new::<T>(self, id);
-        self.type_resolver.register::<T>(type_info, id);
+    pub fn register<T: 'static + StructSerializer>(&mut self, id: u32) {
+        let actual_type_id = T::actual_type_id(id);
+        let type_info = TypeInfo::new::<T>(self, actual_type_id);
+        self.type_resolver.register::<T>(type_info, actual_type_id);
     }
 }
diff --git a/rust/fory-core/src/meta/mod.rs b/rust/fory-core/src/meta/mod.rs
index 04f51a985..27f59ab07 100644
--- a/rust/fory-core/src/meta/mod.rs
+++ b/rust/fory-core/src/meta/mod.rs
@@ -21,4 +21,4 @@ mod type_meta;
 
 pub use meta_string::{Encoding, MetaString, MetaStringDecoder, 
MetaStringEncoder};
 pub use string_util::murmurhash3_x64_128;
-pub use type_meta::{FieldInfo, FieldType, TypeMeta, TypeMetaLayer};
+pub use type_meta::{FieldInfo, FieldType, NullableFieldType, TypeMeta, 
TypeMetaLayer};
diff --git a/rust/fory-core/src/meta/type_meta.rs 
b/rust/fory-core/src/meta/type_meta.rs
index a436f2e89..bfd4a5e2f 100644
--- a/rust/fory-core/src/meta/type_meta.rs
+++ b/rust/fory-core/src/meta/type_meta.rs
@@ -41,17 +41,56 @@ static ENCODING_OPTIONS: &[Encoding] = &[
 
 #[derive(Debug, PartialEq, Eq, Clone)]
 pub struct FieldType {
-    pub type_id: i16,
+    pub type_id: u32,
     pub generics: Vec<FieldType>,
 }
 
+#[derive(Debug, Clone)]
+pub struct NullableFieldType {
+    pub type_id: u32,
+    pub generics: Vec<NullableFieldType>,
+    pub nullable: bool,
+}
+
+impl NullableFieldType {
+    pub fn from(node: FieldType) -> Self {
+        if node.type_id == TypeId::ForyOption as u32 {
+            let inner = 
NullableFieldType::from(node.generics.into_iter().next().unwrap());
+            NullableFieldType {
+                type_id: inner.type_id,
+                generics: inner.generics,
+                nullable: true,
+            }
+        } else {
+            let generics = node
+                .generics
+                .into_iter()
+                .map(NullableFieldType::from)
+                .collect();
+            NullableFieldType {
+                type_id: node.type_id,
+                generics,
+                nullable: false,
+            }
+        }
+    }
+}
+
+impl PartialEq for NullableFieldType {
+    fn eq(&self, other: &Self) -> bool {
+        self.type_id == other.type_id && self.generics == other.generics
+    }
+}
+
+impl Eq for NullableFieldType {}
+
 impl FieldType {
-    pub fn new(type_id: i16, generics: Vec<FieldType>) -> Self {
+    pub fn new(type_id: u32, generics: Vec<FieldType>) -> Self {
         FieldType { type_id, generics }
     }
 
     fn to_bytes(&self, writer: &mut Writer, write_flag: bool, nullable: bool) 
-> Result<(), Error> {
-        if self.type_id == TypeId::ForyOption.into() {
+        if self.type_id == TypeId::ForyOption as u32 {
             self.generics
                 .first()
                 .unwrap()
@@ -68,11 +107,11 @@ impl FieldType {
         }
         writer.var_int32(header);
         match self.type_id {
-            x if x == TypeId::ARRAY as i16 || x == TypeId::SET as i16 => {
+            x if x == TypeId::ARRAY as u32 || x == TypeId::SET as u32 => {
                 let generic = self.generics.first().unwrap();
                 generic.to_bytes(writer, true, false)?;
             }
-            x if x == TypeId::MAP as i16 => {
+            x if x == TypeId::MAP as u32 => {
                 let key_generic = self.generics.first().unwrap();
                 let val_generic = self.generics.get(1).unwrap();
                 key_generic.to_bytes(writer, true, false)?;
@@ -83,28 +122,27 @@ impl FieldType {
         Ok(())
     }
 
-    #[allow(clippy::needless_late_init)]
     fn from_bytes(reader: &mut Reader, read_flag: bool, nullable: 
Option<bool>) -> Self {
         let header = reader.var_int32();
         let type_id;
         let _nullable;
         if read_flag {
-            type_id = (header >> 2) as i16;
+            type_id = (header >> 2) as u32;
             // let tracking_ref = (header & 1) != 0;
             _nullable = (header & 2) != 0;
         } else {
-            type_id = header as i16;
+            type_id = header as u32;
             _nullable = nullable.unwrap();
         }
         let field_type = match type_id {
-            x if x == TypeId::ARRAY.into() || x == TypeId::SET.into() => {
+            x if x == TypeId::ARRAY as u32 || x == TypeId::SET as u32 => {
                 let generic = Self::from_bytes(reader, true, None);
                 Self {
                     type_id,
                     generics: vec![generic],
                 }
             }
-            x if x == TypeId::MAP.into() => {
+            x if x == TypeId::MAP as u32 => {
                 let key_generic = Self::from_bytes(reader, true, None);
                 let val_generic = Self::from_bytes(reader, true, None);
                 Self {
@@ -119,7 +157,7 @@ impl FieldType {
         };
         if _nullable {
             Self {
-                type_id: TypeId::ForyOption.into(),
+                type_id: TypeId::ForyOption as u32,
                 generics: vec![field_type],
             }
         } else {
@@ -188,7 +226,7 @@ impl FieldInfo {
         let name_size = name_encoded.len() - 1;
         let mut header: u8 = (min(FIELD_NAME_SIZE_THRESHOLD, name_size) as u8) 
<< 2;
         // let ref_tracking = false;
-        let nullable = self.field_type.type_id == TypeId::ForyOption.into();
+        let nullable = self.field_type.type_id == TypeId::ForyOption as u32;
         // if ref_tracking {
         //     header |= 1;
         // }
@@ -213,19 +251,19 @@ impl FieldInfo {
 
 #[derive(Debug)]
 pub struct TypeMetaLayer {
-    type_id: i16,
+    type_id: u32,
     field_infos: Vec<FieldInfo>,
 }
 
 impl TypeMetaLayer {
-    pub fn new(type_id: i16, field_infos: Vec<FieldInfo>) -> TypeMetaLayer {
+    pub fn new(type_id: u32, field_infos: Vec<FieldInfo>) -> TypeMetaLayer {
         TypeMetaLayer {
             type_id,
             field_infos,
         }
     }
 
-    pub fn get_type_id(&self) -> i16 {
+    pub fn get_type_id(&self) -> u32 {
         self.type_id
     }
 
@@ -250,7 +288,7 @@ impl TypeMetaLayer {
         if is_register_by_name {
             todo!()
         } else {
-            writer.var_int32(self.type_id as i32);
+            writer.var_uint32(self.type_id);
         }
         for field in self.field_infos.iter() {
             writer.bytes(field.to_bytes()?.as_slice());
@@ -271,7 +309,7 @@ impl TypeMetaLayer {
         if is_register_by_name {
             todo!()
         } else {
-            type_id = reader.var_int32() as i16;
+            type_id = reader.var_uint32();
         }
         let mut field_infos = Vec::with_capacity(num_fields);
         for _ in 0..num_fields {
@@ -293,11 +331,11 @@ impl TypeMeta {
         self.layers.first().unwrap().get_field_infos()
     }
 
-    pub fn get_type_id(&self) -> i16 {
+    pub fn get_type_id(&self) -> u32 {
         self.layers.first().unwrap().get_type_id()
     }
 
-    pub fn from_fields(type_id: i16, field_infos: Vec<FieldInfo>) -> TypeMeta {
+    pub fn from_fields(type_id: u32, field_infos: Vec<FieldInfo>) -> TypeMeta {
         TypeMeta {
             // hash: 0,
             layers: vec![TypeMetaLayer::new(type_id, field_infos)],
diff --git a/rust/fory-core/src/resolver/context.rs 
b/rust/fory-core/src/resolver/context.rs
index 54d8fe809..4100e3706 100644
--- a/rust/fory-core/src/resolver/context.rs
+++ b/rust/fory-core/src/resolver/context.rs
@@ -100,6 +100,16 @@ impl<'de, 'bf: 'de> ReadContext<'de, 'bf> {
         self.meta_resolver.get(type_index)
     }
 
+    pub fn get_meta_by_type_id(&self, type_id: u32) -> Rc<TypeMeta> {
+        let type_defs: Vec<_> = self.meta_resolver.reading_type_defs.to_vec();
+        for type_def in type_defs.iter() {
+            if type_def.get_type_id() == type_id {
+                return type_def.clone();
+            }
+        }
+        unreachable!()
+    }
+
     pub fn load_meta(&mut self, offset: usize) {
         self.meta_resolver
             .load(&mut Reader::new(&self.reader.slice()[offset..]))
diff --git a/rust/fory-core/src/resolver/type_resolver.rs 
b/rust/fory-core/src/resolver/type_resolver.rs
index 41fcdc557..a4cc0bb49 100644
--- a/rust/fory-core/src/resolver/type_resolver.rs
+++ b/rust/fory-core/src/resolver/type_resolver.rs
@@ -48,18 +48,18 @@ impl Harness {
 
 pub struct TypeInfo {
     type_def: Vec<u8>,
-    type_id: i16,
+    type_id: u32,
 }
 
 impl TypeInfo {
-    pub fn new<T: StructSerializer>(fory: &Fory, type_id: i16) -> TypeInfo {
+    pub fn new<T: StructSerializer>(fory: &Fory, type_id: u32) -> TypeInfo {
         TypeInfo {
             type_def: T::type_def(fory, type_id),
             type_id,
         }
     }
 
-    pub fn get_type_id(&self) -> i16 {
+    pub fn get_type_id(&self) -> u32 {
         self.type_id
     }
 
@@ -70,8 +70,8 @@ impl TypeInfo {
 
 #[derive(Default)]
 pub struct TypeResolver {
-    serialize_map: HashMap<i16, Harness>,
-    type_id_map: HashMap<std::any::TypeId, i16>,
+    serialize_map: HashMap<u32, Harness>,
+    type_id_map: HashMap<std::any::TypeId, u32>,
     type_info_map: HashMap<std::any::TypeId, TypeInfo>,
 }
 
@@ -85,7 +85,7 @@ impl TypeResolver {
         })
     }
 
-    pub fn register<T: StructSerializer>(&mut self, type_info: TypeInfo, id: 
i16) {
+    pub fn register<T: StructSerializer>(&mut self, type_info: TypeInfo, id: 
u32) {
         fn serializer<T2: 'static + StructSerializer>(this: &dyn Any, context: 
&mut WriteContext) {
             let this = this.downcast_ref::<T2>();
             match this {
@@ -115,7 +115,7 @@ impl TypeResolver {
         self.get_harness(*self.type_id_map.get(&type_id).unwrap())
     }
 
-    pub fn get_harness(&self, id: i16) -> Option<&Harness> {
+    pub fn get_harness(&self, id: u32) -> Option<&Harness> {
         self.serialize_map.get(&id)
     }
 }
diff --git a/rust/fory-core/src/serializer/any.rs 
b/rust/fory-core/src/serializer/any.rs
index 321f75fc2..9232163a0 100644
--- a/rust/fory-core/src/serializer/any.rs
+++ b/rust/fory-core/src/serializer/any.rs
@@ -19,8 +19,7 @@ use crate::error::Error;
 use crate::fory::Fory;
 use crate::resolver::context::{ReadContext, WriteContext};
 use crate::serializer::Serializer;
-use crate::types::{Mode, RefFlag, TypeId};
-use anyhow::anyhow;
+use crate::types::TypeId;
 use std::any::Any;
 
 impl Serializer for Box<dyn Any> {
@@ -36,8 +35,8 @@ impl Serializer for Box<dyn Any> {
         panic!("unreachable")
     }
 
-    fn get_type_id(_fory: &Fory) -> i16 {
-        TypeId::ForyTypeTag.into()
+    fn get_type_id(_fory: &Fory) -> u32 {
+        TypeId::ForyTypeTag as u32
     }
 
     fn serialize(&self, context: &mut WriteContext) {
@@ -49,40 +48,40 @@ impl Serializer for Box<dyn Any> {
             .get_serializer()(self.as_ref(), context);
     }
 
-    fn deserialize(context: &mut ReadContext) -> Result<Self, Error> {
-        let reset_cursor = context.reader.reset_cursor_to_here();
-        // ref flag
-        let ref_flag = context.reader.i8();
-
-        if ref_flag == (RefFlag::NotNullValue as i8) || ref_flag == 
(RefFlag::RefValue as i8) {
-            if context.get_fory().get_mode().eq(&Mode::Compatible) {
-                let meta_index = context.reader.i16();
-                let type_id = context.meta_resolver.get(meta_index as 
usize).get_type_id();
-                reset_cursor(&mut context.reader);
-                context
-                    .get_fory()
-                    .get_type_resolver()
-                    .get_harness(type_id)
-                    .unwrap()
-                    .get_deserializer()(context)
-            } else {
-                let type_id = context.reader.i16();
-                reset_cursor(&mut context.reader);
-                context
-                    .get_fory()
-                    .get_type_resolver()
-                    .get_harness(type_id)
-                    .unwrap()
-                    .get_deserializer()(context)
-            }
-        } else if ref_flag == (RefFlag::Null as i8) {
-            Err(anyhow!("Try to deserialize `any` to null"))?
-        } else if ref_flag == (RefFlag::Ref as i8) {
-            reset_cursor(&mut context.reader);
-            Err(Error::Ref)
-        } else {
-            reset_cursor(&mut context.reader);
-            Err(anyhow!("Unknown ref flag, value:{ref_flag}"))?
-        }
+    fn deserialize(_context: &mut ReadContext) -> Result<Self, Error> {
+        todo!()
+        // let reset_cursor = context.reader.reset_cursor_to_here();
+        // // ref flag
+        // let ref_flag = context.reader.i8();
+        //
+        // if ref_flag == (RefFlag::NotNullValue as i8) || ref_flag == 
(RefFlag::RefValue as i8) {
+        //     if context.get_fory().get_mode().eq(&Mode::Compatible) {
+        //         let type_id = context.meta_resolver.get(meta_index as 
usize).get_type_id();
+        //         reset_cursor(&mut context.reader);
+        //         context
+        //             .get_fory()
+        //             .get_type_resolver()
+        //             .get_harness(type_id)
+        //             .unwrap()
+        //             .get_deserializer()(context)
+        //     } else {
+        //         let type_id = context.reader.i16();
+        //         reset_cursor(&mut context.reader);
+        //         context
+        //             .get_fory()
+        //             .get_type_resolver()
+        //             .get_harness(type_id)
+        //             .unwrap()
+        //             .get_deserializer()(context)
+        //     }
+        // } else if ref_flag == (RefFlag::Null as i8) {
+        //     Err(anyhow!("Try to deserialize `any` to null"))?
+        // } else if ref_flag == (RefFlag::Ref as i8) {
+        //     reset_cursor(&mut context.reader);
+        //     Err(Error::Ref)
+        // } else {
+        //     reset_cursor(&mut context.reader);
+        //     Err(anyhow!("Unknown ref flag, value:{ref_flag}"))?
+        // }
     }
 }
diff --git a/rust/fory-core/src/serializer/bool.rs 
b/rust/fory-core/src/serializer/bool.rs
index 1fb0f4f5b..06b1d6a0d 100644
--- a/rust/fory-core/src/serializer/bool.rs
+++ b/rust/fory-core/src/serializer/bool.rs
@@ -36,7 +36,7 @@ impl Serializer for bool {
         Ok(context.reader.u8() == 1)
     }
 
-    fn get_type_id(_fory: &Fory) -> i16 {
-        TypeId::BOOL.into()
+    fn get_type_id(_fory: &Fory) -> u32 {
+        TypeId::BOOL as u32
     }
 }
diff --git a/rust/fory-core/src/serializer/datetime.rs 
b/rust/fory-core/src/serializer/datetime.rs
index 8084ffd44..0bedf663c 100644
--- a/rust/fory-core/src/serializer/datetime.rs
+++ b/rust/fory-core/src/serializer/datetime.rs
@@ -44,8 +44,8 @@ impl Serializer for NaiveDateTime {
         mem::size_of::<u64>()
     }
 
-    fn get_type_id(_fory: &Fory) -> i16 {
-        TypeId::TIMESTAMP.into()
+    fn get_type_id(_fory: &Fory) -> u32 {
+        TypeId::TIMESTAMP as u32
     }
 }
 
@@ -70,8 +70,8 @@ impl Serializer for NaiveDate {
             )))
     }
 
-    fn get_type_id(_fory: &Fory) -> i16 {
-        TypeId::LOCAL_DATE.into()
+    fn get_type_id(_fory: &Fory) -> u32 {
+        TypeId::LOCAL_DATE as u32
     }
 }
 
diff --git a/rust/fory-core/src/serializer/list.rs 
b/rust/fory-core/src/serializer/list.rs
index 43490aeb6..014e511d5 100644
--- a/rust/fory-core/src/serializer/list.rs
+++ b/rust/fory-core/src/serializer/list.rs
@@ -31,7 +31,7 @@ where
         context.writer.var_int32(self.len() as i32);
         context
             .writer
-            .reserve((<Self as Serializer>::reserved_space() + 
SIZE_OF_REF_AND_TYPE) * self.len());
+            .reserve((<T as Serializer>::reserved_space() + 
SIZE_OF_REF_AND_TYPE) * self.len());
         for item in self.iter() {
             item.serialize(context);
         }
@@ -50,8 +50,8 @@ where
         mem::size_of::<u32>()
     }
 
-    fn get_type_id(_fory: &Fory) -> i16 {
-        TypeId::ARRAY.into()
+    fn get_type_id(_fory: &Fory) -> u32 {
+        TypeId::ARRAY as u32
     }
 }
 
diff --git a/rust/fory-core/src/serializer/map.rs 
b/rust/fory-core/src/serializer/map.rs
index 6f00bddde..a829f5083 100644
--- a/rust/fory-core/src/serializer/map.rs
+++ b/rust/fory-core/src/serializer/map.rs
@@ -56,8 +56,8 @@ impl<T1: Serializer + Eq + std::hash::Hash, T2: Serializer> 
Serializer for HashM
         mem::size_of::<i32>()
     }
 
-    fn get_type_id(_fory: &Fory) -> i16 {
-        TypeId::MAP.into()
+    fn get_type_id(_fory: &Fory) -> u32 {
+        TypeId::MAP as u32
     }
 }
 
diff --git a/rust/fory-core/src/serializer/mod.rs 
b/rust/fory-core/src/serializer/mod.rs
index 10a84175f..bd07735db 100644
--- a/rust/fory-core/src/serializer/mod.rs
+++ b/rust/fory-core/src/serializer/mod.rs
@@ -20,6 +20,7 @@ use crate::error::Error;
 use crate::fory::Fory;
 use crate::resolver::context::{ReadContext, WriteContext};
 use crate::types::RefFlag;
+use crate::types::TypeId;
 use anyhow::anyhow;
 
 mod any;
@@ -27,18 +28,20 @@ mod bool;
 mod datetime;
 mod list;
 mod map;
-pub mod nonexistent;
 mod number;
 mod option;
 mod primitive_list;
 mod set;
+pub mod skip;
 mod string;
 
 pub fn serialize<T: Serializer>(this: &T, context: &mut WriteContext) {
     // ref flag
     context.writer.i8(RefFlag::NotNullValue as i8);
     // type
-    context.writer.i16(T::get_type_id(context.get_fory()));
+    context
+        .writer
+        .var_uint32(T::get_type_id(context.get_fory()));
     this.write(context);
 }
 
@@ -47,7 +50,7 @@ pub fn deserialize<T: Serializer>(context: &mut ReadContext) 
-> Result<T, Error>
     let ref_flag = context.reader.i8();
 
     if ref_flag == (RefFlag::NotNullValue as i8) || ref_flag == 
(RefFlag::RefValue as i8) {
-        let actual_type_id = context.reader.i16();
+        let actual_type_id = context.reader.var_uint32();
         let expected_type_id = T::get_type_id(context.get_fory());
         ensure!(
             actual_type_id == expected_type_id,
@@ -89,9 +92,13 @@ where
         deserialize(context)
     }
 
-    fn get_type_id(_fory: &Fory) -> i16;
+    fn get_type_id(_fory: &Fory) -> u32;
 }
 
 pub trait StructSerializer: Serializer + 'static {
-    fn type_def(fory: &Fory, type_id: i16) -> Vec<u8>;
+    fn type_def(fory: &Fory, type_id: u32) -> Vec<u8>;
+
+    fn actual_type_id(type_id: u32) -> u32 {
+        (type_id << 8) + TypeId::STRUCT as u32
+    }
 }
diff --git a/rust/fory-core/src/serializer/nonexistent.rs 
b/rust/fory-core/src/serializer/nonexistent.rs
deleted file mode 100644
index b9732ea64..000000000
--- a/rust/fory-core/src/serializer/nonexistent.rs
+++ /dev/null
@@ -1,108 +0,0 @@
-// 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.
-
-use crate::ensure;
-use crate::error::Error;
-use crate::meta::FieldType;
-use crate::resolver::context::ReadContext;
-use crate::serializer::Serializer;
-use crate::types::{RefFlag, TypeId, BASIC_TYPES, COLLECTION_TYPES};
-use anyhow::anyhow;
-use chrono::{NaiveDate, NaiveDateTime};
-
-macro_rules! basic_type_deserialize {
-    ($tid:expr, $context:expr; $(($ty:ty, $id:ident)),+ $(,)?) => {
-        $(
-            if $tid == TypeId::$id {
-                <$ty>::deserialize($context)?;
-                return Ok(());
-            }
-        )+else {
-            unreachable!()
-        }
-    };
-}
-
-pub fn skip_field_value(context: &mut ReadContext, field_type: &FieldType) -> 
Result<(), Error> {
-    match TypeId::try_from(field_type.type_id) {
-        Ok(type_id) => {
-            if BASIC_TYPES.contains(&type_id) {
-                basic_type_deserialize!(type_id, context;
-                    (bool, BOOL),
-                    (i8, INT8),
-                    (i16, INT16),
-                    (i32, INT32),
-                    (i64, INT64),
-                    (f32, FLOAT32),
-                    (f64, FLOAT64),
-                    (String, STRING),
-                    (NaiveDate, LOCAL_DATE),
-                    (NaiveDateTime, TIMESTAMP),
-                );
-            } else if COLLECTION_TYPES.contains(&type_id) {
-                let ref_flag = context.reader.i8();
-                let actual_type_id = context.reader.i16();
-                let type_id_num = type_id.into();
-                ensure!(
-                    actual_type_id == type_id_num,
-                    anyhow!("Invalid field type, expected:{type_id_num}, 
actual:{actual_type_id}")
-                );
-                if ref_flag == (RefFlag::NotNullValue as i8)
-                    || ref_flag == (RefFlag::RefValue as i8)
-                {
-                    if type_id == TypeId::ARRAY || type_id == TypeId::SET {
-                        let length = context.reader.var_int32() as usize;
-                        println!("skipping array with length {}", length);
-                        for _ in 0..length {
-                            skip_field_value(context, 
field_type.generics.first().unwrap())?;
-                        }
-                    } else if type_id == TypeId::MAP {
-                        let length = context.reader.var_int32() as usize;
-                        for _ in 0..length {
-                            skip_field_value(context, 
field_type.generics.first().unwrap())?;
-                            skip_field_value(context, 
field_type.generics.get(1).unwrap())?;
-                        }
-                    }
-                    Ok(())
-                } else if ref_flag == (RefFlag::Null as i8) {
-                    Err(anyhow!("Try to deserialize non-option type to null"))?
-                } else if ref_flag == (RefFlag::Ref as i8) {
-                    Err(Error::Ref)
-                } else {
-                    Err(anyhow!("Unknown ref flag, value:{ref_flag}"))?
-                }
-            } else {
-                unreachable!()
-            }
-        }
-        Err(_) => {
-            // skip ref_flag and meta_index
-            context.reader.i8();
-            context.reader.i16();
-            let type_defs: Vec<_> = 
context.meta_resolver.reading_type_defs.to_vec();
-            for type_def in type_defs.iter() {
-                if type_def.get_type_id() == field_type.type_id {
-                    let field_infos: Vec<_> = 
type_def.get_field_infos().to_vec();
-                    for field_info in field_infos.iter() {
-                        skip_field_value(context, &field_info.field_type)?;
-                    }
-                }
-            }
-            Ok(())
-        }
-    }
-}
diff --git a/rust/fory-core/src/serializer/number.rs 
b/rust/fory-core/src/serializer/number.rs
index 2d637e376..18fdadb71 100644
--- a/rust/fory-core/src/serializer/number.rs
+++ b/rust/fory-core/src/serializer/number.rs
@@ -37,8 +37,8 @@ macro_rules! impl_num_serializer {
                 std::mem::size_of::<$ty>()
             }
 
-            fn get_type_id(_fory: &Fory) -> i16 {
-                ($field_type).into()
+            fn get_type_id(_fory: &Fory) -> u32 {
+                ($field_type) as u32
             }
         }
     };
diff --git a/rust/fory-core/src/serializer/option.rs 
b/rust/fory-core/src/serializer/option.rs
index 9b8d17bc5..f6386b82e 100644
--- a/rust/fory-core/src/serializer/option.rs
+++ b/rust/fory-core/src/serializer/option.rs
@@ -35,7 +35,7 @@ impl<T: Serializer> Serializer for Option<T> {
 
         if ref_flag == (RefFlag::NotNullValue as i8) || ref_flag == 
(RefFlag::RefValue as i8) {
             // type_id
-            let actual_type_id = context.reader.i16();
+            let actual_type_id = context.reader.var_uint32();
             let expected_type_id = T::get_type_id(context.get_fory());
             ensure!(
                 actual_type_id == expected_type_id,
@@ -66,8 +66,9 @@ impl<T: Serializer> Serializer for Option<T> {
                 // ref flag
                 context.writer.i8(RefFlag::NotNullValue as i8);
                 // type
-                context.writer.i16(T::get_type_id(context.get_fory()));
-
+                context
+                    .writer
+                    .var_uint32(T::get_type_id(context.get_fory()));
                 v.write(context);
             }
             None => {
@@ -80,7 +81,7 @@ impl<T: Serializer> Serializer for Option<T> {
         std::mem::size_of::<T>()
     }
 
-    fn get_type_id(fory: &Fory) -> i16 {
+    fn get_type_id(fory: &Fory) -> u32 {
         T::get_type_id(fory)
     }
 }
diff --git a/rust/fory-core/src/serializer/primitive_list.rs 
b/rust/fory-core/src/serializer/primitive_list.rs
index 0eeb7cf51..cce829352 100644
--- a/rust/fory-core/src/serializer/primitive_list.rs
+++ b/rust/fory-core/src/serializer/primitive_list.rs
@@ -60,8 +60,8 @@ macro_rules! impl_primitive_vec {
                 mem::size_of::<i32>()
             }
 
-            fn get_type_id(_fory: &Fory) -> i16 {
-                ($field_type).into()
+            fn get_type_id(_fory: &Fory) -> u32 {
+                ($field_type) as u32
             }
         }
     };
@@ -77,8 +77,8 @@ impl Serializer for Vec<bool> {
         mem::size_of::<u8>()
     }
 
-    fn get_type_id(_fory: &Fory) -> i16 {
-        TypeId::ForyPrimitiveBoolArray.into()
+    fn get_type_id(_fory: &Fory) -> u32 {
+        TypeId::ForyPrimitiveBoolArray as u32
     }
 
     fn read(context: &mut ReadContext) -> Result<Self, Error> {
diff --git a/rust/fory-core/src/serializer/set.rs 
b/rust/fory-core/src/serializer/set.rs
index 6140e5478..6df2e6bd9 100644
--- a/rust/fory-core/src/serializer/set.rs
+++ b/rust/fory-core/src/serializer/set.rs
@@ -27,7 +27,7 @@ use std::mem;
 impl<T: Serializer + Eq + std::hash::Hash> Serializer for HashSet<T> {
     fn write(&self, context: &mut WriteContext) {
         // length
-        context.writer.i32(self.len() as i32);
+        context.writer.var_int32(self.len() as i32);
 
         let reserved_space =
             (<T as Serializer>::reserved_space() + SIZE_OF_REF_AND_TYPE) * 
self.len();
@@ -51,8 +51,8 @@ impl<T: Serializer + Eq + std::hash::Hash> Serializer for 
HashSet<T> {
         mem::size_of::<i32>()
     }
 
-    fn get_type_id(_fory: &Fory) -> i16 {
-        TypeId::SET.into()
+    fn get_type_id(_fory: &Fory) -> u32 {
+        TypeId::SET as u32
     }
 }
 
diff --git a/rust/fory-core/src/serializer/skip.rs 
b/rust/fory-core/src/serializer/skip.rs
new file mode 100644
index 000000000..54276c29f
--- /dev/null
+++ b/rust/fory-core/src/serializer/skip.rs
@@ -0,0 +1,106 @@
+// 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.
+
+use crate::ensure;
+use crate::error::Error;
+use crate::meta::NullableFieldType;
+use crate::resolver::context::ReadContext;
+use crate::serializer::Serializer;
+use crate::types::{RefFlag, TypeId, BASIC_TYPES, COLLECTION_TYPES};
+use anyhow::anyhow;
+use chrono::{NaiveDate, NaiveDateTime};
+
+macro_rules! basic_type_deserialize {
+    ($tid:expr, $context:expr; $(($ty:ty, $id:ident)),+ $(,)?) => {
+        $(
+            if $tid == TypeId::$id {
+                <$ty>::read($context)?;
+                return Ok(());
+            }
+        )+else {
+            unreachable!()
+        }
+    };
+}
+
+pub fn skip_field_value(
+    context: &mut ReadContext,
+    field_type: &NullableFieldType,
+) -> Result<(), Error> {
+    let ref_flag = context.reader.i8();
+    if field_type.nullable
+        && ref_flag != (RefFlag::NotNullValue as i8)
+        && ref_flag != (RefFlag::RefValue as i8)
+    {
+        return Ok(());
+    }
+    let type_id_num = context.reader.var_uint32();
+    match TypeId::try_from(type_id_num as i16) {
+        Ok(type_id) => {
+            let expected_type_id = field_type.type_id;
+            ensure!(
+                type_id_num == expected_type_id,
+                anyhow!("Invalid field type, expected:{expected_type_id}, 
actual:{type_id_num}")
+            );
+            if BASIC_TYPES.contains(&type_id) {
+                basic_type_deserialize!(type_id, context;
+                    (bool, BOOL),
+                    (i8, INT8),
+                    (i16, INT16),
+                    (i32, INT32),
+                    (i64, INT64),
+                    (f32, FLOAT32),
+                    (f64, FLOAT64),
+                    (String, STRING),
+                    (NaiveDate, LOCAL_DATE),
+                    (NaiveDateTime, TIMESTAMP),
+                );
+            } else if COLLECTION_TYPES.contains(&type_id) {
+                if type_id == TypeId::ARRAY || type_id == TypeId::SET {
+                    let length = context.reader.var_int32() as usize;
+                    for _ in 0..length {
+                        skip_field_value(context, 
field_type.generics.first().unwrap())?;
+                    }
+                } else if type_id == TypeId::MAP {
+                    let length = context.reader.var_int32() as usize;
+                    for _ in 0..length {
+                        skip_field_value(context, 
field_type.generics.first().unwrap())?;
+                        skip_field_value(context, 
field_type.generics.get(1).unwrap())?;
+                    }
+                }
+                Ok(())
+            } else {
+                unreachable!()
+            }
+        }
+        Err(_) => {
+            let tag = type_id_num & 0xff;
+            if tag == TypeId::STRUCT as u32 {
+                let type_def = context.get_meta_by_type_id(type_id_num);
+                let field_infos: Vec<_> = type_def.get_field_infos().to_vec();
+                for field_info in field_infos.iter() {
+                    let nullable_field_type =
+                        NullableFieldType::from(field_info.field_type.clone());
+                    skip_field_value(context, &nullable_field_type)?;
+                }
+            } else {
+                unimplemented!()
+            }
+            Ok(())
+        }
+    }
+}
diff --git a/rust/fory-core/src/serializer/string.rs 
b/rust/fory-core/src/serializer/string.rs
index 5c5c3d454..6b0a93582 100644
--- a/rust/fory-core/src/serializer/string.rs
+++ b/rust/fory-core/src/serializer/string.rs
@@ -38,8 +38,8 @@ impl Serializer for String {
         Ok(context.reader.string(len as usize))
     }
 
-    fn get_type_id(_fory: &Fory) -> i16 {
-        TypeId::STRING.into()
+    fn get_type_id(_fory: &Fory) -> u32 {
+        TypeId::STRING as u32
     }
 }
 
diff --git a/rust/fory-core/src/types.rs b/rust/fory-core/src/types.rs
index 6128447f0..cd11c1bb2 100644
--- a/rust/fory-core/src/types.rs
+++ b/rust/fory-core/src/types.rs
@@ -125,8 +125,23 @@ pub const BASIC_TYPES: [TypeId; 10] = [
     TypeId::TIMESTAMP,
 ];
 
+pub const BASIC_TYPE_NAMES: [&str; 10] = [
+    "bool",
+    "i8",
+    "i16",
+    "i32",
+    "i64",
+    "f32",
+    "f64",
+    "String",
+    "NaiveDate",
+    "NaiveDateTime",
+];
+
 pub const COLLECTION_TYPES: [TypeId; 3] = [TypeId::ARRAY, TypeId::SET, 
TypeId::MAP];
 
+pub const COLLECTION_TYPE_NAMES: [&str; 3] = ["Vec", "HashSet", "HashMap"];
+
 pub fn compute_field_hash(hash: u32, id: i16) -> u32 {
     let mut new_hash: u64 = (hash as u64) * 31 + (id as u64);
     while new_hash >= MAX_UNT32 {
diff --git a/rust/fory-derive/src/object/derive_enum.rs 
b/rust/fory-derive/src/object/derive_enum.rs
index a571dd441..32e91ea49 100644
--- a/rust/fory-derive/src/object/derive_enum.rs
+++ b/rust/fory-derive/src/object/derive_enum.rs
@@ -21,7 +21,7 @@ use syn::DataEnum;
 
 pub fn gen_type_def(_data_enum: &DataEnum) -> TokenStream {
     quote! {
-        fn type_def(fory: &fory_core::fory::Fory, type_id: i16) -> Vec<u8> {
+        fn type_def(fory: &fory_core::fory::Fory, type_id: u32) -> Vec<u8> {
             Vec::new()
         }
     }
diff --git a/rust/fory-derive/src/object/misc.rs 
b/rust/fory-derive/src/object/misc.rs
index 867dd255e..7840a801a 100644
--- a/rust/fory-derive/src/object/misc.rs
+++ b/rust/fory-derive/src/object/misc.rs
@@ -56,7 +56,7 @@ fn type_def(fields: &[&Field]) -> TokenStream {
         }
     });
     quote! {
-        fn type_def(fory: &fory_core::fory::Fory, layer_id: i16) -> Vec<u8> {
+        fn type_def(fory: &fory_core::fory::Fory, layer_id: u32) -> Vec<u8> {
             fory_core::meta::TypeMeta::from_fields(
                 layer_id,
                 vec![#(#field_infos),*]
@@ -76,8 +76,8 @@ pub fn gen_in_struct_impl(fields: &[&Field]) -> TokenStream {
 
 pub fn gen() -> TokenStream {
     quote! {
-            fn get_type_id(fory: &fory_core::fory::Fory) -> i16 {
-                
fory.get_type_resolver().get_type_info(std::any::TypeId::of::<Self>()).get_type_id()
 as i16
-            }
+        fn get_type_id(fory: &fory_core::fory::Fory) -> u32 {
+            
fory.get_type_resolver().get_type_info(std::any::TypeId::of::<Self>()).get_type_id()
+        }
     }
 }
diff --git a/rust/fory-derive/src/object/read.rs 
b/rust/fory-derive/src/object/read.rs
index 3678a2697..442040a51 100644
--- a/rust/fory-derive/src/object/read.rs
+++ b/rust/fory-derive/src/object/read.rs
@@ -17,15 +17,18 @@
 
 use proc_macro2::{Ident, TokenStream};
 use quote::{format_ident, quote};
-use syn::Field;
-use syn::Type;
+use syn::{Field, Type};
 
-use super::util::{generic_tree_to_tokens, parse_generic_tree};
+use super::util::{generic_tree_to_tokens, parse_generic_tree, 
NullableTypeNode};
 
 fn create_private_field_name(field: &Field) -> Ident {
     format_ident!("_{}", field.ident.as_ref().expect(""))
 }
 
+fn create_deserialize_nullable_fn_name(field: &Field) -> Ident {
+    format_ident!("_deserialize_nullable_{}", field.ident.as_ref().expect(""))
+}
+
 fn bind(fields: &[&Field]) -> Vec<TokenStream> {
     fields
         .iter()
@@ -70,16 +73,62 @@ fn read(fields: &[&Field]) -> TokenStream {
     }
 }
 
-fn deserialize_compatible(fields: &[&Field]) -> TokenStream {
+fn deserialize_compatible(_fields: &[&Field], struct_ident: &Ident) -> 
TokenStream {
+    quote! {
+        let ref_flag = context.reader.i8();
+        if ref_flag == (fory_core::types::RefFlag::NotNullValue as i8) || 
ref_flag == (fory_core::types::RefFlag::RefValue as i8) {
+            let type_id = context.reader.var_uint32();
+            #struct_ident::read_compatible(context, type_id)
+        } else if ref_flag == (fory_core::types::RefFlag::Null as i8) {
+            Err(fory_core::error::AnyhowError::msg("Try to deserialize 
non-option type to null"))?
+        } else if ref_flag == (fory_core::types::RefFlag::Ref as i8) {
+            Err(fory_core::error::Error::Ref)
+        } else {
+            Err(fory_core::error::AnyhowError::msg("Unknown ref flag, 
value:{ref_flag}"))?
+        }
+    }
+}
+
+fn deserialize_nullable(fields: &[&Field]) -> TokenStream {
+    let func_tokens: Vec<TokenStream> = fields
+        .iter()
+        .map(|field| {
+            let fn_name = create_deserialize_nullable_fn_name(field);
+            let ty = &field.ty;
+            let generic_tree = parse_generic_tree(ty);
+            let nullable_generic_tree = NullableTypeNode::from(generic_tree);
+            let deserialize_tokens = 
nullable_generic_tree.to_deserialize_tokens(&vec![]);
+            quote! {
+                fn #fn_name(
+                    context: &mut fory_core::resolver::context::ReadContext,
+                    local_nullable_type: &fory_core::meta::NullableFieldType,
+                    remote_nullable_type: &fory_core::meta::NullableFieldType
+                ) -> Result<#ty, fory_core::error::Error> {
+                    // println!("remote:{:#?}", remote_nullable_type);
+                    #deserialize_tokens
+                }
+            }
+        })
+        .collect::<Vec<_>>();
+    quote! {
+        #(#func_tokens)*
+    }
+}
+
+pub fn gen_nullable(fields: &[&Field]) -> TokenStream {
+    deserialize_nullable(fields)
+}
+
+pub fn gen_read_compatible(fields: &[&Field], struct_ident: &Ident) -> 
TokenStream {
     let pattern_items = fields.iter().map(|field| {
         let ty = &field.ty;
         let var_name = create_private_field_name(field);
+        let deserialize_nullable_fn_name = 
create_deserialize_nullable_fn_name(field);
 
         let generic_tree = parse_generic_tree(ty);
         let generic_token = generic_tree_to_tokens(&generic_tree, true);
 
         let field_name_str = field.ident.as_ref().unwrap().to_string();
-
         let base_ty = match &ty {
             Type::Path(type_path) => {
                 &type_path.path.segments.first().unwrap().ident
@@ -87,52 +136,62 @@ fn deserialize_compatible(fields: &[&Field]) -> 
TokenStream {
             _ => panic!("Unsupported type"),
         };
         quote! {
-            (ident, field_type)
-                if ident == #field_name_str
-                    && *field_type == #generic_token
-            => {
-                #var_name = Some(<#ty as 
fory_core::serializer::Serializer>::deserialize(context).unwrap_or_else(|_err| {
-                    // println!("skip deserialize {:?}", ident);
-                    #base_ty::default()
-                }));
+            if _field.field_name.as_str() == #field_name_str {
+                let local_field_type = #generic_token;
+                if &_field.field_type == &local_field_type {
+                    #var_name = Some(<#ty as 
fory_core::serializer::Serializer>::deserialize(context).unwrap_or_else(|_err| {
+                        // same type, err means something wrong
+                        panic!("Err at deserializing {:?}: {:?}", 
#field_name_str, _err);
+                    }));
+                } else {
+                    let local_nullable_type = 
fory_core::meta::NullableFieldType::from(local_field_type.clone());
+                    let remote_nullable_type = 
fory_core::meta::NullableFieldType::from(_field.field_type.clone());
+                    if local_nullable_type != remote_nullable_type {
+                        // set default and skip bytes
+                        println!("Type not match, just skip: {}", 
#field_name_str);
+                        fory_core::serializer::skip::skip_field_value(context, 
&remote_nullable_type).unwrap();
+                        #var_name = Some(#base_ty::default());
+                    } else {
+                        println!("Try to deserialize: {}", #field_name_str);
+                        #var_name = Some(
+                            #struct_ident::#deserialize_nullable_fn_name(
+                                context,
+                                &local_nullable_type,
+                                &remote_nullable_type
+                            ).unwrap_or_else(|_err| {
+                                // same nulable type, err means something wrong
+                                panic!("Err at deserializing {:?}: {:?}", 
#field_name_str, _err);
+                            })
+                        );
+                    }
+                }
             }
         }
     });
     let bind: Vec<TokenStream> = bind(fields);
     let create: Vec<TokenStream> = create(fields);
     quote! {
-        let ref_flag = context.reader.i8();
-        if ref_flag == (fory_core::types::RefFlag::NotNullValue as i8) || 
ref_flag == (fory_core::types::RefFlag::RefValue as i8) {
-            let meta_index = context.reader.i16() as usize;
-            let meta = context.get_meta(meta_index).clone();
+        fn read_compatible(context: &mut 
fory_core::resolver::context::ReadContext, type_id: u32) -> Result<Self, 
fory_core::error::Error> {
+            let meta = context.get_meta_by_type_id(type_id);
             let fields = meta.get_field_infos();
             #(#bind)*
             for _field in fields.iter() {
-                match (_field.field_name.as_str(), &_field.field_type) {
-                    #(#pattern_items),*
-                    _ => {
-                        // skip bytes
-                        println!("no need to deserialize {:?}:{:?}", 
_field.field_name.as_str(), _field.field_type);
-                        
fory_core::serializer::nonexistent::skip_field_value(context, 
&_field.field_type).unwrap();
-                    }
+                #(#pattern_items else)* {
+                    println!("skip {:?}:{:?}", _field.field_name.as_str(), 
_field.field_type);
+                    let nullable_field_type = 
fory_core::meta::NullableFieldType::from(_field.field_type.clone());
+                    fory_core::serializer::skip::skip_field_value(context, 
&nullable_field_type).unwrap();
                 }
             }
             Ok(Self {
                 #(#create),*
             })
-        } else if ref_flag == (fory_core::types::RefFlag::Null as i8) {
-            Err(fory_core::error::AnyhowError::msg("Try to deserialize 
non-option type to null"))?
-        } else if ref_flag == (fory_core::types::RefFlag::Ref as i8) {
-            Err(fory_core::error::Error::Ref)
-        } else {
-            Err(fory_core::error::AnyhowError::msg("Unknown ref flag, 
value:{ref_flag}"))?
         }
     }
 }
 
-pub fn gen(fields: &[&Field]) -> TokenStream {
+pub fn gen(fields: &[&Field], struct_ident: &Ident) -> TokenStream {
     let read_token_stream = read(fields);
-    let compatible_token_stream = deserialize_compatible(fields);
+    let compatible_token_stream = deserialize_compatible(fields, struct_ident);
 
     quote! {
         fn deserialize(context: &mut 
fory_core::resolver::context::ReadContext) -> Result<Self, 
fory_core::error::Error> {
diff --git a/rust/fory-derive/src/object/serializer.rs 
b/rust/fory-derive/src/object/serializer.rs
index a3be005d3..cf5195dbe 100644
--- a/rust/fory-derive/src/object/serializer.rs
+++ b/rust/fory-derive/src/object/serializer.rs
@@ -22,19 +22,29 @@ use quote::quote;
 
 pub fn derive_serializer(ast: &syn::DeriveInput) -> TokenStream {
     let name = &ast.ident;
-    let (type_def_token_stream, write_token_stream, read_token_stream) = match 
&ast.data {
+    let (
+        type_def_token_stream,
+        write_token_stream,
+        read_token_stream,
+        read_compatible_token_stream,
+        read_nullable_token_stream,
+    ) = match &ast.data {
         syn::Data::Struct(s) => {
             let fields = sorted_fields(&s.fields);
             (
                 misc::gen_in_struct_impl(&fields),
                 write::gen(&fields),
-                read::gen(&fields),
+                read::gen(&fields, name),
+                read::gen_read_compatible(&fields, name),
+                read::gen_nullable(&fields),
             )
         }
         syn::Data::Enum(s) => (
             derive_enum::gen_type_def(s),
             derive_enum::gen_write(s),
             derive_enum::gen_read(s),
+            quote! {},
+            quote! {},
         ),
         syn::Data::Union(_) => {
             panic!("Union is not supported")
@@ -53,6 +63,10 @@ pub fn derive_serializer(ast: &syn::DeriveInput) -> 
TokenStream {
             #write_token_stream
             #read_token_stream
         }
+        impl #name {
+            #read_compatible_token_stream
+            #read_nullable_token_stream
+        }
     };
     gen.into()
 }
diff --git a/rust/fory-derive/src/object/util.rs 
b/rust/fory-derive/src/object/util.rs
index 8981ddaee..93b2a4880 100644
--- a/rust/fory-derive/src/object/util.rs
+++ b/rust/fory-derive/src/object/util.rs
@@ -15,17 +15,288 @@
 // specific language governing permissions and limitations
 // under the License.
 
-use fory_core::types::TypeId;
+use fory_core::types::{TypeId, BASIC_TYPE_NAMES, COLLECTION_TYPE_NAMES};
 use proc_macro2::TokenStream;
 use quote::quote;
 use std::fmt;
-use syn::{GenericArgument, PathArguments, Type};
+use syn::{parse_str, GenericArgument, PathArguments, Type};
 
 pub(super) struct TypeNode {
     name: String,
     generics: Vec<TypeNode>,
 }
 
+pub(super) struct NullableTypeNode {
+    name: String,
+    generics: Vec<NullableTypeNode>,
+    nullable: bool,
+}
+
+macro_rules! basic_type_deserialize {
+    ($name:expr, $nullable:expr; $( ($ty_str:expr, $ty:ty) ),* $(,)?) => {
+        match $name {
+            $(
+                $ty_str => {
+                    if $nullable {
+                        quote! {
+                            let res1 = if cur_remote_nullable_type.nullable && 
ref_flag == (fory_core::types::RefFlag::Null as i8) {
+                                None
+                            } else {
+                                let _type_id = context.reader.var_uint32();
+                                Some(<$ty as 
fory_core::serializer::Serializer>::read(context)
+                                    .map_err(fory_core::error::Error::from)?)
+                            };
+                            Ok::<Option<$ty>, fory_core::error::Error>(res1)
+                        }
+                    } else {
+                        quote! {
+                            let res2 = if cur_remote_nullable_type.nullable && 
ref_flag == (fory_core::types::RefFlag::Null as i8) {
+                                $ty::default()
+                            } else {
+                                let _type_id = context.reader.var_uint32();
+                                <$ty as 
fory_core::serializer::Serializer>::read(context)
+                                    .map_err(fory_core::error::Error::from)?
+                            };
+                            Ok::<$ty, fory_core::error::Error>(res2)
+                        }
+                    }
+                }
+            )*
+            _ => unreachable!(),
+        }
+    };
+}
+
+impl NullableTypeNode {
+    pub(super) fn to_deserialize_tokens(&self, generic_path: &Vec<i8>) -> 
TokenStream {
+        let tokens = if BASIC_TYPE_NAMES.contains(&self.name.as_str()) {
+            basic_type_deserialize!(self.name.as_str(), self.nullable;
+                ("bool", bool),
+                ("i8", i8),
+                ("i16", i16),
+                ("i32", i32),
+                ("i64", i64),
+                ("f32", f32),
+                ("f64", f64),
+                ("String", String),
+                ("NaiveDate", chrono::NaiveDate),
+                ("NaiveDateTime", chrono::NaiveDateTime),
+            )
+        } else if COLLECTION_TYPE_NAMES.contains(&self.name.as_str()) {
+            let ty = parse_str::<Type>(&self.to_string()).unwrap();
+            let mut new_path = generic_path.clone();
+            match self.name.as_str() {
+                "Vec" => {
+                    let generic_node = self.generics.first().unwrap();
+                    new_path.push(0);
+                    let element_tokens = 
generic_node.to_deserialize_tokens(&new_path);
+                    let element_ty: Type = 
parse_str(&generic_node.to_string()).unwrap();
+                    if self.nullable {
+                        quote! {
+                            let v = if cur_remote_nullable_type.nullable && 
ref_flag == (fory_core::types::RefFlag::Null as i8) {
+                                None
+                            } else {
+                                let _arr_type_id = context.reader.var_uint32();
+                                let length = context.reader.var_int32() as 
usize;
+                                let mut v = Vec::with_capacity(length);
+                                for _ in 0..length {
+                                    let element: #element_ty = 
{#element_tokens}?;
+                                    v.push(element);
+                                }
+                                Some(v)
+                            };
+                            Ok::<#ty, fory_core::error::Error>(v)
+                        }
+                    } else {
+                        quote! {
+                            let v = if cur_remote_nullable_type.nullable && 
ref_flag == (fory_core::types::RefFlag::Null as i8) {
+                                Vec::default()
+                            } else {
+                                let _arr_type_id = context.reader.var_uint32();
+                                let length = context.reader.var_int32() as 
usize;
+                                let mut v = Vec::with_capacity(length);
+                                for _ in 0..length {
+                                    let element: #element_ty = 
{#element_tokens}?;
+                                    v.push(element);
+                                }
+                                v
+                            };
+                            Ok::<#ty, fory_core::error::Error>(v)
+                        }
+                    }
+                }
+                "HashSet" => {
+                    let generic_node = self.generics.first().unwrap();
+                    new_path.push(0);
+                    let element_tokens = 
generic_node.to_deserialize_tokens(&new_path);
+                    let element_ty: Type = 
parse_str(&generic_node.to_string()).unwrap();
+                    if self.nullable {
+                        quote! {
+                            let s = if cur_remote_nullable_type.nullable && 
ref_flag == (fory_core::types::RefFlag::Null as i8) {
+                                None
+                            } else {
+                                let _set_type_id = context.reader.var_uint32();
+                                let length = context.reader.var_int32() as 
usize;
+                                let mut s = HashSet::with_capacity(length);
+                                for _ in 0..length {
+                                    let element: #element_ty = 
{#element_tokens}?;
+                                    s.insert(element);
+                                }
+                                Some(s)
+                            };
+                            Ok::<#ty, fory_core::error::Error>(s)
+                        }
+                    } else {
+                        quote! {
+                            let s = if cur_remote_nullable_type.nullable && 
ref_flag == (fory_core::types::RefFlag::Null as i8) {
+                                HashSet::default()
+                            } else {
+                                let _set_type_id = context.reader.var_uint32();
+                                let length = context.reader.var_int32() as 
usize;
+                                let mut s = HashSet::with_capacity(length);
+                                for _ in 0..length {
+                                    let element: #element_ty = 
{#element_tokens}?;
+                                    s.insert(element);
+                                }
+                                s
+                            };
+                            Ok::<#ty, fory_core::error::Error>(s)
+                        }
+                    }
+                }
+                "HashMap" => {
+                    let key_generic_node = self.generics.first().unwrap();
+                    let val_generic_node = self.generics.get(1).unwrap();
+                    new_path.push(0);
+                    let key_tokens = 
key_generic_node.to_deserialize_tokens(&new_path);
+                    new_path.pop();
+                    new_path.push(1);
+                    let val_tokens = 
val_generic_node.to_deserialize_tokens(&new_path);
+                    let key_ty: Type = 
parse_str(&key_generic_node.to_string()).unwrap();
+                    let val_ty: Type = 
parse_str(&val_generic_node.to_string()).unwrap();
+                    if self.nullable {
+                        quote! {
+                            let m = if cur_remote_nullable_type.nullable && 
ref_flag == (fory_core::types::RefFlag::Null as i8) {
+                                None
+                            } else {
+                                let _map_type_id = context.reader.var_uint32();
+                                let length = context.reader.var_int32() as 
usize;
+                                let mut m = HashMap::with_capacity(length);
+                                for _ in 0..length {
+                                    let key: #key_ty = {#key_tokens}?;
+                                    let value: #val_ty = {#val_tokens}?;
+                                    m.insert(key, value);
+                                }
+                                Some(m)
+                            };
+                            Ok::<#ty, fory_core::error::Error>(m)
+                        }
+                    } else {
+                        quote! {
+                            let m = if cur_remote_nullable_type.nullable && 
ref_flag == (fory_core::types::RefFlag::Null as i8) {
+                                HashMap::default()
+                            } else {
+                                let _map_type_id = context.reader.var_uint32();
+                                let length = context.reader.var_int32() as 
usize;
+                                let mut m = HashMap::with_capacity(length);
+                                for _ in 0..length {
+                                    let key: #key_ty = {#key_tokens}?;
+                                    let value: #val_ty = {#val_tokens}?;
+                                    m.insert(key, value);
+                                }
+                                m
+                            };
+                            Ok::<#ty, fory_core::error::Error>(m)
+                        }
+                    }
+                }
+                _ => quote! { compile_error!("Unsupported type for 
collection"); },
+            }
+        } else {
+            // struct
+            let nullable_ty = 
parse_str::<Type>(&self.nullable_ty_string()).unwrap();
+            let ty = parse_str::<Type>(&self.to_string()).unwrap();
+            if self.nullable {
+                quote! {
+                    let res1 = if cur_remote_nullable_type.nullable && 
ref_flag == (fory_core::types::RefFlag::Null as i8) {
+                        None
+                    } else {
+                        let type_id = context.reader.var_uint32();
+                        let internal_id = type_id & 0xff;
+                        assert_eq!(internal_id as i16, 
fory_core::types::TypeId::STRUCT as i16);
+                        Some(#nullable_ty::read_compatible(context, type_id)
+                                    .map_err(fory_core::error::Error::from)?)
+                    };
+                    Ok::<#ty, fory_core::error::Error>(res1)
+                }
+            } else {
+                quote! {
+                    let res2 = if cur_remote_nullable_type.nullable && 
ref_flag == (fory_core::types::RefFlag::Null as i8) {
+                        #ty::default()
+                    } else {
+                        let type_id = context.reader.var_uint32();
+                        let internal_id = type_id & 0xff;
+                        assert_eq!(internal_id as i16, 
fory_core::types::TypeId::STRUCT as i16);
+                        <#nullable_ty>::read_compatible(context, type_id)
+                                .map_err(fory_core::error::Error::from)?
+                    };
+                    Ok::<#ty, fory_core::error::Error>(res2)
+                }
+            }
+        };
+        let mut cur_remote_nullable_type = quote! { remote_nullable_type };
+        for idx in generic_path {
+            cur_remote_nullable_type = quote! {
+                #cur_remote_nullable_type.generics.get(#idx as usize).unwrap()
+            };
+        }
+        quote! {
+            let cur_remote_nullable_type = &#cur_remote_nullable_type;
+            let ref_flag = context.reader.i8();
+            #tokens
+        }
+    }
+
+    pub(super) fn from(node: TypeNode) -> Self {
+        if node.name == "Option" {
+            let inner = 
NullableTypeNode::from(node.generics.into_iter().next().unwrap());
+            NullableTypeNode {
+                name: inner.name,
+                generics: inner.generics,
+                nullable: true,
+            }
+        } else {
+            let generics = node
+                .generics
+                .into_iter()
+                .map(NullableTypeNode::from)
+                .collect();
+
+            NullableTypeNode {
+                name: node.name,
+                generics,
+                nullable: false,
+            }
+        }
+    }
+
+    pub(super) fn nullable_ty_string(&self) -> String {
+        if self.generics.is_empty() {
+            self.name.clone()
+        } else {
+            format!(
+                "{}<{}>",
+                self.name,
+                self.generics
+                    .iter()
+                    .map(|g| g.to_string())
+                    .collect::<Vec<_>>()
+                    .join(",")
+            )
+        }
+    }
+}
+
 impl fmt::Display for TypeNode {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         if self.generics.is_empty() {
@@ -45,6 +316,30 @@ impl fmt::Display for TypeNode {
     }
 }
 
+impl fmt::Display for NullableTypeNode {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let inner_type = if self.generics.is_empty() {
+            self.name.clone()
+        } else {
+            format!(
+                "{}<{}>",
+                self.name,
+                self.generics
+                    .iter()
+                    .map(|g| g.to_string())
+                    .collect::<Vec<_>>()
+                    .join(",")
+            )
+        };
+
+        if self.nullable {
+            write!(f, "Option<{}>", inner_type)
+        } else {
+            write!(f, "{}", inner_type)
+        }
+    }
+}
+
 fn extract_type_name(ty: &Type) -> String {
     if let Type::Path(type_path) = ty {
         type_path.path.segments.last().unwrap().ident.to_string()
@@ -80,6 +375,11 @@ pub(super) fn parse_generic_tree(ty: &Type) -> TypeNode {
 }
 
 pub(super) fn generic_tree_to_tokens(node: &TypeNode, have_context: bool) -> 
TokenStream {
+    if node.name == "Option" && node.generics.first().unwrap().name == 
"Option" {
+        return quote! {
+            compile_error!("adjacent Options are not supported");
+        };
+    }
     let children_tokens: Vec<TokenStream> = node
         .generics
         .iter()
@@ -96,7 +396,7 @@ pub(super) fn generic_tree_to_tokens(node: &TypeNode, 
have_context: bool) -> Tok
         }
     };
     let get_type_id = if node.name == "Option" {
-        let option_type_id: i16 = TypeId::ForyOption.into();
+        let option_type_id = TypeId::ForyOption as u32;
         quote! {
             #option_type_id
         }
diff --git a/rust/fory-derive/src/object/write.rs 
b/rust/fory-derive/src/object/write.rs
index 44ec8cd2a..05c40de7d 100644
--- a/rust/fory-derive/src/object/write.rs
+++ b/rust/fory-derive/src/object/write.rs
@@ -44,10 +44,8 @@ pub fn gen(fields: &[&Field]) -> TokenStream {
                 },
                 fory_core::types::Mode::Compatible => {
                     context.writer.i8(fory_core::types::RefFlag::NotNullValue 
as i8);
-                    let meta_index = context.push_meta(
-                            std::any::TypeId::of::<Self>()
-                        ) as i16;
-                    context.writer.i16(meta_index);
+                    let type_id = Self::get_type_id(context.get_fory());
+                    context.writer.var_uint32(type_id);
                     self.write(context);
                 }
             }
@@ -55,6 +53,9 @@ pub fn gen(fields: &[&Field]) -> TokenStream {
 
 
         fn write(&self, context: &mut 
fory_core::resolver::context::WriteContext) {
+            let _meta_index = context.push_meta(
+                std::any::TypeId::of::<Self>()
+            ) as i16;
             // write fields
             #(#accessor_expr)*
         }
diff --git a/rust/tests/tests/test_buffer.rs b/rust/tests/tests/test_buffer.rs
index e1dcdb887..16370406b 100644
--- a/rust/tests/tests/test_buffer.rs
+++ b/rust/tests/tests/test_buffer.rs
@@ -48,4 +48,12 @@ fn test_var_int32() {
         let res = reader.var_int32();
         assert_eq!(res, data);
     }
+    for &data in &test_data {
+        let mut writer = Writer::default();
+        writer.var_uint32(data as u32);
+        let binding = writer.dump();
+        let mut reader = Reader::new(binding.as_slice());
+        let res = reader.var_uint32();
+        assert_eq!(res, data as u32);
+    }
 }
diff --git a/rust/tests/tests/test_compatible.rs 
b/rust/tests/tests/test_compatible.rs
index d9d94d72b..9bd1263a0 100644
--- a/rust/tests/tests/test_compatible.rs
+++ b/rust/tests/tests/test_compatible.rs
@@ -18,8 +18,7 @@
 use fory_core::fory::Fory;
 use fory_core::types::Mode::Compatible;
 use fory_derive::Fory;
-use std::collections::HashMap;
-
+use std::collections::{HashMap, HashSet};
 // RUSTFLAGS="-Awarnings" cargo expand -p fory-tests --test test_compatible
 #[test]
 fn simple() {
@@ -46,7 +45,6 @@ fn simple() {
         f7: i16,
         f8: i8,
     }
-
     let mut fory1 = Fory::default().mode(Compatible);
     let mut fory2 = Fory::default().mode(Compatible);
     fory1.register::<Animal1>(999);
@@ -72,6 +70,38 @@ fn simple() {
     assert_eq!(animal.f8, obj.f8);
 }
 
+#[test]
+fn skip_option() {
+    #[derive(Fory, Debug)]
+    struct Item1 {
+        f1: Option<String>,
+        f2: Option<String>,
+        last: i64,
+    }
+
+    #[derive(Fory, Debug)]
+    struct Item2 {
+        f1: i8,
+        f2: i8,
+        last: i64,
+    }
+    let mut fory1 = Fory::default().mode(Compatible);
+    let mut fory2 = Fory::default().mode(Compatible);
+    fory1.register::<Item1>(999);
+    fory2.register::<Item2>(999);
+    let item1 = Item1 {
+        f1: None,
+        f2: Some(String::from("f2")),
+        last: 42,
+    };
+    let bin = fory1.serialize(&item1);
+    let item2: Item2 = fory2.deserialize(&bin).unwrap();
+
+    assert_eq!(item2.f1, i8::default());
+    assert_eq!(item2.f2, i8::default());
+    assert_eq!(item2.last, item1.last)
+}
+
 #[test]
 fn nonexistent_struct() {
     #[derive(Fory, Debug, Default)]
@@ -86,13 +116,13 @@ fn nonexistent_struct() {
     struct Person1 {
         f2: Item1,
         f3: i8,
-        f4: String,
+        last: String,
     }
     #[derive(Fory, Debug)]
     struct Person2 {
         f2: Item2,
         f3: i64,
-        f4: String,
+        last: String,
     }
     let mut fory1 = Fory::default().mode(Compatible);
     let mut fory2 = Fory::default().mode(Compatible);
@@ -103,13 +133,13 @@ fn nonexistent_struct() {
     let person = Person1 {
         f2: Item1 { f1: 42 },
         f3: 24,
-        f4: String::from("foo"),
+        last: String::from("foo"),
     };
     let bin = fory1.serialize(&person);
     let obj: Person2 = fory2.deserialize(&bin).unwrap();
     assert_eq!(obj.f2, Item2::default());
     assert_eq!(obj.f3, i64::default());
-    assert_eq!(obj.f4, person.f4);
+    assert_eq!(obj.last, person.last);
 }
 
 #[test]
@@ -122,6 +152,7 @@ fn option() {
         // adjacent Options are not supported
         // f4: Option<Option<String>>,
         f5: Vec<Option<Vec<Option<String>>>>,
+        last: i64,
     }
     let mut fory = Fory::default().mode(Compatible);
     fory.register::<Animal>(999);
@@ -131,12 +162,217 @@ fn option() {
         f3: vec![Option::<String>::None, Some(String::from("f3"))],
         // f4: Some(Some(String::from("f4"))),
         f5: vec![Some(vec![Some(String::from("f1"))])],
+        last: 666,
     };
     let bin = fory.serialize(&animal);
     let obj: Animal = fory.deserialize(&bin).unwrap();
     assert_eq!(animal, obj);
 }
 
+#[test]
+fn nullable() {
+    /*
+        f1: value -> value
+        f2: value -> Option(value)
+        f3: Option(value) -> value
+        f4: Option(value) -> Option(value)
+        f5: Option(None) -> Option(None)
+        f6: Option(None) -> value_default
+    */
+    #[derive(Fory, Debug, Default)]
+    pub struct Item1 {
+        f2: i8,
+        f3: Option<i8>,
+        f4: Option<i8>,
+        f5: Option<i8>,
+        f6: Option<i8>,
+        last: i64,
+    }
+
+    #[derive(Fory, Debug, Default)]
+    pub struct Item2 {
+        f2: Option<i8>,
+        f3: i8,
+        f4: Option<i8>,
+        f5: Option<i8>,
+        f6: i8,
+        last: i64,
+    }
+
+    let mut fory1 = Fory::default().mode(Compatible);
+    let mut fory2 = Fory::default().mode(Compatible);
+    fory1.register::<Item1>(999);
+    fory2.register::<Item2>(999);
+
+    let item1 = Item1 {
+        f2: 43,
+        f3: Some(44),
+        f4: Some(45),
+        f5: None,
+        f6: None,
+        last: 666,
+    };
+
+    let bin = fory1.serialize(&item1);
+    let item2: Item2 = fory2.deserialize(&bin).unwrap();
+    assert_eq!(item2.f2.unwrap(), item1.f2);
+    assert_eq!(item2.f3, item1.f3.unwrap());
+    assert_eq!(item2.f4, item1.f4);
+    assert_eq!(item2.f5, item1.f5);
+    assert_eq!(item2.f6, i8::default());
+    assert_eq!(item2.last, item1.last);
+}
+
+#[test]
+fn nullable_collection() {
+    #[derive(Fory, Debug, Default)]
+    pub struct Item1 {
+        f1: Vec<i8>,
+        f2: Option<Vec<i8>>,
+        f3: HashSet<i8>,
+        f4: Option<HashSet<i8>>,
+        f5: HashMap<i8, Vec<i8>>,
+        f6: Option<HashMap<i8, Vec<i8>>>,
+        f7: Option<Vec<i8>>,
+        f8: Option<HashSet<i8>>,
+        f9: Option<HashMap<i8, i8>>,
+        last: i64,
+    }
+
+    #[derive(Fory, Debug, Default)]
+    pub struct Item2 {
+        f1: Option<Vec<i8>>,
+        f2: Vec<i8>,
+        f3: Option<HashSet<i8>>,
+        f4: HashSet<i8>,
+        f5: Option<HashMap<i8, Vec<i8>>>,
+        f6: HashMap<i8, Vec<i8>>,
+        f7: Vec<i8>,
+        f8: HashSet<i8>,
+        f9: HashMap<i8, i8>,
+        last: i64,
+    }
+
+    let mut fory1 = Fory::default().mode(Compatible);
+    let mut fory2 = Fory::default().mode(Compatible);
+    fory1.register::<Item1>(999);
+    fory2.register::<Item2>(999);
+
+    let item1 = Item1 {
+        f1: vec![44, 45],
+        f2: Some(vec![43]),
+        f3: HashSet::from([44, 45]),
+        f4: Some(HashSet::from([46, 47])),
+        f5: HashMap::from([(48, vec![49])]),
+        f6: Some(HashMap::from([(48, vec![49])])),
+        f7: None,
+        f8: None,
+        f9: None,
+        last: 666,
+    };
+
+    let bin = fory1.serialize(&item1);
+    let item2: Item2 = fory2.deserialize(&bin).unwrap();
+
+    assert_eq!(item2.f1.unwrap(), item1.f1);
+    assert_eq!(item2.f2, item1.f2.unwrap());
+    assert_eq!(item2.f3.unwrap(), item1.f3);
+    assert_eq!(item2.f4, item1.f4.unwrap());
+    assert_eq!(item2.f5.unwrap(), item1.f5);
+    assert_eq!(item2.f6, item1.f6.unwrap());
+    assert_eq!(item2.f7, Vec::default());
+    assert_eq!(item2.f8, HashSet::default());
+    assert_eq!(item2.f9, HashMap::default());
+    assert_eq!(item2.last, item1.last);
+}
+
+#[test]
+fn inner_nullable() {
+    #[derive(Fory, Debug, Default)]
+    pub struct Item1 {
+        f1: Vec<Option<i8>>,
+        f2: HashSet<Option<i8>>,
+        f3: HashMap<i8, Option<i8>>,
+    }
+
+    #[derive(Fory, Debug, Default)]
+    pub struct Item2 {
+        f1: Vec<i8>,
+        f2: HashSet<i8>,
+        f3: HashMap<i8, i8>,
+    }
+    let mut fory1 = Fory::default().mode(Compatible);
+    let mut fory2 = Fory::default().mode(Compatible);
+    fory1.register::<Item1>(999);
+    fory2.register::<Item2>(999);
+
+    let item1 = Item1 {
+        f1: vec![None, Some(42)],
+        f2: HashSet::from([None, Some(43)]),
+        f3: HashMap::from([(44, None), (45, Some(46))]),
+    };
+    let bin = fory1.serialize(&item1);
+    let item2: Item2 = fory2.deserialize(&bin).unwrap();
+
+    assert_eq!(item2.f1, vec![0, 42]);
+    assert_eq!(item2.f2, HashSet::from([0, 43]));
+    assert_eq!(item2.f3, HashMap::from([(44, 0), (45, 46)]));
+}
+
+#[test]
+fn nullable_struct() {
+    #[derive(Fory, Debug, Default, PartialEq)]
+    pub struct Item {
+        name: String,
+        data: Vec<Option<i8>>,
+        last: i64,
+    }
+
+    #[derive(Fory, Debug, Default)]
+    pub struct Person1 {
+        f1: Item,
+        f2: Option<Item>,
+        f3: Option<Item>,
+        last: i64,
+    }
+
+    #[derive(Fory, Debug, Default)]
+    pub struct Person2 {
+        f1: Option<Item>,
+        f2: Item,
+        f3: Item,
+        last: i64,
+    }
+    let mut fory1 = Fory::default().mode(Compatible);
+    let mut fory2 = Fory::default().mode(Compatible);
+    fory1.register::<Item>(199);
+    fory1.register::<Person1>(200);
+    fory2.register::<Item>(199);
+    fory2.register::<Person2>(200);
+
+    let person1 = Person1 {
+        f1: Item {
+            name: "f1".to_string(),
+            data: vec![None, Some(42)],
+            last: 43,
+        },
+        f2: None,
+        f3: Some(Item {
+            name: "f3".to_string(),
+            data: vec![None, Some(44)],
+            last: 45,
+        }),
+        last: 46,
+    };
+    let bin = fory1.serialize(&person1);
+    let person2: Person2 = fory2.deserialize(&bin).unwrap();
+
+    assert_eq!(person2.f1.unwrap(), person1.f1);
+    assert_eq!(person2.f2, Item::default());
+    assert_eq!(person2.f3, person1.f3.unwrap());
+    assert_eq!(person2.last, person1.last);
+}
+
 // #[test]
 // fn not_impl_default() {
 //     #[derive(Fory, Debug)]
diff --git a/rust/tests/tests/test_complex_struct.rs 
b/rust/tests/tests/test_complex_struct.rs
index 461233c30..140154ca0 100644
--- a/rust/tests/tests/test_complex_struct.rs
+++ b/rust/tests/tests/test_complex_struct.rs
@@ -15,10 +15,10 @@
 // specific language governing permissions and limitations
 // under the License.
 
-use chrono::{DateTime, NaiveDate, NaiveDateTime};
 use fory_core::fory::Fory;
 use fory_derive::Fory;
 // use std::any::Any;
+use chrono::{DateTime, NaiveDate, NaiveDateTime};
 use std::collections::HashMap;
 
 // RUSTFLAGS="-Awarnings" cargo expand -p fory-tests --test test_complex_struct


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

Reply via email to