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 3ee85f4c5 feat(Rust): sort fields && feat Enum && fix read/write 
type_info && fix type_meta en/decode (#2630)
3ee85f4c5 is described below

commit 3ee85f4c54a7f48330edf2d78b64daa5b3ce22b3
Author: urlyy <[email protected]>
AuthorDate: Fri Sep 19 18:36:05 2025 +0800

    feat(Rust): sort fields && feat Enum && fix read/write type_info && fix 
type_meta en/decode (#2630)
    
    ## What does this PR do?
    1. Sort fields both at compile-time and runtime.Add a `fn
    get_sorted_field_names(fory: &Fory) -> Vec<String>;` to get
    T::sorted_field_names, and use these sorted_names to guide read/write
    order at runtime. Sort `primitive/nullable_ primitive/container` fields
    at compile-time, and classify `enum/struct` and sort `final/other`
    fields at runtime. And it will `conditionally render code`(when a
    field_group is empty, will not render). What's more,
    `get_sorted_field_names()` caches results based on `TypeId`.
    
    2. Feat Enum. Fulfill the code in `derive_enum.rs` and add unit test
    with java.
    
    3. Fix read/write type_info. The `serialize()` function only deals with
    the `is_field` parameter and the `ref_flag`. Reading and writing
    `type_id` and `meta_share` happens inside `read()` and `write()`. What
    these functions do depends on the type `T` and whether `is_field` is
    set. For structs, no matter if they’re the outer object, a field, or an
    element in a container, they always write `type_id` and `meta_index`.
    For other types(include container and enums), `type_id` is only read or
    written when they are the outer object. If they show up as a field,
    `type_id` is skipped.
    
    4. Fix type_meta en/decode. In previous versions, the execution order
    for taking the absolute value of `global_hash` was incorrect. And Some
    calls to `var_int()` have been changed to `var_uint()`.
    
    5. Add some unit tests.
    
    ## Does this PR introduce any user-facing change?
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
---
 .../test/java/org/apache/fory/RustXlangTest.java   |  65 +++-
 rust/fory-core/src/fory.rs                         |  51 ++-
 rust/fory-core/src/meta/type_meta.rs               |  51 ++-
 rust/fory-core/src/resolver/context.rs             |  21 +-
 rust/fory-core/src/resolver/meta_resolver.rs       |   6 +-
 rust/fory-core/src/resolver/type_resolver.rs       |  19 +-
 rust/fory-core/src/serializer/bool.rs              |  13 +-
 rust/fory-core/src/serializer/collection.rs        |  36 +-
 rust/fory-core/src/serializer/datetime.rs          |  24 +-
 rust/fory-core/src/serializer/list.rs              |   8 +-
 rust/fory-core/src/serializer/map.rs               |  24 +-
 rust/fory-core/src/serializer/mod.rs               |  53 +--
 rust/fory-core/src/serializer/number.rs            |  11 +-
 rust/fory-core/src/serializer/option.rs            |  61 +---
 rust/fory-core/src/serializer/primitive_list.rs    |  11 +-
 rust/fory-core/src/serializer/set.rs               |   8 +-
 rust/fory-core/src/serializer/skip.rs              |  64 ++--
 rust/fory-core/src/serializer/string.rs            |  13 +-
 rust/fory-core/src/types.rs                        |  15 +-
 rust/fory-derive/src/object/derive_enum.rs         |  30 +-
 rust/fory-derive/src/object/misc.rs                |  36 +-
 rust/fory-derive/src/object/read.rs                | 107 ++++--
 rust/fory-derive/src/object/serializer.rs          |  14 +-
 rust/fory-derive/src/object/util.rs                | 373 +++++++++++++++++++--
 rust/fory-derive/src/object/write.rs               |  75 +++--
 rust/tests/tests/test_compatible.rs                | 205 ++++++-----
 rust/tests/tests/test_cross_language.rs            |  80 +++--
 27 files changed, 1038 insertions(+), 436 deletions(-)

diff --git a/java/fory-core/src/test/java/org/apache/fory/RustXlangTest.java 
b/java/fory-core/src/test/java/org/apache/fory/RustXlangTest.java
index 12711c34d..04a58c75d 100644
--- a/java/fory-core/src/test/java/org/apache/fory/RustXlangTest.java
+++ b/java/fory-core/src/test/java/org/apache/fory/RustXlangTest.java
@@ -328,8 +328,22 @@ public class RustXlangTest extends ForyTestBase {
     }
   }
 
+  enum Color {
+    Green,
+    Red,
+    Blue,
+    White,
+  }
+
   private void testCrossLanguageSerializer(Language language, List<String> 
command)
       throws Exception {
+    List<String> strList = Arrays.asList("hello", "world");
+    Set<String> strSet = new HashSet<>(strList);
+    Map<String, String> strMap = new HashMap();
+    strMap.put("hello", "world");
+    strMap.put("foo", "bar");
+    Color color = Color.White;
+
     Fory fory =
         Fory.builder()
             .withLanguage(Language.XLANG)
@@ -338,6 +352,7 @@ public class RustXlangTest extends ForyTestBase {
             .withCompatibleMode(CompatibleMode.COMPATIBLE)
             .withWriteNumUtf16BytesForUtf8Encoding(false)
             .build();
+    fory.register(Color.class, 101);
     MemoryBuffer buffer = MemoryUtils.buffer(32);
     fory.serialize(buffer, true);
     fory.serialize(buffer, false);
@@ -364,15 +379,10 @@ public class RustXlangTest extends ForyTestBase {
     fory.serialize(buffer, new long[] {1, Long.MAX_VALUE});
     fory.serialize(buffer, new float[] {1.f, 2.f});
     fory.serialize(buffer, new double[] {1.0, 2.0});
-
-    List<String> strList = Arrays.asList("hello", "world");
     fory.serialize(buffer, strList);
-    Set<String> strSet = new HashSet<>(strList);
     fory.serialize(buffer, strSet);
-    HashMap<String, Integer> strMap = new HashMap();
-    strMap.put("hello", 42);
-    strMap.put("world", 666);
     fory.serialize(buffer, strMap);
+    fory.serialize(buffer, color);
     //    Map<Object, Object> map = new HashMap<>();
     //    for (int i = 0; i < list.size(); i++) {
     //        map.put("k" + i, list.get(i));
@@ -411,6 +421,7 @@ public class RustXlangTest extends ForyTestBase {
           assertStringEquals(fory.deserialize(buf), strList, useToString);
           assertStringEquals(fory.deserialize(buf), strSet, useToString);
           assertStringEquals(fory.deserialize(buf), strMap, useToString);
+          assertStringEquals(fory.deserialize(buf), color, useToString);
           //            assertStringEquals(fory.deserialize(buf), list, 
useToString);
           //            assertStringEquals(fory.deserialize(buf), map, 
useToString);
           //            assertStringEquals(fory.deserialize(buf), set, 
useToString);
@@ -424,9 +435,18 @@ public class RustXlangTest extends ForyTestBase {
     function.accept(buffer2, true);
   }
 
+  @Data
+  static class Item {
+    String name;
+  }
+
   @Data
   static class SimpleStruct {
-    int f2;
+    //        int f2;
+    Item f3;
+    String f4;
+    Color f5;
+    //        int last;
   }
 
   private void testSimpleStruct(Language language, List<String> command)
@@ -435,21 +455,30 @@ public class RustXlangTest extends ForyTestBase {
         Fory.builder()
             .withLanguage(Language.XLANG)
             .withRefTracking(false)
-            .requireClassRegistration(false)
             .withCompatibleMode(CompatibleMode.COMPATIBLE)
+            .withCodegen(false)
+            .withStringCompressed(true)
+            .withWriteNumUtf16BytesForUtf8Encoding(false)
             .build();
-    fory.register(SimpleStruct.class, 100);
+    fory.register(Color.class, 101);
+    fory.register(Item.class, 102);
+    fory.register(SimpleStruct.class, 103);
+    Item item = new Item();
+    item.name = "item";
     SimpleStruct obj = new SimpleStruct();
-    obj.f2 = 20;
+    //    obj.f2 = 10;
+    obj.f3 = item;
+    obj.f4 = "f3";
+    obj.f5 = Color.White;
+    //    obj.last = 42;
     byte[] serialized = fory.serialize(obj);
-    //      Assert.assertEquals(fory.deserialize(serialized), obj);
-    //        System.out.println(Arrays.toString(serialized));
-    //        Path dataFile = Files.createTempFile("test_simple_struct", 
"data");
-    //        Pair<Map<String,String>, File> env_workdir = 
setFilePath(language, command, dataFile,
-    //    serialized);
-    //        Assert.assertTrue(executeCommand(command, 30, 
env_workdir.getLeft(),
-    //    env_workdir.getRight()));
-    //            
Assert.assertEquals(fory.deserialize(Files.readAllBytes(dataFile)), obj);
+    Assert.assertEquals(fory.deserialize(serialized), obj);
+    System.out.println(Arrays.toString(serialized));
+    Path dataFile = Files.createTempFile("test_simple_struct", "data");
+    Pair<Map<String, String>, File> env_workdir =
+        setFilePath(language, command, dataFile, serialized);
+    Assert.assertTrue(executeCommand(command, 30, env_workdir.getLeft(), 
env_workdir.getRight()));
+    Assert.assertEquals(fory.deserialize(Files.readAllBytes(dataFile)), obj);
   }
 
   /**
diff --git a/rust/fory-core/src/fory.rs b/rust/fory-core/src/fory.rs
index 9a974ff79..baa0808a3 100644
--- a/rust/fory-core/src/fory.rs
+++ b/rust/fory-core/src/fory.rs
@@ -23,6 +23,7 @@ use crate::resolver::context::ReadContext;
 use crate::resolver::context::WriteContext;
 use crate::resolver::type_resolver::{TypeInfo, TypeResolver};
 use crate::serializer::{Serializer, StructSerializer};
+use crate::types::config_flags::IS_NULL_FLAG;
 use crate::types::{
     config_flags::{IS_CROSS_LANGUAGE_FLAG, IS_LITTLE_ENDIAN_FLAG},
     Language, Mode, MAGIC_NUMBER, SIZE_OF_REF_AND_TYPE,
@@ -60,7 +61,7 @@ impl Fory {
         &self.mode
     }
 
-    pub fn write_head<T: Serializer>(&self, writer: &mut Writer) {
+    pub fn write_head<T: Serializer>(&self, is_none: bool, writer: &mut 
Writer) {
         const HEAD_SIZE: usize = 10;
         writer.reserve(<T as Serializer>::reserved_space() + 
SIZE_OF_REF_AND_TYPE + HEAD_SIZE);
         if self.xlang {
@@ -73,13 +74,19 @@ impl Fory {
         if self.xlang {
             bitmap |= IS_CROSS_LANGUAGE_FLAG;
         }
+        if is_none {
+            bitmap |= IS_NULL_FLAG;
+        }
         writer.u8(bitmap);
+        if is_none {
+            return;
+        }
         if self.xlang {
             writer.u8(Language::Rust as u8);
         }
     }
 
-    fn read_head(&self, reader: &mut Reader) -> Result<(), Error> {
+    fn read_head(&self, reader: &mut Reader) -> Result<bool, Error> {
         if self.xlang {
             let magic_numer = reader.u16();
             ensure!(
@@ -93,6 +100,11 @@ impl Fory {
             )
         }
         let bitmap = reader.u8();
+        let peer_is_xlang = (bitmap & IS_CROSS_LANGUAGE_FLAG) != 0;
+        ensure!(
+            self.xlang == peer_is_xlang,
+            anyhow!("header bitmap mismatch at xlang bit")
+        );
         let is_little_endian = (bitmap & IS_LITTLE_ENDIAN_FLAG) != 0;
         ensure!(
             is_little_endian,
@@ -100,11 +112,14 @@ impl Fory {
                 "Big endian is not supported for now, please ensure peer 
machine is little endian."
             )
         );
-        let peer_is_xlang = (bitmap & IS_CROSS_LANGUAGE_FLAG) != 0;
+        let is_none = (bitmap & IS_NULL_FLAG) != 0;
+        if is_none {
+            return Ok(true);
+        }
         if peer_is_xlang {
             let _peer_lang = reader.u8();
         }
-        Ok(())
+        Ok(false)
     }
 
     pub fn deserialize<T: Serializer>(&self, bf: &[u8]) -> Result<T, Error> {
@@ -117,14 +132,17 @@ impl Fory {
         &self,
         context: &mut ReadContext,
     ) -> Result<T, Error> {
-        self.read_head(&mut context.reader)?;
+        let is_none = self.read_head(&mut context.reader)?;
+        if is_none {
+            return Ok(T::default());
+        }
         if self.mode == Mode::Compatible {
             let meta_offset = context.reader.i32();
             if meta_offset != -1 {
                 context.load_meta(meta_offset as usize);
             }
         }
-        <T as Serializer>::deserialize(context)
+        <T as Serializer>::deserialize(context, false)
     }
 
     pub fn serialize<T: Serializer>(&self, record: &T) -> Vec<u8> {
@@ -138,16 +156,17 @@ impl Fory {
         record: &T,
         context: &mut WriteContext,
     ) -> Vec<u8> {
-        let mut meta_offset = 0;
-        self.write_head::<T>(context.writer);
-        if self.mode == Mode::Compatible {
-            context.writer.i32(-1);
-            meta_offset = context.writer.len() - 4;
-        }
-        <T as Serializer>::serialize(record, context);
-        if self.mode == Mode::Compatible && !context.empty() {
-            assert!(meta_offset > 0);
-            context.write_meta(meta_offset);
+        let is_none = record.is_none();
+        self.write_head::<T>(is_none, context.writer);
+        let meta_start_offset = context.writer.len();
+        if !is_none {
+            if self.mode == Mode::Compatible {
+                context.writer.i32(-1);
+            };
+            <T as Serializer>::serialize(record, context, false);
+            if self.mode == Mode::Compatible && !context.empty() {
+                context.write_meta(meta_start_offset);
+            }
         }
         context.writer.dump()
     }
diff --git a/rust/fory-core/src/meta/type_meta.rs 
b/rust/fory-core/src/meta/type_meta.rs
index b5cdaabd1..eef783f20 100644
--- a/rust/fory-core/src/meta/type_meta.rs
+++ b/rust/fory-core/src/meta/type_meta.rs
@@ -29,9 +29,9 @@ const FIELD_NAME_SIZE_THRESHOLD: usize = 0b1111;
 
 const BIG_NAME_THRESHOLD: usize = 0b111111;
 
-const META_SIZE_MASK: u64 = 0xfff;
-const COMPRESS_META_FLAG: u64 = 0b1 << 13;
-const HAS_FIELDS_META_FLAG: u64 = 0b1 << 12;
+const META_SIZE_MASK: i64 = 0xfff;
+const COMPRESS_META_FLAG: i64 = 0b1 << 13;
+const HAS_FIELDS_META_FLAG: i64 = 0b1 << 12;
 const NUM_HASH_BITS: i8 = 50;
 
 static ENCODING_OPTIONS: &[Encoding] = &[
@@ -55,7 +55,7 @@ pub struct NullableFieldType {
 
 impl NullableFieldType {
     pub fn from(node: FieldType) -> Self {
-        if node.type_id == TypeId::ForyOption as u32 {
+        if node.type_id == TypeId::ForyNullable as u32 {
             let inner = 
NullableFieldType::from(node.generics.into_iter().next().unwrap());
             NullableFieldType {
                 type_id: inner.type_id,
@@ -91,14 +91,14 @@ impl FieldType {
     }
 
     fn to_bytes(&self, writer: &mut Writer, write_flag: bool, nullable: bool) 
-> Result<(), Error> {
-        if self.type_id == TypeId::ForyOption as u32 {
+        if self.type_id == TypeId::ForyNullable as u32 {
             self.generics
                 .first()
                 .unwrap()
                 .to_bytes(writer, write_flag, true)?;
             return Ok(());
         }
-        let mut header: i32 = self.type_id as i32;
+        let mut header = self.type_id;
         if write_flag {
             header <<= 2;
             // let ref_tracking = false;
@@ -106,7 +106,7 @@ impl FieldType {
                 header |= 2;
             }
         }
-        writer.var_int32(header);
+        writer.var_uint32(header);
         match self.type_id {
             x if x == TypeId::LIST as u32 || x == TypeId::SET as u32 => {
                 let generic = self.generics.first().unwrap();
@@ -124,15 +124,15 @@ impl FieldType {
     }
 
     fn from_bytes(reader: &mut Reader, read_flag: bool, nullable: 
Option<bool>) -> Self {
-        let header = reader.var_int32();
+        let header = reader.var_uint32();
         let type_id;
         let _nullable;
         if read_flag {
-            type_id = (header >> 2) as u32;
+            type_id = header >> 2;
             // let tracking_ref = (header & 1) != 0;
             _nullable = (header & 2) != 0;
         } else {
-            type_id = header as u32;
+            type_id = header;
             _nullable = nullable.unwrap();
         }
         let field_type = match type_id {
@@ -158,7 +158,7 @@ impl FieldType {
         };
         if _nullable {
             Self {
-                type_id: TypeId::ForyOption as u32,
+                type_id: TypeId::ForyNullable as u32,
                 generics: vec![field_type],
             }
         } else {
@@ -199,7 +199,7 @@ impl FieldInfo {
         let encoding = Self::u8_to_encoding((header >> 6) & 0b11).unwrap();
         let mut name_size = ((header >> 2) & FIELD_NAME_SIZE_THRESHOLD as u8) 
as usize;
         if name_size == FIELD_NAME_SIZE_THRESHOLD {
-            name_size += reader.var_int32() as usize;
+            name_size += reader.var_uint32() as usize;
         }
         name_size += 1;
 
@@ -227,7 +227,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 as u32;
+        let nullable = self.field_type.type_id == TypeId::ForyNullable as u32;
         // if ref_tracking {
         //     header |= 1;
         // }
@@ -241,7 +241,7 @@ impl FieldInfo {
         header |= encoding_idx << 6;
         writer.u8(header);
         if name_size >= FIELD_NAME_SIZE_THRESHOLD {
-            writer.var_int32((name_size - FIELD_NAME_SIZE_THRESHOLD) as i32);
+            writer.var_uint32((name_size - FIELD_NAME_SIZE_THRESHOLD) as u32);
         }
         self.field_type.to_bytes(&mut writer, false, nullable)?;
         // write field_name
@@ -332,7 +332,7 @@ impl TypeMetaLayer {
     fn to_bytes(&self) -> Result<Vec<u8>, Error> {
         // layer_bytes:| meta_header | fields meta |
         let mut writer = Writer::default();
-        let num_fields = self.field_infos.len() - 1;
+        let num_fields = self.field_infos.len();
         let _internal_id = self.type_id & 0xff;
         // meta_header: | unuse:2 bits | is_register_by_id:1 bit | 
num_fields:4 bits |
         let mut meta_header: u8 = min(num_fields, SMALL_NUM_FIELDS_THRESHOLD) 
as u8;
@@ -341,7 +341,7 @@ impl TypeMetaLayer {
         }
         writer.u8(meta_header);
         if num_fields >= SMALL_NUM_FIELDS_THRESHOLD {
-            writer.var_int32((num_fields - SMALL_NUM_FIELDS_THRESHOLD) as i32);
+            writer.var_uint32((num_fields - SMALL_NUM_FIELDS_THRESHOLD) as 
u32);
         }
         if self.register_by_name {
             self.write_namespace(&mut writer);
@@ -360,9 +360,8 @@ impl TypeMetaLayer {
         let register_by_name = (meta_header & REGISTER_BY_NAME_FLAG) != 0;
         let mut num_fields = meta_header as usize & SMALL_NUM_FIELDS_THRESHOLD;
         if num_fields == SMALL_NUM_FIELDS_THRESHOLD {
-            num_fields += reader.var_int32() as usize;
+            num_fields += reader.var_uint32() as usize;
         }
-        num_fields += 1;
         let type_id;
         let namespace;
         let type_name;
@@ -420,10 +419,10 @@ impl TypeMeta {
     }
     #[allow(unused_assignments)]
     pub fn from_bytes(reader: &mut Reader) -> TypeMeta {
-        let header = reader.u64();
+        let header = reader.i64();
         let mut meta_size = header & META_SIZE_MASK;
         if meta_size == META_SIZE_MASK {
-            meta_size += reader.var_int32() as u64;
+            meta_size += reader.var_uint32() as i64;
         }
 
         // let write_fields_meta = (header & HAS_FIELDS_META_FLAG) != 0;
@@ -447,8 +446,8 @@ impl TypeMeta {
         // }
         
layers_writer.bytes(self.layers.first().unwrap().to_bytes()?.as_slice());
         // global_binary_header:| hash:50bits | is_compressed:1bit | 
write_fields_meta:1bit | meta_size:12bits |
-        let meta_size = layers_writer.len() as u64;
-        let mut header: u64 = min(META_SIZE_MASK, meta_size);
+        let meta_size = layers_writer.len() as i64;
+        let mut header: i64 = min(META_SIZE_MASK, meta_size);
         let write_meta_fields_flag = true;
         if write_meta_fields_flag {
             header |= HAS_FIELDS_META_FLAG;
@@ -457,11 +456,11 @@ impl TypeMeta {
         if is_compressed {
             header |= COMPRESS_META_FLAG;
         }
-        let meta_hash = murmurhash3_x64_128(layers_writer.dump().as_slice(), 
47).0;
-        header |= meta_hash << (64 - NUM_HASH_BITS);
-        result.u64(header);
+        let meta_hash = murmurhash3_x64_128(layers_writer.dump().as_slice(), 
47).0 as i64;
+        header |= (meta_hash << (64 - NUM_HASH_BITS)).abs();
+        result.i64(header);
         if meta_size >= META_SIZE_MASK {
-            result.var_int32((meta_size - META_SIZE_MASK) as i32);
+            result.var_uint32((meta_size - META_SIZE_MASK) as u32);
         }
         result.bytes(layers_writer.dump().as_slice());
         Ok(result.dump())
diff --git a/rust/fory-core/src/resolver/context.rs 
b/rust/fory-core/src/resolver/context.rs
index f98fab5c5..0cfb35f78 100644
--- a/rust/fory-core/src/resolver/context.rs
+++ b/rust/fory-core/src/resolver/context.rs
@@ -50,8 +50,10 @@ impl<'se> WriteContext<'se> {
     }
 
     pub fn write_meta(&mut self, offset: usize) {
-        self.writer
-            .set_bytes(offset, &(self.writer.len() as u32).to_le_bytes());
+        self.writer.set_bytes(
+            offset,
+            &((self.writer.len() - offset - 4) as u32).to_le_bytes(),
+        );
         self.meta_resolver.to_bytes(self.writer).unwrap()
     }
 
@@ -104,19 +106,10 @@ 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..]))
+        self.meta_resolver.load(&mut Reader::new(
+            &self.reader.slice_after_cursor()[offset..],
+        ))
     }
 
     pub fn read_tag(&mut self) -> Result<&str, Error> {
diff --git a/rust/fory-core/src/resolver/meta_resolver.rs 
b/rust/fory-core/src/resolver/meta_resolver.rs
index 44271e79c..cdd23023e 100644
--- a/rust/fory-core/src/resolver/meta_resolver.rs
+++ b/rust/fory-core/src/resolver/meta_resolver.rs
@@ -33,8 +33,8 @@ impl MetaReaderResolver {
     }
 
     pub fn load(&mut self, reader: &mut Reader) {
-        let meta_size = reader.var_int32();
-        self.reading_type_defs.reserve(meta_size as usize);
+        let meta_size = reader.var_uint32();
+        // self.reading_type_defs.reserve(meta_size as usize);
         for _ in 0..meta_size {
             self.reading_type_defs
                 .push(Rc::new(TypeMeta::from_bytes(reader)));
@@ -67,7 +67,7 @@ impl<'a> MetaWriterResolver<'a> {
     }
 
     pub fn to_bytes(&self, writer: &mut Writer) -> Result<(), Error> {
-        writer.var_int32(self.type_defs.len() as i32);
+        writer.var_uint32(self.type_defs.len() as u32);
         for item in &self.type_defs {
             writer.bytes(item);
         }
diff --git a/rust/fory-core/src/resolver/type_resolver.rs 
b/rust/fory-core/src/resolver/type_resolver.rs
index 27daac968..03bfafea7 100644
--- a/rust/fory-core/src/resolver/type_resolver.rs
+++ b/rust/fory-core/src/resolver/type_resolver.rs
@@ -19,6 +19,7 @@ use super::context::{ReadContext, WriteContext};
 use crate::error::Error;
 use crate::fory::Fory;
 use crate::serializer::StructSerializer;
+use std::cell::RefCell;
 use std::{any::Any, collections::HashMap};
 
 pub struct Harness {
@@ -93,6 +94,7 @@ pub struct TypeResolver {
     // Fast lookup by numeric ID for common types
     type_id_index: Vec<u32>,
     type_id_counter: u32,
+    sorted_field_names_map: RefCell<HashMap<std::any::TypeId, Vec<String>>>,
 }
 
 const NO_TYPE_ID: u32 = 1000000000;
@@ -127,7 +129,7 @@ impl TypeResolver {
             let this = this.downcast_ref::<T2>();
             match this {
                 Some(v) => {
-                    T2::serialize(v, context);
+                    T2::write(v, context, true);
                 }
                 None => todo!(),
             }
@@ -136,7 +138,7 @@ impl TypeResolver {
         fn deserializer<T2: 'static + StructSerializer>(
             context: &mut ReadContext,
         ) -> Result<Box<dyn Any>, Error> {
-            match T2::deserialize(context) {
+            match T2::read_compatible(context) {
                 Ok(v) => Ok(Box::new(v)),
                 Err(e) => Err(e),
             }
@@ -177,4 +179,17 @@ impl TypeResolver {
     pub fn get_harness(&self, id: u32) -> Option<&Harness> {
         self.serialize_map.get(&id)
     }
+
+    pub fn get_sorted_field_names<T: StructSerializer>(
+        &self,
+        type_id: std::any::TypeId,
+    ) -> Option<Vec<String>> {
+        let map = self.sorted_field_names_map.borrow();
+        map.get(&type_id).cloned()
+    }
+
+    pub fn set_sorted_field_names<T: StructSerializer>(&self, field_names: 
&[String]) {
+        let mut map = self.sorted_field_names_map.borrow_mut();
+        map.insert(std::any::TypeId::of::<T>(), field_names.to_owned());
+    }
 }
diff --git a/rust/fory-core/src/serializer/bool.rs 
b/rust/fory-core/src/serializer/bool.rs
index 06b1d6a0d..d6f61ae2d 100644
--- a/rust/fory-core/src/serializer/bool.rs
+++ b/rust/fory-core/src/serializer/bool.rs
@@ -20,7 +20,7 @@ use crate::fory::Fory;
 use crate::resolver::context::ReadContext;
 use crate::resolver::context::WriteContext;
 use crate::serializer::Serializer;
-use crate::types::TypeId;
+use crate::types::{Mode, TypeId};
 use std::mem;
 
 impl Serializer for bool {
@@ -28,11 +28,18 @@ impl Serializer for bool {
         mem::size_of::<i32>()
     }
 
-    fn write(&self, context: &mut WriteContext) {
+    fn write(&self, context: &mut WriteContext, is_field: bool) {
+        if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
+            context.writer.var_uint32(TypeId::BOOL as u32);
+        }
         context.writer.u8(if *self { 1 } else { 0 });
     }
 
-    fn read(context: &mut ReadContext) -> Result<Self, Error> {
+    fn read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
+        if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
+            let remote_type_id = context.reader.var_uint32();
+            assert_eq!(remote_type_id, TypeId::BOOL as u32);
+        }
         Ok(context.reader.u8() == 1)
     }
 
diff --git a/rust/fory-core/src/serializer/collection.rs 
b/rust/fory-core/src/serializer/collection.rs
index b14186da5..3e20f4fb5 100644
--- a/rust/fory-core/src/serializer/collection.rs
+++ b/rust/fory-core/src/serializer/collection.rs
@@ -19,13 +19,13 @@ use crate::ensure;
 use crate::error::Error;
 use crate::resolver::context::ReadContext;
 use crate::resolver::context::WriteContext;
-use crate::serializer::Serializer;
-use crate::types::SIZE_OF_REF_AND_TYPE;
+use crate::serializer::{deserialize, serialize, Serializer};
+use crate::types::{Mode, SIZE_OF_REF_AND_TYPE};
 use anyhow::anyhow;
 
 // const TRACKING_REF: u8 = 0b1;
 
-const HAS_NULL: u8 = 0b10;
+pub const HAS_NULL: u8 = 0b10;
 
 // Whether collection elements type is not declare type.
 const NOT_DECL_ELEMENT_TYPE: u8 = 0b100;
@@ -36,10 +36,18 @@ const NOT_DECL_ELEMENT_TYPE: u8 = 0b100;
 pub fn write_collection<'a, T: Serializer + 'a, I: IntoIterator<Item = &'a T>>(
     iter: I,
     context: &mut WriteContext,
+    is_field: bool,
+    collection_type_id: u32,
 ) {
+    if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
+        context.writer.var_uint32(collection_type_id);
+    }
     let iter = iter.into_iter();
     let len = iter.size_hint().0;
     context.writer.var_uint32(len as u32);
+    if len == 0 {
+        return;
+    }
     let has_null = T::is_option();
     let mut header = 0;
     if has_null {
@@ -55,24 +63,36 @@ pub fn write_collection<'a, T: Serializer + 'a, I: 
IntoIterator<Item = &'a T>>(
             .writer
             .reserve((<T as Serializer>::reserved_space()) * len);
         for item in iter {
-            item.write(context);
+            item.write(context, true);
         }
     } else {
         context
             .writer
             .reserve((<T as Serializer>::reserved_space() + 
SIZE_OF_REF_AND_TYPE) * len);
         for item in iter {
-            item.serialize(context);
+            serialize(item, context, true);
         }
     }
+    println!("bytes after write collection {:?}", context.writer.dump());
 }
 
-pub fn read_collection<C, T>(context: &mut ReadContext) -> Result<C, Error>
+pub fn read_collection<C, T>(
+    context: &mut ReadContext,
+    is_field: bool,
+    expected_collection_type_id: u32,
+) -> Result<C, Error>
 where
     T: Serializer,
     C: FromIterator<T>,
 {
+    if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
+        let remote_collection_type_id = context.reader.var_uint32();
+        assert_eq!(remote_collection_type_id, expected_collection_type_id);
+    }
     let len = context.reader.var_uint32();
+    if len == 0 {
+        return Ok(C::from_iter(std::iter::empty()));
+    }
     let header = context.reader.u8();
     let actual_elem_type_id = context.reader.var_uint32();
     let expected_elem_id = T::get_type_id(context.fory);
@@ -82,11 +102,11 @@ where
     );
     if header & HAS_NULL == 0 {
         (0..len)
-            .map(|_| T::read(context))
+            .map(|_| T::read(context, true))
             .collect::<Result<C, Error>>()
     } else {
         (0..len)
-            .map(|_| T::deserialize(context))
+            .map(|_| deserialize(context, true))
             .collect::<Result<C, Error>>()
     }
 }
diff --git a/rust/fory-core/src/serializer/datetime.rs 
b/rust/fory-core/src/serializer/datetime.rs
index 9af93af82..48c0b57b9 100644
--- a/rust/fory-core/src/serializer/datetime.rs
+++ b/rust/fory-core/src/serializer/datetime.rs
@@ -20,14 +20,18 @@ use crate::fory::Fory;
 use crate::resolver::context::ReadContext;
 use crate::resolver::context::WriteContext;
 use crate::serializer::Serializer;
-use crate::types::{ForyGeneralList, TypeId};
+use crate::types::{ForyGeneralList, Mode, TypeId};
 use crate::util::EPOCH;
 use anyhow::anyhow;
 use chrono::{DateTime, Days, NaiveDate, NaiveDateTime};
 use std::mem;
 
 impl Serializer for NaiveDateTime {
-    fn read(context: &mut ReadContext) -> Result<Self, Error> {
+    fn read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
+        if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
+            let remote_type_id = context.reader.var_uint32();
+            assert_eq!(remote_type_id, TypeId::TIMESTAMP as u32);
+        }
         let micros = context.reader.i64();
         let seconds = micros / 1_000_000;
         let subsec_micros = (micros % 1_000_000) as u32;
@@ -39,7 +43,10 @@ impl Serializer for NaiveDateTime {
             )))
     }
 
-    fn write(&self, context: &mut WriteContext) {
+    fn write(&self, context: &mut WriteContext, is_field: bool) {
+        if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
+            context.writer.var_uint32(TypeId::TIMESTAMP as u32);
+        }
         let dt = self.and_utc();
         let micros = dt.timestamp() * 1_000_000 + dt.timestamp_subsec_micros() 
as i64;
         context.writer.i64(micros);
@@ -57,7 +64,10 @@ impl Serializer for NaiveDateTime {
 impl ForyGeneralList for NaiveDateTime {}
 
 impl Serializer for NaiveDate {
-    fn write(&self, context: &mut WriteContext) {
+    fn write(&self, context: &mut WriteContext, is_field: bool) {
+        if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
+            context.writer.var_uint32(TypeId::LOCAL_DATE as u32);
+        }
         let days_since_epoch = self.signed_duration_since(EPOCH).num_days();
         context.writer.i32(days_since_epoch as i32);
     }
@@ -66,7 +76,11 @@ impl Serializer for NaiveDate {
         mem::size_of::<i32>()
     }
 
-    fn read(context: &mut ReadContext) -> Result<Self, Error> {
+    fn read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
+        if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
+            let remote_type_id = context.reader.var_uint32();
+            assert_eq!(remote_type_id, TypeId::LOCAL_DATE as u32);
+        }
         let days = context.reader.i32();
         EPOCH
             .checked_add_days(Days::new(days as u64))
diff --git a/rust/fory-core/src/serializer/list.rs 
b/rust/fory-core/src/serializer/list.rs
index 9f7e7a103..cf95253b6 100644
--- a/rust/fory-core/src/serializer/list.rs
+++ b/rust/fory-core/src/serializer/list.rs
@@ -29,12 +29,12 @@ impl<T> Serializer for Vec<T>
 where
     T: Serializer + ForyGeneralList,
 {
-    fn write(&self, context: &mut WriteContext) {
-        write_collection(self.iter(), context);
+    fn write(&self, context: &mut WriteContext, is_field: bool) {
+        write_collection(self.iter(), context, is_field, TypeId::LIST as u32);
     }
 
-    fn read(context: &mut ReadContext) -> Result<Self, Error> {
-        read_collection(context)
+    fn read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
+        read_collection(context, is_field, TypeId::LIST as u32)
     }
 
     fn reserved_space() -> usize {
diff --git a/rust/fory-core/src/serializer/map.rs 
b/rust/fory-core/src/serializer/map.rs
index a3a9c6d28..f374a1cdf 100644
--- a/rust/fory-core/src/serializer/map.rs
+++ b/rust/fory-core/src/serializer/map.rs
@@ -21,7 +21,7 @@ use crate::fory::Fory;
 use crate::resolver::context::ReadContext;
 use crate::resolver::context::WriteContext;
 use crate::serializer::Serializer;
-use crate::types::{ForyGeneralList, TypeId, SIZE_OF_REF_AND_TYPE};
+use crate::types::{ForyGeneralList, Mode, TypeId, SIZE_OF_REF_AND_TYPE};
 use anyhow::anyhow;
 use std::collections::HashMap;
 use std::mem;
@@ -29,7 +29,10 @@ use std::mem;
 const MAX_CHUNK_SIZE: u8 = 255;
 
 impl<T1: Serializer + Eq + std::hash::Hash, T2: Serializer> Serializer for 
HashMap<T1, T2> {
-    fn write(&self, context: &mut WriteContext) {
+    fn write(&self, context: &mut WriteContext, is_field: bool) {
+        if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
+            context.writer.var_uint32(TypeId::MAP as u32);
+        }
         context.writer.var_uint32(self.len() as u32);
         let reserved_space = (<T1 as Serializer>::reserved_space() + 
SIZE_OF_REF_AND_TYPE)
             * self.len()
@@ -42,6 +45,11 @@ impl<T1: Serializer + Eq + std::hash::Hash, T2: Serializer> 
Serializer for HashM
         for entry in self.iter() {
             if !header_gen {
                 header_offset = context.writer.len();
+                let _is_key_null = false;
+                let _is_val_null = false;
+                // todo
+                // if T1::is_option() {}
+                // if T2::is_option() {}
                 context.writer.i16(-1);
                 context
                     .writer
@@ -53,8 +61,8 @@ impl<T1: Serializer + Eq + std::hash::Hash, T2: Serializer> 
Serializer for HashM
                 context.writer.set_bytes(header_offset, &[header]);
                 header_gen = true;
             }
-            entry.0.write(context);
-            entry.1.write(context);
+            entry.0.write(context, true);
+            entry.1.write(context, true);
             pair_counter += 1;
             if pair_counter == MAX_CHUNK_SIZE {
                 context.writer.set_bytes(header_offset + 1, &[pair_counter]);
@@ -68,7 +76,11 @@ impl<T1: Serializer + Eq + std::hash::Hash, T2: Serializer> 
Serializer for HashM
         }
     }
 
-    fn read(context: &mut ReadContext) -> Result<Self, Error> {
+    fn read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
+        if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
+            let remote_collection_type_id = context.reader.var_uint32();
+            assert_eq!(remote_collection_type_id, TypeId::MAP as u32);
+        }
         let mut map = HashMap::<T1, T2>::new();
         let len = context.reader.var_uint32();
         let mut len_counter = 0;
@@ -92,7 +104,7 @@ impl<T1: Serializer + Eq + std::hash::Hash, T2: Serializer> 
Serializer for HashM
             );
             assert!(len_counter + chunk_size as u32 <= len);
             for _ in (0..chunk_size).enumerate() {
-                map.insert(T1::read(context)?, T2::read(context)?);
+                map.insert(T1::read(context, true)?, T2::read(context, true)?);
             }
             len_counter += chunk_size as u32;
         }
diff --git a/rust/fory-core/src/serializer/mod.rs 
b/rust/fory-core/src/serializer/mod.rs
index 5666721e1..b918db5f5 100644
--- a/rust/fory-core/src/serializer/mod.rs
+++ b/rust/fory-core/src/serializer/mod.rs
@@ -15,7 +15,6 @@
 // specific language governing permissions and limitations
 // under the License.
 
-use crate::ensure;
 use crate::error::Error;
 use crate::fory::Fory;
 use crate::resolver::context::{ReadContext, WriteContext};
@@ -35,28 +34,22 @@ 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
-        .var_uint32(T::get_type_id(context.get_fory()));
-    this.write(context);
+pub fn serialize<T: Serializer + 'static>(record: &T, context: &mut 
WriteContext, is_field: bool) {
+    if record.is_none() {
+        context.writer.i8(RefFlag::Null as i8);
+    } else {
+        context.writer.i8(RefFlag::NotNullValue as i8);
+        record.write(context, is_field);
+    }
 }
 
-pub fn deserialize<T: Serializer + Default>(context: &mut ReadContext) -> 
Result<T, Error> {
-    // ref flag
+pub fn deserialize<T: Serializer + Default>(
+    context: &mut ReadContext,
+    is_field: bool,
+) -> 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.var_uint32();
-        let expected_type_id = T::get_type_id(context.get_fory());
-        ensure!(
-            expected_type_id == actual_type_id,
-            anyhow!("Invalid field type, expected:{expected_type_id}, 
actual:{actual_type_id}")
-        );
-        T::read(context)
+        T::read(context, is_field)
     } else if ref_flag == (RefFlag::Null as i8) {
         Ok(T::default())
         // Err(anyhow!("Try to deserialize non-option type to null"))?
@@ -69,27 +62,27 @@ pub fn deserialize<T: Serializer + Default>(context: &mut 
ReadContext) -> Result
 
 pub trait Serializer
 where
-    Self: Sized + Default,
+    Self: Sized + Default + 'static,
 {
     /// The possible max memory size of the type.
     /// Used to reserve the buffer space to avoid reallocation, which may hurt 
performance.
     fn reserved_space() -> usize;
 
     /// Write the data into the buffer.
-    fn write(&self, context: &mut WriteContext);
+    fn write(&self, context: &mut WriteContext, is_field: bool);
 
     /// Entry point of the serialization.
     ///
     /// Step 1: write the type flag and type flag into the buffer.
     /// Step 2: invoke the write function to write the Rust object.
-    fn serialize(&self, context: &mut WriteContext) {
-        serialize(self, context);
+    fn serialize(&self, context: &mut WriteContext, is_field: bool) {
+        serialize(self, context, is_field);
     }
 
-    fn read(context: &mut ReadContext) -> Result<Self, Error>;
+    fn read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error>;
 
-    fn deserialize(context: &mut ReadContext) -> Result<Self, Error> {
-        deserialize(context)
+    fn deserialize(context: &mut ReadContext, is_field: bool) -> Result<Self, 
Error> {
+        deserialize(context, is_field)
     }
 
     fn get_type_id(_fory: &Fory) -> u32;
@@ -97,6 +90,10 @@ where
     fn is_option() -> bool {
         false
     }
+
+    fn is_none(&self) -> bool {
+        false
+    }
 }
 
 pub trait StructSerializer: Serializer + 'static {
@@ -110,4 +107,8 @@ pub trait StructSerializer: Serializer + 'static {
 
     fn type_index() -> u32;
     fn actual_type_id(type_id: u32) -> u32;
+
+    fn read_compatible(context: &mut ReadContext) -> Result<Self, Error>;
+
+    fn get_sorted_field_names(fory: &Fory) -> Vec<String>;
 }
diff --git a/rust/fory-core/src/serializer/number.rs 
b/rust/fory-core/src/serializer/number.rs
index c925a88c4..aa5688dc6 100644
--- a/rust/fory-core/src/serializer/number.rs
+++ b/rust/fory-core/src/serializer/number.rs
@@ -25,11 +25,18 @@ use crate::types::{ForyGeneralList, TypeId};
 macro_rules! impl_num_serializer {
     ($name: ident, $ty:tt, $field_type: expr) => {
         impl Serializer for $ty {
-            fn write(&self, context: &mut WriteContext) {
+            fn write(&self, context: &mut WriteContext, is_field: bool) {
+                if *context.get_fory().get_mode() == 
crate::types::Mode::Compatible && !is_field {
+                    context.writer.var_uint32($field_type as u32);
+                }
                 context.writer.$name(*self);
             }
 
-            fn read(context: &mut ReadContext) -> Result<Self, Error> {
+            fn read(context: &mut ReadContext, is_field: bool) -> Result<Self, 
Error> {
+                if *context.get_fory().get_mode() == 
crate::types::Mode::Compatible && !is_field {
+                    let remote_type_id = context.reader.var_uint32();
+                    assert_eq!(remote_type_id, $field_type as u32);
+                }
                 Ok(context.reader.$name())
             }
 
diff --git a/rust/fory-core/src/serializer/option.rs 
b/rust/fory-core/src/serializer/option.rs
index c787b32b7..8a1129d0c 100644
--- a/rust/fory-core/src/serializer/option.rs
+++ b/rust/fory-core/src/serializer/option.rs
@@ -15,68 +15,33 @@
 // specific language governing permissions and limitations
 // under the License.
 
-use crate::ensure;
 use crate::error::Error;
 use crate::fory::Fory;
 use crate::resolver::context::ReadContext;
 use crate::resolver::context::WriteContext;
 use crate::serializer::Serializer;
-use crate::types::{ForyGeneralList, RefFlag};
-use anyhow::anyhow;
+use crate::types::{ForyGeneralList, Mode, TypeId};
 
 impl<T: Serializer> Serializer for Option<T> {
-    fn read(context: &mut ReadContext) -> Result<Self, Error> {
-        Ok(Some(T::read(context)?))
-    }
-
-    fn deserialize(context: &mut ReadContext) -> Result<Self, Error> {
-        // ref flag
-        let ref_flag = context.reader.i8();
-
-        if ref_flag == (RefFlag::NotNullValue as i8) || ref_flag == 
(RefFlag::RefValue as i8) {
-            // type_id
-            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,
-                anyhow!("Invalid field type, expected:{expected_type_id}, 
actual:{actual_type_id}")
-            );
-
-            Ok(Some(T::read(context)?))
-        } else if ref_flag == (RefFlag::Null as i8) {
-            Ok(None)
-        } else if ref_flag == (RefFlag::Ref as i8) {
-            Err(Error::Ref)
-        } else {
-            Err(anyhow!("Unknown ref flag, value:{ref_flag}"))?
+    fn read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
+        if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
+            let remote_type_id = context.reader.var_uint32();
+            assert_eq!(remote_type_id, T::get_type_id(context.fory));
         }
+        Ok(Some(T::read(context, is_field)?))
     }
 
-    fn write(&self, context: &mut WriteContext) {
+    fn write(&self, context: &mut WriteContext, is_field: bool) {
+        if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
+            context.writer.var_uint32(TypeId::BOOL as u32);
+        }
         if let Some(v) = self {
-            T::write(v, context)
+            T::write(v, context, is_field)
         } else {
             unreachable!("write should be call by serialize")
         }
     }
 
-    fn serialize(&self, context: &mut WriteContext) {
-        match self {
-            Some(v) => {
-                // ref flag
-                context.writer.i8(RefFlag::NotNullValue as i8);
-                // type
-                context
-                    .writer
-                    .var_uint32(T::get_type_id(context.get_fory()));
-                v.write(context);
-            }
-            None => {
-                context.writer.i8(RefFlag::Null as i8);
-            }
-        }
-    }
-
     fn reserved_space() -> usize {
         std::mem::size_of::<T>()
     }
@@ -88,6 +53,10 @@ impl<T: Serializer> Serializer for Option<T> {
     fn is_option() -> bool {
         true
     }
+
+    fn is_none(&self) -> bool {
+        self.is_none()
+    }
 }
 
 impl<T: Serializer> ForyGeneralList for Option<T> {}
diff --git a/rust/fory-core/src/serializer/primitive_list.rs 
b/rust/fory-core/src/serializer/primitive_list.rs
index 895c23e63..4abea1a53 100644
--- a/rust/fory-core/src/serializer/primitive_list.rs
+++ b/rust/fory-core/src/serializer/primitive_list.rs
@@ -25,7 +25,10 @@ use crate::types::TypeId;
 macro_rules! impl_primitive_vec {
     ($name:ident, $ty:ty, $field_type:expr) => {
         impl Serializer for Vec<$ty> {
-            fn write(&self, context: &mut WriteContext) {
+            fn write(&self, context: &mut WriteContext, is_field: bool) {
+                if *context.get_fory().get_mode() == 
crate::types::Mode::Compatible && !is_field {
+                    context.writer.var_uint32($field_type as u32);
+                }
                 let len_bytes = self.len() * std::mem::size_of::<$ty>();
                 context.writer.var_uint32(len_bytes as u32);
                 context.writer.reserve(len_bytes);
@@ -39,7 +42,11 @@ macro_rules! impl_primitive_vec {
                 }
             }
 
-            fn read(context: &mut ReadContext) -> Result<Self, Error> {
+            fn read(context: &mut ReadContext, is_field: bool) -> Result<Self, 
Error> {
+                if *context.get_fory().get_mode() == 
crate::types::Mode::Compatible && !is_field {
+                    let remote_type_id = context.reader.var_uint32();
+                    assert_eq!(remote_type_id, $field_type as u32);
+                }
                 let size_bytes = context.reader.var_uint32() as usize;
 
                 if size_bytes % std::mem::size_of::<$ty>() != 0 {
diff --git a/rust/fory-core/src/serializer/set.rs 
b/rust/fory-core/src/serializer/set.rs
index 19d3e5d8f..8b2949fd5 100644
--- a/rust/fory-core/src/serializer/set.rs
+++ b/rust/fory-core/src/serializer/set.rs
@@ -26,12 +26,12 @@ use std::collections::HashSet;
 use std::mem;
 
 impl<T: Serializer + Eq + std::hash::Hash> Serializer for HashSet<T> {
-    fn write(&self, context: &mut WriteContext) {
-        write_collection(self.iter(), context);
+    fn write(&self, context: &mut WriteContext, is_field: bool) {
+        write_collection(self.iter(), context, is_field, TypeId::SET as u32);
     }
 
-    fn read(context: &mut ReadContext) -> Result<Self, Error> {
-        read_collection(context)
+    fn read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
+        read_collection(context, is_field, TypeId::SET as u32)
     }
 
     fn reserved_space() -> usize {
diff --git a/rust/fory-core/src/serializer/skip.rs 
b/rust/fory-core/src/serializer/skip.rs
index e02d98a79..0972afcb8 100644
--- a/rust/fory-core/src/serializer/skip.rs
+++ b/rust/fory-core/src/serializer/skip.rs
@@ -15,20 +15,19 @@
 // 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::collection::HAS_NULL;
 use crate::serializer::Serializer;
 use crate::types::{RefFlag, TypeId, BASIC_TYPES, CONTAINER_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)?;
+                <$ty>::read($context, true)?;
                 return Ok(());
             }
         )+else {
@@ -37,25 +36,25 @@ macro_rules! basic_type_deserialize {
     };
 }
 
+// call when is_field && is_compatible_mode
+#[allow(unreachable_code)]
 pub fn skip_field_value(
     context: &mut ReadContext,
     field_type: &NullableFieldType,
+    read_ref_flag: bool,
 ) -> 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(());
+    if read_ref_flag {
+        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();
+    let type_id_num = field_type.type_id;
     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),
@@ -79,15 +78,24 @@ pub fn skip_field_value(
                 );
             } else if CONTAINER_TYPES.contains(&type_id) {
                 if type_id == TypeId::LIST || type_id == TypeId::SET {
-                    let length = context.reader.var_int32() as usize;
+                    let length = context.reader.var_uint32() as usize;
+                    // todo
+                    let header = context.reader.u8();
+                    let read_ref_flag = (header & HAS_NULL) != 0;
+                    let _elem_type = context.reader.var_uint32();
                     for _ in 0..length {
-                        skip_field_value(context, 
field_type.generics.first().unwrap())?;
+                        skip_field_value(
+                            context,
+                            field_type.generics.first().unwrap(),
+                            read_ref_flag,
+                        )?;
                     }
                 } else if type_id == TypeId::MAP {
-                    let length = context.reader.var_int32() as usize;
+                    todo!();
+                    let length = context.reader.var_uint32() 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())?;
+                        skip_field_value(context, 
field_type.generics.first().unwrap(), true)?;
+                        skip_field_value(context, 
field_type.generics.get(1).unwrap(), true)?;
                     }
                 }
                 Ok(())
@@ -97,14 +105,24 @@ pub fn skip_field_value(
         }
         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);
+            if tag == TypeId::COMPATIBLE_STRUCT as u32 {
+                let remote_type_id = context.reader.var_uint32();
+                let meta_index = context.reader.var_uint32();
+                let type_def = context.get_meta(meta_index as usize);
+                assert_eq!(remote_type_id, type_def.get_type_id());
                 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)?;
+                    skip_field_value(context, &nullable_field_type, true)?;
                 }
+            } else if tag == TypeId::ENUM as u32 {
+                context
+                    .fory
+                    .get_type_resolver()
+                    .get_harness(type_id_num)
+                    .unwrap()
+                    .get_deserializer()(context)?;
             } else {
                 unimplemented!()
             }
diff --git a/rust/fory-core/src/serializer/string.rs 
b/rust/fory-core/src/serializer/string.rs
index d9d1749ec..bc1bf1592 100644
--- a/rust/fory-core/src/serializer/string.rs
+++ b/rust/fory-core/src/serializer/string.rs
@@ -21,7 +21,7 @@ use crate::meta::get_latin1_length;
 use crate::resolver::context::ReadContext;
 use crate::resolver::context::WriteContext;
 use crate::serializer::Serializer;
-use crate::types::{ForyGeneralList, TypeId};
+use crate::types::{ForyGeneralList, Mode, TypeId};
 use std::mem;
 
 enum StrEncoding {
@@ -35,7 +35,10 @@ impl Serializer for String {
         mem::size_of::<i32>()
     }
 
-    fn write(&self, context: &mut WriteContext) {
+    fn write(&self, context: &mut WriteContext, is_field: bool) {
+        if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
+            context.writer.var_uint32(TypeId::STRING as u32);
+        }
         let mut len = get_latin1_length(self);
         if len >= 0 {
             let bitor = (len as u64) << 2 | StrEncoding::Latin1 as u64;
@@ -49,7 +52,11 @@ impl Serializer for String {
         }
     }
 
-    fn read(context: &mut ReadContext) -> Result<Self, Error> {
+    fn read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
+        if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
+            let remote_type_id = context.reader.var_uint32();
+            assert_eq!(remote_type_id, TypeId::STRING as u32);
+        }
         let bitor = context.reader.var_uint36_small();
         let len = bitor >> 2;
         let encoding = bitor & 0b11;
diff --git a/rust/fory-core/src/types.rs b/rust/fory-core/src/types.rs
index 55d41e81d..39a395296 100644
--- a/rust/fory-core/src/types.rs
+++ b/rust/fory-core/src/types.rs
@@ -85,7 +85,7 @@ pub enum TypeId {
     UNKNOWN = 63,
     ForyAny = 256,
     // only used at receiver peer
-    ForyOption = 265,
+    ForyNullable = 265,
 }
 
 pub trait ForyGeneralList {}
@@ -126,6 +126,19 @@ pub static BASIC_TYPES: [TypeId; 18] = [
     TypeId::FLOAT64_ARRAY,
 ];
 
+pub static FINAL_TYPES: [TypeId; 3] = [TypeId::STRING, TypeId::LOCAL_DATE, 
TypeId::TIMESTAMP];
+
+pub static PRIMITIVE_ARRAY_TYPES: [TypeId; 8] = [
+    TypeId::BOOL_ARRAY,
+    TypeId::BINARY,
+    TypeId::INT8_ARRAY,
+    TypeId::INT16_ARRAY,
+    TypeId::INT32_ARRAY,
+    TypeId::INT64_ARRAY,
+    TypeId::FLOAT32_ARRAY,
+    TypeId::FLOAT64_ARRAY,
+];
+
 pub static BASIC_TYPE_NAMES: [&str; 10] = [
     "bool",
     "i8",
diff --git a/rust/fory-derive/src/object/derive_enum.rs 
b/rust/fory-derive/src/object/derive_enum.rs
index be89631e6..6c9efa703 100644
--- a/rust/fory-derive/src/object/derive_enum.rs
+++ b/rust/fory-derive/src/object/derive_enum.rs
@@ -27,14 +27,18 @@ pub fn gen_type_def(_data_enum: &DataEnum) -> TokenStream {
 
 pub fn gen_write(data_enum: &DataEnum) -> TokenStream {
     let variant_idents: Vec<_> = data_enum.variants.iter().map(|v| 
&v.ident).collect();
-    let variant_values: Vec<_> = (0..variant_idents.len()).map(|v| v as 
i32).collect();
+    let variant_values: Vec<_> = (0..variant_idents.len()).map(|v| v as 
u32).collect();
 
     quote! {
-        fn write(&self, context: &mut 
fory_core::resolver::context::WriteContext) {
+        fn write(&self, context: &mut 
fory_core::resolver::context::WriteContext, is_field: bool) {
+            if !is_field {
+                let type_id = Self::get_type_id(context.get_fory());
+                context.writer.var_uint32(type_id);
+            }
             match self {
                 #(
                     Self::#variant_idents => {
-                        context.writer.var_int32(#variant_values);
+                        context.writer.var_uint32(#variant_values);
                     }
                 )*
             }
@@ -48,14 +52,20 @@ pub fn gen_write(data_enum: &DataEnum) -> TokenStream {
 
 pub fn gen_read(data_enum: &DataEnum) -> TokenStream {
     let variant_idents: Vec<_> = data_enum.variants.iter().map(|v| 
&v.ident).collect();
-    let variant_values: Vec<_> = (0..variant_idents.len()).map(|v| v as 
i32).collect();
+    let variant_values: Vec<_> = (0..variant_idents.len()).map(|v| v as 
u32).collect();
 
     quote! {
        fn read(
            context: &mut fory_core::resolver::context::ReadContext,
+            is_field: bool
        ) -> Result<Self, fory_core::error::Error> {
-           let v = context.reader.var_int32();
-           match v {
+            if !is_field {
+                let remote_type_id = context.reader.var_uint32();
+                let local_type_id = Self::get_type_id(context.get_fory());
+                assert_eq!(remote_type_id, local_type_id);
+            }
+           let ordinal = context.reader.var_uint32();
+           match ordinal {
                #(
                    #variant_values => Ok(Self::#variant_idents),
                )*
@@ -70,3 +80,11 @@ pub fn gen_actual_type_id() -> TokenStream {
         (type_id << 8) + fory_core::types::TypeId::ENUM as u32
     }
 }
+
+pub fn gen_read_compatible(_data_enum: &DataEnum) -> TokenStream {
+    quote! {
+        fn read_compatible(context: &mut 
fory_core::resolver::context::ReadContext) -> Result<Self, 
fory_core::error::Error> {
+            <Self as fory_core::serializer::Serializer>::read(context, true)
+        }
+    }
+}
diff --git a/rust/fory-derive/src/object/misc.rs 
b/rust/fory-derive/src/object/misc.rs
index c03b210c2..ec7eac6bf 100644
--- a/rust/fory-derive/src/object/misc.rs
+++ b/rust/fory-derive/src/object/misc.rs
@@ -20,7 +20,7 @@ use quote::quote;
 use std::sync::atomic::{AtomicU32, Ordering};
 use syn::Field;
 
-use super::util::{generic_tree_to_tokens, parse_generic_tree};
+use super::util::{generic_tree_to_tokens, get_sort_fields_ts, 
parse_generic_tree};
 
 // Global type ID counter that auto-grows from 0 at macro processing time
 static TYPE_ID_COUNTER: AtomicU32 = AtomicU32::new(0);
@@ -65,13 +65,24 @@ fn type_def(fields: &[&Field]) -> TokenStream {
         }
     });
     quote! {
-        fory_core::meta::TypeMeta::from_fields(
+         let sorted_field_names = <Self as 
fory_core::serializer::StructSerializer>::get_sorted_field_names(fory);
+        let field_infos = vec![#(#field_infos),*];
+        let mut sorted_field_infos = Vec::with_capacity(field_infos.len());
+        for name in &sorted_field_names {
+            if let Some(info) = field_infos.iter().find(|f| &f.field_name == 
name) {
+                sorted_field_infos.push(info.clone());
+            } else {
+                panic!("Field {} not found in field_infos", name);
+            }
+        }
+        let meta = fory_core::meta::TypeMeta::from_fields(
             type_id,
             namespace,
             type_name,
             register_by_name,
-            vec![#(#field_infos),*]
-        ).to_bytes().unwrap()
+            sorted_field_infos,
+        );
+        meta.to_bytes().unwrap()
     }
 }
 
@@ -86,7 +97,7 @@ pub fn gen_in_struct_impl(fields: &[&Field]) -> TokenStream {
 
 pub fn gen_actual_type_id() -> TokenStream {
     quote! {
-        (type_id << 8) + fory_core::types::TypeId::STRUCT as u32
+        (type_id << 8) + fory_core::types::TypeId::COMPATIBLE_STRUCT as u32
     }
 }
 
@@ -105,3 +116,18 @@ pub fn gen_type_index(type_id: u32) -> TokenStream {
         }
     }
 }
+
+pub fn gen_sort_fields(fields: &[&Field]) -> TokenStream {
+    let create_sorted_field_names = get_sort_fields_ts(fields);
+    quote! {
+        let sorted_field_names = match 
fory.get_type_resolver().get_sorted_field_names::<Self>(std::any::TypeId::of::<Self>())
 {
+            Some(result) => result,
+            None => {
+                #create_sorted_field_names
+                
fory.get_type_resolver().set_sorted_field_names::<Self>(&sorted_field_names);
+                sorted_field_names
+            }
+        };
+        sorted_field_names
+    }
+}
diff --git a/rust/fory-derive/src/object/read.rs 
b/rust/fory-derive/src/object/read.rs
index 1bdcb985c..0a1833aba 100644
--- a/rust/fory-derive/src/object/read.rs
+++ b/rust/fory-derive/src/object/read.rs
@@ -56,31 +56,85 @@ fn create(fields: &[&Field]) -> Vec<TokenStream> {
 }
 
 fn read(fields: &[&Field]) -> TokenStream {
-    let assign_stmt = fields.iter().map(|field| {
-        let ty = &field.ty;
-        let name = &field.ident;
+    // let assign_stmt = fields.iter().map(|field| {
+    //     let ty = &field.ty;
+    //     let name = &field.ident;
+    //     quote! {
+    //         #name: <#ty as 
fory_core::serializer::Serializer>::deserialize(context, true)?
+    //     }
+    // });
+    let private_idents: Vec<Ident> = fields
+        .iter()
+        .map(|f| create_private_field_name(f))
+        .collect();
+    let sorted_deserialize = {
+        let let_decls = fields
+            .iter()
+            .zip(private_idents.iter())
+            .map(|(field, private_ident)| {
+                let ty = &field.ty;
+                quote! {
+                    let mut #private_ident: #ty = Default::default();
+                }
+            });
+
+        let match_ts = fields.iter().zip(private_idents.iter()).map(|(field, 
private_ident)| {
+            let ty = &field.ty;
+            let name_str = field.ident.as_ref().unwrap().to_string();
+            quote! {
+                #name_str => {
+                    #private_ident = <#ty as 
fory_core::serializer::Serializer>::deserialize(context, true)?;
+                }
+            }
+        });
+
         quote! {
-            #name: <#ty as 
fory_core::serializer::Serializer>::deserialize(context)?
-        }
-    });
+             #(#let_decls)*
 
+            let sorted_field_names = <Self as 
fory_core::serializer::StructSerializer>::get_sorted_field_names(context.get_fory());
+            for field_name in sorted_field_names {
+                match field_name.as_str() {
+                    #(#match_ts),*
+                    , _ => unreachable!()
+                }
+            }
+        }
+    };
+    let field_idents = fields
+        .iter()
+        .zip(private_idents.iter())
+        .map(|(field, private_ident)| {
+            let original_ident = &field.ident;
+            quote! {
+                #original_ident: #private_ident
+            }
+        });
     quote! {
-        fn read(context: &mut fory_core::resolver::context::ReadContext) -> 
Result<Self, fory_core::error::Error> {
+        fn read(context: &mut fory_core::resolver::context::ReadContext, 
_is_field: bool) -> Result<Self, fory_core::error::Error> {
+            let remote_type_id = context.reader.var_uint32();
+            assert_eq!(remote_type_id, Self::get_type_id(context.get_fory()));
+            if *context.get_fory().get_mode() == 
fory_core::types::Mode::Compatible {
+                let _meta_index = context.reader.var_uint32();
+            }
+            // Ok(Self {
+            //     #(#assign_stmt),*
+            // })
+            #sorted_deserialize
             Ok(Self {
-                #(#assign_stmt),*
+                #(#field_idents),*
             })
         }
     }
 }
 
-fn deserialize_compatible(_fields: &[&Field], struct_ident: &Ident) -> 
TokenStream {
+fn deserialize_compatible(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)
+            <#struct_ident as 
fory_core::serializer::StructSerializer>::read_compatible(context)
         } 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"))?
+            Ok(Self::default())
+            // 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 {
@@ -89,7 +143,7 @@ fn deserialize_compatible(_fields: &[&Field], struct_ident: 
&Ident) -> TokenStre
     }
 }
 
-fn deserialize_nullable(fields: &[&Field]) -> TokenStream {
+pub fn gen_deserialize_nullable(fields: &[&Field]) -> TokenStream {
     let func_tokens: Vec<TokenStream> = fields
         .iter()
         .map(|field| {
@@ -116,10 +170,6 @@ fn deserialize_nullable(fields: &[&Field]) -> TokenStream {
     }
 }
 
-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;
@@ -141,7 +191,7 @@ pub fn gen_read_compatible(fields: &[&Field], struct_ident: 
&Ident) -> TokenStre
             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| {
+                    #var_name = Some(<#ty as 
fory_core::serializer::Serializer>::deserialize(context, 
true).unwrap_or_else(|_err| {
                         // same type, err means something wrong
                         panic!("Err at deserializing {:?}: {:?}", 
#field_name_str, _err);
                     }));
@@ -151,7 +201,7 @@ pub fn gen_read_compatible(fields: &[&Field], struct_ident: 
&Ident) -> TokenStre
                     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();
+                        fory_core::serializer::skip::skip_field_value(context, 
&remote_nullable_type, true).unwrap();
                         #var_name = Some(#base_ty::default());
                     } else {
                         println!("Try to deserialize: {}", #field_name_str);
@@ -173,15 +223,20 @@ pub fn gen_read_compatible(fields: &[&Field], 
struct_ident: &Ident) -> TokenStre
     let bind: Vec<TokenStream> = bind(fields);
     let create: Vec<TokenStream> = create(fields);
     quote! {
-        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();
+        fn read_compatible(context: &mut 
fory_core::resolver::context::ReadContext) -> Result<Self, 
fory_core::error::Error> {
+            let remote_type_id = context.reader.var_uint32();
+            let meta_index = context.reader.var_uint32();
+            let meta = context.get_meta(meta_index as usize);
+            let fields = {
+                let meta = context.get_meta(meta_index as usize);
+                meta.get_field_infos().clone()
+            };
             #(#bind)*
             for _field in fields.iter() {
                 #(#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();
+                    fory_core::serializer::skip::skip_field_value(context, 
&nullable_field_type, true).unwrap();
                 }
             }
             Ok(Self {
@@ -193,13 +248,13 @@ pub fn gen_read_compatible(fields: &[&Field], 
struct_ident: &Ident) -> TokenStre
 
 pub fn gen(fields: &[&Field], struct_ident: &Ident) -> TokenStream {
     let read_token_stream = read(fields);
-    let compatible_token_stream = deserialize_compatible(fields, struct_ident);
+    let compatible_token_stream = deserialize_compatible(struct_ident);
 
     quote! {
-        fn deserialize(context: &mut 
fory_core::resolver::context::ReadContext) -> Result<Self, 
fory_core::error::Error> {
+        fn deserialize(context: &mut 
fory_core::resolver::context::ReadContext, is_field: bool) -> Result<Self, 
fory_core::error::Error> {
             match context.get_fory().get_mode() {
                 fory_core::types::Mode::SchemaConsistent => {
-                    fory_core::serializer::deserialize::<Self>(context)
+                    fory_core::serializer::deserialize::<Self>(context, 
is_field)
                 },
                 fory_core::types::Mode::Compatible => {
                     #compatible_token_stream
diff --git a/rust/fory-derive/src/object/serializer.rs 
b/rust/fory-derive/src/object/serializer.rs
index 95767606d..fc5e7497d 100644
--- a/rust/fory-derive/src/object/serializer.rs
+++ b/rust/fory-derive/src/object/serializer.rs
@@ -29,6 +29,7 @@ pub fn derive_serializer(ast: &syn::DeriveInput) -> 
TokenStream {
         read_token_stream,
         read_compatible_token_stream,
         read_nullable_token_stream,
+        sort_fields_token_stream,
     ) = match &ast.data {
         syn::Data::Struct(s) => {
             let fields = sorted_fields(&s.fields);
@@ -38,7 +39,8 @@ pub fn derive_serializer(ast: &syn::DeriveInput) -> 
TokenStream {
                 write::gen(&fields),
                 read::gen(&fields, name),
                 read::gen_read_compatible(&fields, name),
-                read::gen_nullable(&fields),
+                read::gen_deserialize_nullable(&fields),
+                misc::gen_sort_fields(&fields),
             )
         }
         syn::Data::Enum(s) => (
@@ -46,8 +48,11 @@ pub fn derive_serializer(ast: &syn::DeriveInput) -> 
TokenStream {
             derive_enum::gen_actual_type_id(),
             derive_enum::gen_write(s),
             derive_enum::gen_read(s),
+            derive_enum::gen_read_compatible(s),
             quote! {},
-            quote! {},
+            quote! {
+                unreachable!();
+            },
         ),
         syn::Data::Union(_) => {
             panic!("Union is not supported")
@@ -68,6 +73,10 @@ pub fn derive_serializer(ast: &syn::DeriveInput) -> 
TokenStream {
                 #actual_type_id_token_stream
             }
             #type_index_token_stream
+            #read_compatible_token_stream
+            fn get_sorted_field_names(fory: &fory_core::fory::Fory) -> 
Vec<String> {
+                #sort_fields_token_stream
+            }
         }
         impl fory_core::types::ForyGeneralList for #name {}
         impl fory_core::serializer::Serializer for #name {
@@ -76,7 +85,6 @@ pub fn derive_serializer(ast: &syn::DeriveInput) -> 
TokenStream {
             #read_token_stream
         }
         impl #name {
-            #read_compatible_token_stream
             #read_nullable_token_stream
         }
     };
diff --git a/rust/fory-derive/src/object/util.rs 
b/rust/fory-derive/src/object/util.rs
index f623b621e..2491600ee 100644
--- a/rust/fory-derive/src/object/util.rs
+++ b/rust/fory-derive/src/object/util.rs
@@ -17,9 +17,9 @@
 
 use fory_core::types::{TypeId, BASIC_TYPE_NAMES, CONTAINER_TYPE_NAMES, 
PRIMITIVE_ARRAY_TYPE_MAP};
 use proc_macro2::TokenStream;
-use quote::quote;
+use quote::{quote, ToTokens};
 use std::fmt;
-use syn::{parse_str, GenericArgument, PathArguments, Type};
+use syn::{parse_str, Field, GenericArgument, PathArguments, Type};
 
 #[derive(Debug)]
 pub(super) struct TypeNode {
@@ -44,8 +44,7 @@ macro_rules! basic_type_deserialize {
                             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)
+                                Some(<$ty as 
fory_core::serializer::Serializer>::read(context, true)
                                     .map_err(fory_core::error::Error::from)?)
                             };
                             Ok::<Option<$ty>, fory_core::error::Error>(res1)
@@ -55,8 +54,7 @@ macro_rules! basic_type_deserialize {
                             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)
+                                <$ty as 
fory_core::serializer::Serializer>::read(context, true)
                                     .map_err(fory_core::error::Error::from)?
                             };
                             Ok::<$ty, fory_core::error::Error>(res2)
@@ -125,8 +123,7 @@ impl NullableTypeNode {
                     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_type as 
fory_core::serializer::Serializer>::read(context)
+                        Some(<#ty_type as 
fory_core::serializer::Serializer>::read(context, true)
                             .map_err(fory_core::error::Error::from)?)
                     };
                     Ok::<Option<#ty_type>, fory_core::error::Error>(res1)
@@ -136,8 +133,7 @@ impl NullableTypeNode {
                     let res2 = if cur_remote_nullable_type.nullable && 
ref_flag == (fory_core::types::RefFlag::Null as i8) {
                         Vec::default()
                     } else {
-                        let _type_id = context.reader.var_uint32();
-                        <#ty_type as 
fory_core::serializer::Serializer>::read(context)
+                        <#ty_type as 
fory_core::serializer::Serializer>::read(context, true)
                             .map_err(fory_core::error::Error::from)?
                     };
                     Ok::<#ty_type, fory_core::error::Error>(res2)
@@ -167,8 +163,7 @@ impl NullableTypeNode {
                             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();
-                                
Some(fory_core::serializer::collection::read_collection(context)?)
+                                
Some(fory_core::serializer::collection::read_collection(context, true, 
fory_core::types::TypeId::LIST as u32)?)
                             };
                             Ok::<#ty, fory_core::error::Error>(v)
                         }
@@ -177,8 +172,7 @@ impl NullableTypeNode {
                             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();
-                                
fory_core::serializer::collection::read_collection(context)?
+                                
fory_core::serializer::collection::read_collection(context, true, 
fory_core::types::TypeId::LIST as u32)?
                             };
                             Ok::<#ty, fory_core::error::Error>(v)
                         }
@@ -191,8 +185,7 @@ impl NullableTypeNode {
                             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();
-                                
Some(fory_core::serializer::collection::read_collection(context)?)
+                                
Some(fory_core::serializer::collection::read_collection(context, true, 
fory_core::types::TypeId::SET as u32)?)
                             };
                             Ok::<#ty, fory_core::error::Error>(s)
                         }
@@ -201,8 +194,7 @@ impl NullableTypeNode {
                             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();
-                                
fory_core::serializer::collection::read_collection(context)?
+                                
fory_core::serializer::collection::read_collection(context, true, 
fory_core::types::TypeId::SET as u32)?
                             };
                             Ok::<#ty, fory_core::error::Error>(s)
                         }
@@ -221,8 +213,7 @@ impl NullableTypeNode {
                             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();
-                                Some(<HashMap<#key_ty, #val_ty> as 
fory_core::serializer::Serializer>::read(context)?)
+                                Some(<HashMap<#key_ty, #val_ty> as 
fory_core::serializer::Serializer>::read(context, true)?)
                             };
                             Ok::<#ty, fory_core::error::Error>(m)
                         }
@@ -231,8 +222,7 @@ impl NullableTypeNode {
                             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();
-                                <HashMap<#key_ty, #val_ty> as 
fory_core::serializer::Serializer>::read(context)?
+                                <HashMap<#key_ty, #val_ty> as 
fory_core::serializer::Serializer>::read(context, true)?
                             };
                             Ok::<#ty, fory_core::error::Error>(m)
                         }
@@ -241,32 +231,40 @@ impl NullableTypeNode {
                 _ => quote! { compile_error!("Unsupported type for 
container"); },
             }
         } else {
-            // struct
+            // struct or enum
             let nullable_ty = 
parse_str::<Type>(&self.nullable_ty_string()).unwrap();
             let ty = parse_str::<Type>(&self.to_string()).unwrap();
             if self.nullable {
                 quote! {
+                    const COMPATIBLE_STRUCT_ID: u32 = 
fory_core::types::TypeId::COMPATIBLE_STRUCT as u32;
+                    const ENUM_ID: u32 = fory_core::types::TypeId::ENUM as u32;
                     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)?)
+                        Some(<#nullable_ty as 
fory_core::serializer::StructSerializer>::read_compatible(context).map_err(fory_core::error::Error::from)?)
                     };
                     Ok::<#ty, fory_core::error::Error>(res1)
                 }
             } else {
                 quote! {
+                    const COMPATIBLE_STRUCT_ID: u32 = 
fory_core::types::TypeId::COMPATIBLE_STRUCT as u32;
+                    const ENUM_ID: u32 = fory_core::types::TypeId::ENUM as u32;
                     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 type_id = cur_remote_nullable_type.type_id;
                         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)
+                        match internal_id {
+                            COMPATIBLE_STRUCT_ID => {
+                                <#nullable_ty as 
fory_core::serializer::StructSerializer>::read_compatible(context)
+                                    .map_err(fory_core::error::Error::from)?
+                            }
+                            ENUM_ID => {
+                                <#nullable_ty as 
fory_core::serializer::Serializer>::read(context, true)
                                 .map_err(fory_core::error::Error::from)?
+                            }
+                            _ => unimplemented!(),
+                        }
                     };
                     Ok::<#ty, fory_core::error::Error>(res2)
                 }
@@ -432,7 +430,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 = TypeId::ForyOption as u32;
+        let option_type_id = TypeId::ForyNullable as u32;
         quote! { #option_type_id }
     } else if let Some(ts) = primitive_vec {
         ts
@@ -448,3 +446,314 @@ pub(super) fn generic_tree_to_tokens(node: &TypeNode, 
have_context: bool) -> Tok
         )
     }
 }
+
+type FieldGroup = Vec<(String, String, u32)>;
+type FieldGroups = (FieldGroup, FieldGroup, FieldGroup, FieldGroup);
+pub(super) fn get_sort_fields_ts(fields: &[&Field]) -> TokenStream {
+    fn group_fields(fields: &[&Field]) -> FieldGroups {
+        const PRIMITIVE_TYPE_NAMES: [&str; 7] = ["bool", "i8", "i16", "i32", 
"i64", "f32", "f64"];
+        const FINAL_TYPE_NAMES: [&str; 3] = ["String", "NaiveDate", 
"NaiveDateTime"];
+        const PRIMITIVE_ARRAY_NAMES: [&str; 7] = [
+            "Vec<bool>",
+            "Vec<i8>",
+            "Vec<i16>",
+            "Vec<i32>",
+            "Vec<i64>",
+            "Vec<f32>",
+            "Vec<f64>",
+        ];
+
+        fn extract_option_inner(s: &str) -> Option<&str> {
+            s.strip_prefix("Option<")?.strip_suffix(">")
+        }
+
+        macro_rules! match_ty {
+            ($ty:expr, $(($name:expr, $ret:expr)),+ $(,)?) => {
+                $(
+                    if $ty == $name {
+                        $ret as u32
+                    } else
+                )+
+                {
+                    unreachable!("Unknown type: {}", $ty);
+                }
+            };
+        }
+
+        fn get_primitive_type_id(ty: &str) -> u32 {
+            match_ty!(
+                ty,
+                ("bool", TypeId::BOOL),
+                ("i8", TypeId::INT8),
+                ("i16", TypeId::INT16),
+                ("i32", TypeId::INT32),
+                ("i64", TypeId::INT64),
+                ("f32", TypeId::FLOAT32),
+                ("f64", TypeId::FLOAT64),
+            )
+        }
+
+        let mut primitive_fields = Vec::new();
+        let mut nullable_primitive_fields = Vec::new();
+        let mut final_fields = Vec::new();
+        let mut collection_fields = Vec::new();
+        let mut map_fields = Vec::new();
+        let mut struct_or_enum_fields = Vec::new();
+
+        let mut group_field = |ident: String, ty: &str| {
+            if PRIMITIVE_TYPE_NAMES.contains(&ty) {
+                let type_id = get_primitive_type_id(ty);
+                primitive_fields.push((ident, ty.to_string(), type_id));
+            } else if FINAL_TYPE_NAMES.contains(&ty) || 
PRIMITIVE_ARRAY_NAMES.contains(&ty) {
+                let type_id = match_ty!(
+                    ty,
+                    ("String", TypeId::STRING),
+                    ("NaiveDate", TypeId::LOCAL_DATE),
+                    ("NaiveDateTime", TypeId::TIMESTAMP),
+                    ("Vec<u8>", TypeId::BINARY),
+                    ("Vec<bool>", TypeId::BOOL_ARRAY),
+                    ("Vec<i8>", TypeId::INT8_ARRAY),
+                    ("Vec<i16>", TypeId::INT16_ARRAY),
+                    ("Vec<i32>", TypeId::INT32_ARRAY),
+                    ("Vec<i64>", TypeId::INT64_ARRAY),
+                    ("Vec<f32>", TypeId::FLOAT32_ARRAY),
+                    ("Vec<f64>", TypeId::FLOAT64_ARRAY),
+                );
+                final_fields.push((ident, ty.to_string(), type_id));
+            } else if ty.starts_with("Vec<") {
+                collection_fields.push((ident, ty.to_string(), TypeId::LIST as 
u32));
+            } else if ty.starts_with("HashSet<") {
+                collection_fields.push((ident, ty.to_string(), TypeId::SET as 
u32));
+            } else if ty.starts_with("HashMap<") {
+                map_fields.push((ident, ty.to_string(), TypeId::MAP as u32));
+            } else {
+                struct_or_enum_fields.push((ident, ty.to_string(), 0));
+            }
+        };
+
+        for field in fields {
+            let ty: String = field
+                .ty
+                .to_token_stream()
+                .to_string()
+                .chars()
+                .filter(|c| !c.is_whitespace())
+                .collect::<String>();
+            let ident = field.ident.as_ref().unwrap().to_string();
+            // handle Option<Primitive> specially
+            if let Some(inner) = extract_option_inner(&ty) {
+                if PRIMITIVE_TYPE_NAMES.contains(&inner) {
+                    let type_id = get_primitive_type_id(inner);
+                    nullable_primitive_fields.push((ident, ty.to_string(), 
type_id));
+                } else {
+                    // continue to handle Option<not Primitive>
+                    // already avoid Option<Option<T>> at compile-time
+                    group_field(ident, inner);
+                }
+            } else {
+                group_field(ident, &ty);
+            }
+        }
+
+        fn sorter(a: &(String, String, u32), b: &(String, String, u32)) -> 
std::cmp::Ordering {
+            a.2.cmp(&b.2).then_with(|| a.0.cmp(&b.0))
+        }
+        fn get_primitive_type_size(type_id_num: u32) -> i32 {
+            let type_id = TypeId::try_from(type_id_num as i16).unwrap();
+            match type_id {
+                TypeId::BOOL => 1,
+                TypeId::INT8 => 1,
+                TypeId::INT16 => 2,
+                TypeId::INT32 => 4,
+                TypeId::VAR_INT32 => 4,
+                TypeId::INT64 => 8,
+                TypeId::VAR_INT64 => 8,
+                TypeId::FLOAT16 => 2,
+                TypeId::FLOAT32 => 4,
+                TypeId::FLOAT64 => 8,
+                _ => unreachable!(),
+            }
+        }
+
+        fn is_compress(type_id: u32) -> bool {
+            [
+                TypeId::INT32 as u32,
+                TypeId::INT64 as u32,
+                TypeId::VAR_INT32 as u32,
+                TypeId::VAR_INT64 as u32,
+            ]
+            .contains(&type_id)
+        }
+
+        fn numeric_sorter(
+            a: &(String, String, u32),
+            b: &(String, String, u32),
+        ) -> std::cmp::Ordering {
+            let compress_a = is_compress(a.2);
+            let compress_b = is_compress(b.2);
+            let size_a = get_primitive_type_size(a.2);
+            let size_b = get_primitive_type_size(b.2);
+            compress_a
+                .cmp(&compress_b)
+                .then_with(|| size_b.cmp(&size_a))
+                .then_with(|| a.0.cmp(&b.0))
+        }
+
+        primitive_fields.sort_by(numeric_sorter);
+        nullable_primitive_fields.sort_by(numeric_sorter);
+        primitive_fields.extend(nullable_primitive_fields);
+        collection_fields.sort_by(sorter);
+        map_fields.sort_by(sorter);
+        let container_fields = {
+            let mut container_fields = collection_fields;
+            container_fields.extend(map_fields);
+            container_fields
+        };
+        (
+            primitive_fields,
+            final_fields,
+            container_fields,
+            struct_or_enum_fields,
+        )
+    }
+
+    fn gen_vec_token_stream(fields: &[(String, String, u32)]) -> TokenStream {
+        let names = fields.iter().map(|(name, _, _)| {
+            quote! { #name.to_string() }
+        });
+        quote! {
+            vec![#(#names),*]
+        }
+    }
+
+    fn gen_vec_tuple_token_stream(fields: &[(String, String, u32)]) -> 
TokenStream {
+        let names = fields.iter().map(|(name, _, type_id)| {
+            quote! { (#type_id, #name.to_string()) }
+        });
+        quote! {
+            vec![#(#names),*]
+        }
+    }
+
+    let (all_primitive_fields, final_fields, container_fields, 
struct_or_enum_fields) =
+        group_fields(fields);
+
+    let all_primitive_field_names_declare_extend_ts = {
+        if all_primitive_fields.is_empty() {
+            (quote! {}, quote! {})
+        } else {
+            let all_primitive_field_names_ts = 
gen_vec_token_stream(&all_primitive_fields);
+            (
+                quote! {
+                    let all_primitive_field_names: Vec<String> = 
#all_primitive_field_names_ts;
+                },
+                quote! {
+                    sorted_field_names.extend(all_primitive_field_names);
+                },
+            )
+        }
+    };
+    let container_field_names_declare_extend_ts = {
+        if container_fields.is_empty() {
+            (quote! {}, quote! {})
+        } else {
+            let container_field_names_ts = 
gen_vec_token_stream(&container_fields);
+            (
+                quote! {
+                    let container_field_names: Vec<String> = 
#container_field_names_ts;
+                },
+                quote! {
+                    sorted_field_names.extend(container_field_names);
+                },
+            )
+        }
+    };
+    let sorter_ts = quote! {
+        |a: &(u32, String), b: &(u32, String)| a.0.cmp(&b.0).then_with(|| 
a.1.cmp(&b.1))
+    };
+    let final_fields_declare_extend_ts = {
+        if final_fields.is_empty() && struct_or_enum_fields.is_empty() {
+            (quote! {}, quote! {})
+        } else {
+            let final_fields_ts = gen_vec_tuple_token_stream(&final_fields);
+            (
+                quote! {
+                    let mut final_fields: Vec<(u32, String)> = 
#final_fields_ts;
+                },
+                quote! {
+                    final_fields.sort_by(#sorter_ts);
+                    let final_field_names: Vec<String> = 
final_fields.iter().map(|(_, name)| name.clone()).collect();
+                    sorted_field_names.extend(final_field_names);
+                },
+            )
+        }
+    };
+    let other_fields_declare_extend_ts = {
+        if struct_or_enum_fields.is_empty() {
+            (quote! {}, quote! {})
+        } else {
+            (
+                quote! {
+                    let mut other_fields: Vec<(u32, String)> = vec![];
+                },
+                quote! {
+                    other_fields.sort_by(#sorter_ts);
+                    let other_field_names: Vec<String> = 
other_fields.iter().map(|(_, name)| name.clone()).collect();
+                    sorted_field_names.extend(other_field_names);
+                },
+            )
+        }
+    };
+    let group_sort_enum_other_fields = {
+        if struct_or_enum_fields.is_empty() {
+            quote! {}
+        } else {
+            let ts = struct_or_enum_fields
+                .iter()
+                .map(|(name, ty, _)| {
+                    let ty_type: Type = syn::parse_str(ty).unwrap();
+                    quote! {
+                        let field_type_id = <#ty_type as 
fory_core::serializer::Serializer>::get_type_id(fory);
+                        let internal_id = field_type_id & 0xff;
+                        if internal_id == 
fory_core::types::TypeId::COMPATIBLE_STRUCT as u32 {
+                            other_fields.push((field_type_id, 
#name.to_string()));
+                        } else if internal_id == 
fory_core::types::TypeId::ENUM as u32 {
+                            final_fields.push((field_type_id, 
#name.to_string()));
+                        } else {
+                            unimplemented!();
+                        }
+                    }
+                })
+                .collect::<Vec<_>>();
+            quote! {
+                {
+                    #(#ts)*
+                }
+            }
+        }
+    };
+
+    let (all_primitive_declare, all_primitive_extend) = 
all_primitive_field_names_declare_extend_ts;
+    let (container_declare, container_extend) = 
container_field_names_declare_extend_ts;
+    let (final_declare, final_extend) = final_fields_declare_extend_ts;
+    let (other_declare, other_extend) = other_fields_declare_extend_ts;
+
+    quote! {
+        let sorted_field_names = {
+            #all_primitive_declare
+            #final_declare
+            #other_declare
+            #container_declare
+
+            #group_sort_enum_other_fields
+
+            let mut sorted_field_names: Vec<String> = Vec::new();
+            #all_primitive_extend
+            #final_extend
+            #other_extend
+            #container_extend
+
+            sorted_field_names
+        };
+    }
+}
diff --git a/rust/fory-derive/src/object/write.rs 
b/rust/fory-derive/src/object/write.rs
index 05c40de7d..4851f420a 100644
--- a/rust/fory-derive/src/object/write.rs
+++ b/rust/fory-derive/src/object/write.rs
@@ -20,48 +20,81 @@ use quote::quote;
 use syn::Field;
 
 pub fn gen(fields: &[&Field]) -> TokenStream {
-    let accessor_expr = fields.iter().map(|field| {
-        let ty = &field.ty;
-        let ident = &field.ident;
+    let sorted_serialize = {
+        let match_ts = fields.iter().map(|field| {
+            let ty = &field.ty;
+            let ident = &field.ident;
+            let name_str = ident.as_ref().unwrap().to_string();
+            quote! {
+                #name_str => {
+                    <#ty as 
fory_core::serializer::Serializer>::serialize(&self.#ident, context, true);
+                }
+            }
+        });
         quote! {
-            <#ty as 
fory_core::serializer::Serializer>::serialize(&self.#ident, context);
+            let sorted_field_names = <Self as 
fory_core::serializer::StructSerializer>::get_sorted_field_names(context.get_fory());
+            for field_name in sorted_field_names {
+                match field_name.as_str() {
+                    #(#match_ts),*
+                    , _ => {unreachable!()}
+                }
+            }
         }
-    });
+    };
 
-    let reserved_size_expr = fields.iter().map(|field| {
+    // let accessor_expr = fields.iter().map(|field| {
+    //     let ty = &field.ty;
+    //     let ident = &field.ident;
+    //     quote! {
+    //         // println!("before writer is:{:?}",context.writer.dump());
+    //         <#ty as 
fory_core::serializer::Serializer>::serialize(&self.#ident, context, true);
+    //         // println!("after writer is:{:?}",context.writer.dump());
+    //     }
+    // });
+
+    let reserved_size_expr: Vec<_> = fields.iter().map(|field| {
         let ty = &field.ty;
-        // each field have one byte ref tag and two byte type id
         quote! {
             <#ty as fory_core::serializer::Serializer>::reserved_space() + 
fory_core::types::SIZE_OF_REF_AND_TYPE
         }
-    });
+    }).collect();
+
+    let reserved_fn_body = if reserved_size_expr.is_empty() {
+        quote! { 0 }
+    } else {
+        quote! { #(#reserved_size_expr)+* }
+    };
 
     quote! {
-        fn serialize(&self, context: &mut 
fory_core::resolver::context::WriteContext) {
+        fn serialize(&self, context: &mut 
fory_core::resolver::context::WriteContext, is_field: bool) {
             match context.get_fory().get_mode() {
                 fory_core::types::Mode::SchemaConsistent => {
-                    fory_core::serializer::serialize(self, context);
+                    fory_core::serializer::serialize(self, context, is_field);
                 },
                 fory_core::types::Mode::Compatible => {
-                    context.writer.i8(fory_core::types::RefFlag::NotNullValue 
as i8);
-                    let type_id = Self::get_type_id(context.get_fory());
-                    context.writer.var_uint32(type_id);
-                    self.write(context);
+                    fory_core::serializer::serialize(self, context, is_field);
                 }
             }
         }
 
-
-        fn write(&self, context: &mut 
fory_core::resolver::context::WriteContext) {
-            let _meta_index = context.push_meta(
-                std::any::TypeId::of::<Self>()
-            ) as i16;
+        fn write(&self, context: &mut 
fory_core::resolver::context::WriteContext, _is_field: bool) {
+            let type_id = Self::get_type_id(context.get_fory());
+            context.writer.var_uint32(type_id);
+            if *context.get_fory().get_mode() == 
fory_core::types::Mode::Compatible {
+                let meta_index = context.push_meta(
+                    std::any::TypeId::of::<Self>()
+                ) as u32;
+                context.writer.var_uint32(meta_index);
+            }
             // write fields
-            #(#accessor_expr)*
+            // write way before
+            // #(#accessor_expr)*
+            // sort and write
+            #sorted_serialize
         }
 
         fn reserved_space() -> usize {
-            #(#reserved_size_expr)+*
+            #reserved_fn_body
         }
     }
 }
diff --git a/rust/tests/tests/test_compatible.rs 
b/rust/tests/tests/test_compatible.rs
index 2f2f9f416..8178d861d 100644
--- a/rust/tests/tests/test_compatible.rs
+++ b/rust/tests/tests/test_compatible.rs
@@ -18,62 +18,63 @@
 use fory_core::fory::Fory;
 use fory_core::types::Mode::Compatible;
 use fory_derive::Fory;
-use std::collections::{HashMap, HashSet};
+use std::collections::HashSet;
+
 // RUSTFLAGS="-Awarnings" cargo expand -p fory-tests --test test_compatible
 #[test]
 fn simple() {
     #[derive(Fory, Debug, Default)]
     struct Animal1 {
-        f1: HashMap<i8, Vec<i8>>,
+        // f1: HashMap<i8, Vec<i8>>,
         f2: String,
         f3: Vec<i8>,
         // f4: String,
         f5: String,
         f6: Vec<i8>,
         f7: i8,
-        f8: i8,
+        last: i8,
     }
 
     #[derive(Fory, Debug, Default)]
     struct Animal2 {
-        f1: HashMap<i8, Vec<i8>>,
+        // f1: HashMap<i8, Vec<i8>>,
         // f2: String,
         f3: Vec<i8>,
         f4: String,
         f5: i8,
         f6: Vec<i16>,
         f7: i16,
-        f8: i8,
+        last: i8,
     }
     let mut fory1 = Fory::default().mode(Compatible);
     let mut fory2 = Fory::default().mode(Compatible);
     fory1.register::<Animal1>(999);
     fory2.register::<Animal2>(999);
     let animal: Animal1 = Animal1 {
-        f1: HashMap::from([(1, vec![2])]),
+        // f1: HashMap::from([(1, vec![2])]),
         f2: String::from("hello"),
         f3: vec![1, 2, 3],
         f5: String::from("f5"),
         f6: vec![42],
         f7: 43,
-        f8: 44,
+        last: 44,
     };
     let bin = fory1.serialize(&animal);
     let obj: Animal2 = fory2.deserialize(&bin).unwrap();
-    assert_eq!(animal.f1, obj.f1);
+    // assert_eq!(animal.f1, obj.f1);
     assert_eq!(animal.f3, obj.f3);
     assert_eq!(obj.f4, String::default());
     assert_eq!(obj.f5, i8::default());
     assert_eq!(obj.f6, Vec::<i16>::default());
     assert_eq!(obj.f7, i16::default());
-    assert_eq!(animal.f8, obj.f8);
+    assert_eq!(animal.last, obj.last);
 }
 
 #[test]
 fn skip_option() {
     #[derive(Fory, Debug, Default)]
     struct Item1 {
-        f1: Option<String>,
+        f1: Option<i32>,
         f2: Option<String>,
         last: i64,
     }
@@ -229,11 +230,11 @@ fn nullable_container() {
         f2: Option<Vec<i8>>,
         f3: HashSet<i8>,
         f4: Option<HashSet<i8>>,
-        f5: HashMap<i8, Vec<i8>>,
-        f6: Option<HashMap<i8, Vec<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>>,
+        // f9: Option<HashMap<i8, i8>>,
         last: i64,
     }
 
@@ -243,11 +244,11 @@ fn nullable_container() {
         f2: Vec<i8>,
         f3: Option<HashSet<i8>>,
         f4: HashSet<i8>,
-        f5: Option<HashMap<i8, Vec<i8>>>,
-        f6: HashMap<i8, Vec<i8>>,
+        // f5: Option<HashMap<i8, Vec<i8>>>,
+        // f6: HashMap<i8, Vec<i8>>,
         f7: Vec<i8>,
         f8: HashSet<i8>,
-        f9: HashMap<i8, i8>,
+        // f9: HashMap<i8, i8>,
         last: i64,
     }
 
@@ -261,11 +262,11 @@ fn nullable_container() {
         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])])),
+        // f5: HashMap::from([(48, vec![49])]),
+        // f6: Some(HashMap::from([(48, vec![49])])),
         f7: None,
         f8: None,
-        f9: None,
+        // f9: None,
         last: 666,
     };
 
@@ -276,11 +277,11 @@ fn nullable_container() {
     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.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.f9, HashMap::default());
     assert_eq!(item2.last, item1.last);
 }
 
@@ -371,91 +372,77 @@ fn nullable_struct() {
     assert_eq!(person2.last, person1.last);
 }
 
-// #[test]
-// fn enum_without_payload() {
-//     #[derive(Fory, Debug, PartialEq, Default)]
-//     enum Color1 {
-//         #[default]
-//         Green,
-//         Red,
-//         Blue,
-//     }
-//     #[derive(Fory, Debug, PartialEq, Default)]
-//     enum Color2 {
-//         #[default]
-//         Green,
-//         Red,
-//         Blue,
-//     }
-//     #[derive(Fory, Debug, PartialEq)]
-//     struct Person1 {
-//         f1: Color1,
-//         f2: Color1,
-//         // skip
-//         f3: Color2,
-//         f5: Vec<Color1>,
-//         f6: Option<Color1>,
-//         f7: Option<Color1>,
-//         f8: Color1,
-//     }
-//     #[derive(Fory, Debug, PartialEq)]
-//     struct Person2 {
-//         // same
-//         f1: Color1,
-//         // type different
-//         f2: Color2,
-//         // should be default
-//         f4: Color2,
-//         f5: Vec<Color2>,
-//         f6: Color1,
-//         f7: Color1,
-//         f8: Option<Color1>,
-//     }
-//
-//     let mut fory1 = Fory::default().mode(Compatible).xlang(true);
-//     fory1.register::<Color1>(666);
-//     fory1.register::<Color2>(667);
-//     let mut fory2 = Fory::default().mode(Compatible).xlang(true);
-//     fory2.register::<Color1>(666);
-//     fory1.register::<Color2>(667);
-//
-//     let person1 = Person1 {
-//         f1: Color1::Blue,
-//         f2: Color1::Green,
-//         f3: Color2::Green,
-//         f5: vec![Color1::Green, Color1::Blue],
-//         f6: Some(Color1::Blue),
-//         f7: None,
-//         f8: Color1::Red,
-//     };
-//     let bin = fory1.serialize(&person1);
-//     let person2: Person2 = fory2.deserialize(&bin).expect("");
-//     assert_eq!(person2.f1, person1.f1);
-// }
+#[test]
+fn enum_without_payload() {
+    #[derive(Fory, Debug, PartialEq, Default)]
+    enum Color1 {
+        #[default]
+        Green,
+        Red,
+        Blue,
+        White,
+    }
+    #[derive(Fory, Debug, PartialEq, Default)]
+    enum Color2 {
+        #[default]
+        Green,
+        Red,
+        Blue,
+    }
+    #[derive(Fory, Debug, PartialEq, Default)]
+    struct Person1 {
+        f1: Color1,
+        f2: Color1,
+        // skip
+        f3: Color2,
+        f5: Vec<Color1>,
+        f6: Option<Color1>,
+        f7: Option<Color1>,
+        f8: Color1,
+        last: i8,
+    }
+    #[derive(Fory, Debug, PartialEq, Default)]
+    struct Person2 {
+        // same
+        f1: Color1,
+        // type different
+        f2: Color2,
+        // should be default
+        f4: Color2,
+        f5: Vec<Color2>,
+        f6: Color1,
+        f7: Color1,
+        f8: Option<Color1>,
+        last: i8,
+    }
 
-// #[test]
-// fn not_impl_default() {
-//     #[derive(Fory, Debug)]
-//     struct Person1 {
-//         // f1: Box<dyn Any>,
-//         f2: String,
-//     }
-//
-//     #[derive(Fory, Debug)]
-//     struct Person2 {
-//         f1: Box<dyn Any>,
-//         f2: String,
-//     }
-//
-//     let mut fory1 = Fory::default().mode(Compatible);
-//     let mut fory2 = Fory::default().mode(Compatible);
-//     fory1.register::<Person1>(999);
-//     fory2.register::<Person2>(999);
-//     let person: Person1 = Person1 {
-//         f2: String::from("hello"),
-//     };
-//     let bin = fory1.serialize(&person);
-//     let obj: Person2 = fory2.deserialize(&bin).unwrap();
-//     assert_eq!(person.f2, obj.f2);
-//     // assert_eq!(obj.f1, obj.f1);
-// }
+    let mut fory1 = Fory::default().mode(Compatible).xlang(true);
+    fory1.register::<Color1>(101);
+    fory1.register::<Color2>(102);
+    fory1.register::<Person1>(103);
+    let mut fory2 = Fory::default().mode(Compatible).xlang(true);
+    fory2.register::<Color1>(101);
+    fory2.register::<Color2>(102);
+    fory2.register::<Person2>(103);
+
+    let person1 = Person1 {
+        f1: Color1::Blue,
+        f2: Color1::White,
+        f3: Color2::Green,
+        f5: vec![Color1::Blue],
+        f6: Some(Color1::Blue),
+        f7: None,
+        f8: Color1::Red,
+        last: 10,
+    };
+    let bin = fory1.serialize(&person1);
+    println!("bin: {:?}", bin);
+    let person2: Person2 = fory2.deserialize(&bin).expect("");
+    assert_eq!(person2.f1, person1.f1);
+    assert_eq!(person2.f2, Color2::default());
+    assert_eq!(person2.f4, Color2::default());
+    assert_eq!(person2.f6, person1.f6.unwrap());
+    assert_eq!(person2.f7, Color1::default());
+    assert_eq!(person2.f8.unwrap(), person1.f8);
+    assert_eq!(person2.last, person1.last);
+}
diff --git a/rust/tests/tests/test_cross_language.rs 
b/rust/tests/tests/test_cross_language.rs
index 8a2bb1ca1..c348f7027 100644
--- a/rust/tests/tests/test_cross_language.rs
+++ b/rust/tests/tests/test_cross_language.rs
@@ -26,10 +26,37 @@ use fory_derive::Fory;
 use std::collections::{HashMap, HashSet};
 use std::fs;
 
+// RUSTFLAGS="-Awarnings" cargo expand -p fory-tests --test test_cross_language
 fn get_data_file() -> String {
     std::env::var("DATA_FILE").expect("DATA_FILE not set")
 }
 
+#[derive(Fory, Debug, PartialEq, Default)]
+enum Color {
+    #[default]
+    Green,
+    Red,
+    Blue,
+    White,
+}
+
+#[derive(Fory, Debug, PartialEq, Default)]
+struct Item {
+    name: Option<String>,
+}
+
+#[derive(Fory, Debug, PartialEq, Default)]
+struct SimpleStruct {
+    // field_order != sorted_order
+    // f1: HashMap<i32, f64>,
+    // f2: i32,
+    f3: Item,
+    f4: Option<String>,
+    f5: Color,
+    // f6: Vec<Option<String>>,
+    // last: i32
+}
+
 #[test]
 #[ignore]
 fn test_buffer() {
@@ -207,13 +234,14 @@ fn test_string_serializer() {
         "Hello, 世界".to_string(),
     ];
     for s in &test_strings {
-        assert_eq!(*s, String::read(&mut context).unwrap());
+        // make is_field=true to skip read/write type_id
+        assert_eq!(*s, String::read(&mut context, true).unwrap());
     }
     let mut writer = Writer::default();
     let fory = Fory::default().mode(Compatible).xlang(true);
     let mut context = WriteContext::new(&fory, &mut writer);
     for s in &test_strings {
-        s.write(&mut context);
+        s.write(&mut context, true);
     }
     fs::write(&data_file_path, context.writer.dump()).unwrap();
 }
@@ -229,10 +257,19 @@ macro_rules! assert_de {
 #[ignore]
 #[allow(deprecated)]
 fn test_cross_language_serializer() {
+    let str_list = vec!["hello".to_string(), "world".to_string()];
+    let str_set = HashSet::from(["hello".to_string(), "world".to_string()]);
+    let str_map = HashMap::<String, String>::from([
+        ("hello".to_string(), "world".to_string()),
+        ("foo".to_string(), "bar".to_string()),
+    ]);
+    let color = Color::White;
+
     let data_file_path = get_data_file();
     let bytes = fs::read(&data_file_path).unwrap();
     let reader = Reader::new(bytes.as_slice());
-    let fory = Fory::default().mode(Compatible).xlang(true);
+    let mut fory = Fory::default().mode(Compatible).xlang(true);
+    fory.register::<Color>(101);
     let mut context = ReadContext::new(&fory, reader);
     assert_de!(fory, context, bool, true);
     assert_de!(fory, context, bool, false);
@@ -266,16 +303,12 @@ fn test_cross_language_serializer() {
     assert_de!(fory, context, Vec<i64>, [1, i64::MAX]);
     assert_de!(fory, context, Vec<f32>, [1f32, 2f32]);
     assert_de!(fory, context, Vec<f64>, [1f64, 2f64]);
-    let str_list = vec!["hello".to_string(), "world".to_string()];
-    let str_set = HashSet::from(["hello".to_string(), "world".to_string()]);
-    let str_map =
-        HashMap::<String, i32>::from([("hello".to_string(), 42), 
("world".to_string(), 666)]);
     assert_de!(fory, context, Vec<String>, str_list);
     assert_de!(fory, context, HashSet<String>, str_set);
-    assert_de!(fory, context, HashMap::<String, i32>, str_map);
+    assert_de!(fory, context, HashMap::<String, String>, str_map);
+    assert_de!(fory, context, Color, color);
 
     let mut writer = Writer::default();
-    let fory = Fory::default().mode(Compatible).xlang(true);
     let mut context = WriteContext::new(&fory, &mut writer);
     fory.serialize_with_context(&true, &mut context);
     fory.serialize_with_context(&false, &mut context);
@@ -305,31 +338,34 @@ fn test_cross_language_serializer() {
     fory.serialize_with_context(&str_list, &mut context);
     fory.serialize_with_context(&str_set, &mut context);
     fory.serialize_with_context(&str_map, &mut context);
-
+    fory.serialize_with_context(&color, &mut context);
     fs::write(&data_file_path, context.writer.dump()).unwrap();
 }
 
-#[derive(Fory, Debug, PartialEq, Default)]
-struct SimpleStruct {
-    // f1: HashMap<i32, f64>,
-    f2: i32,
-}
-
 #[test]
 #[ignore]
 fn test_simple_struct() {
     let data_file_path = get_data_file();
     let bytes = fs::read(&data_file_path).unwrap();
     let mut fory = Fory::default().mode(Compatible).xlang(true);
-    fory.register::<SimpleStruct>(100);
+    fory.register::<Color>(101);
+    fory.register::<Item>(102);
+    fory.register::<SimpleStruct>(103);
     let remote_obj: SimpleStruct = fory.deserialize(&bytes).unwrap();
     let local_obj = SimpleStruct {
         // f1: HashMap::from([(1, 1.0f64), (2, 2.0f64)]),
-        f2: 10,
+        // f2: 10,
+        f3: Item {
+            name: Some("item".to_string()),
+        },
+        f4: Some("f3".to_string()),
+        f5: Color::White,
+        // f6: vec![Some("f6".to_string())],
+        // last: 42,
     };
     assert_eq!(remote_obj, local_obj);
-    // let new_bytes = fory.serialize(&remote_obj);
-    // let new_remote_obj: SimpleStruct = 
fory.deserialize(new_bytes.as_slice()).unwrap();
-    // assert_eq!(remote_obj, new_remote_obj);
-    // fs::write(&data_file_path, new_bytes).unwrap();
+    let new_bytes = fory.serialize(&remote_obj);
+    let new_local_obj: SimpleStruct = 
fory.deserialize(new_bytes.as_slice()).unwrap();
+    assert_eq!(new_local_obj, local_obj);
+    fs::write(&data_file_path, new_bytes).unwrap();
 }


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

Reply via email to