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

wangweipeng 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 1b9c0d3cb feat(JavaScript): align xlang protocol (#3316)
1b9c0d3cb is described below

commit 1b9c0d3cb4bc4a3ce00abfb91226ff813b260a76
Author: weipeng <[email protected]>
AuthorDate: Mon Feb 9 23:23:57 2026 +0800

    feat(JavaScript): align xlang protocol (#3316)
    
    ## Why?
    
    
    
    ## What does this PR do?
    1. More test case were passed, the current progross is 26/39
    
    ## Related issues
    #3133
    ## 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?
    
    ## Benchmark
---
 .../org/apache/fory/xlang/JavaScriptXlangTest.java |  74 ++++++-
 javascript/packages/fory/lib/gen/number.ts         |   4 +-
 javascript/packages/fory/lib/meta/TypeMeta.ts      | 159 +++++++++-----
 javascript/packages/fory/lib/type.ts               |  13 ++
 javascript/test/crossLanguage.test.ts              | 235 +++++++++++++++------
 5 files changed, 358 insertions(+), 127 deletions(-)

diff --git 
a/java/fory-core/src/test/java/org/apache/fory/xlang/JavaScriptXlangTest.java 
b/java/fory-core/src/test/java/org/apache/fory/xlang/JavaScriptXlangTest.java
index 946777f00..8cb87be1c 100644
--- 
a/java/fory-core/src/test/java/org/apache/fory/xlang/JavaScriptXlangTest.java
+++ 
b/java/fory-core/src/test/java/org/apache/fory/xlang/JavaScriptXlangTest.java
@@ -22,10 +22,12 @@ package org.apache.fory.xlang;
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
+import org.apache.fory.Fory;
+import org.apache.fory.config.CompatibleMode;
+import org.apache.fory.config.Language;
+import org.apache.fory.memory.MemoryBuffer;
+import org.testng.Assert;
 import org.testng.SkipException;
 import org.testng.annotations.Test;
 
@@ -244,7 +246,69 @@ public class JavaScriptXlangTest extends XlangTestBase {
   @Override
   @Test(dataProvider = "enableCodegen")
   public void testNullableFieldCompatibleNull(boolean enableCodegen) throws 
java.io.IOException {
-    super.testNullableFieldCompatibleNull(enableCodegen);
+    // JavaScript properly supports Optional and sends actual null values,
+    // unlike Rust which sends default values. Override with 
JavaScript-specific expectations.
+    String caseName = "test_nullable_field_compatible_null";
+    Fory fory =
+        Fory.builder()
+            .withLanguage(Language.XLANG)
+            .withCompatibleMode(CompatibleMode.COMPATIBLE)
+            .withCodegen(enableCodegen)
+            .withMetaCompressor(new NoOpMetaCompressor())
+            .build();
+    fory.register(NullableComprehensiveCompatible.class, 402);
+
+    NullableComprehensiveCompatible obj = new 
NullableComprehensiveCompatible();
+    // Base non-nullable primitive fields - must have values
+    obj.byteField = 1;
+    obj.shortField = 2;
+    obj.intField = 42;
+    obj.longField = 123456789L;
+    obj.floatField = 1.5f;
+    obj.doubleField = 2.5;
+    obj.boolField = true;
+
+    // Base non-nullable boxed fields - must have values
+    obj.boxedInt = 10;
+    obj.boxedLong = 20L;
+    obj.boxedFloat = 1.1f;
+    obj.boxedDouble = 2.2;
+    obj.boxedBool = true;
+
+    // Base non-nullable reference fields - must have values
+    obj.stringField = "hello";
+    obj.listField = Arrays.asList("a", "b", "c");
+    obj.setField = new HashSet<>(Arrays.asList("x", "y"));
+    obj.mapField = new HashMap<>();
+    obj.mapField.put("key1", "value1");
+    obj.mapField.put("key2", "value2");
+
+    // Nullable group 1 - all null
+    obj.nullableInt1 = null;
+    obj.nullableLong1 = null;
+    obj.nullableFloat1 = null;
+    obj.nullableDouble1 = null;
+    obj.nullableBool1 = null;
+
+    // Nullable group 2 - all null
+    obj.nullableString2 = null;
+    obj.nullableList2 = null;
+    obj.nullableSet2 = null;
+    obj.nullableMap2 = null;
+
+    MemoryBuffer buffer = MemoryBuffer.newHeapBuffer(1024);
+    fory.serialize(buffer, obj);
+
+    ExecutionContext ctx = prepareExecution(caseName, buffer.getBytes(0, 
buffer.writerIndex()));
+    runPeer(ctx);
+
+    MemoryBuffer buffer2 = readBuffer(ctx.dataFile());
+    NullableComprehensiveCompatible result =
+        (NullableComprehensiveCompatible) fory.deserialize(buffer2);
+
+    // JavaScript properly supports Optional and sends actual null values
+    // (unlike Rust which sends default values)
+    Assert.assertEquals(result, obj);
   }
 
   @Test(dataProvider = "enableCodegen")
diff --git a/javascript/packages/fory/lib/gen/number.ts 
b/javascript/packages/fory/lib/gen/number.ts
index a33033975..4c5f6e3a0 100644
--- a/javascript/packages/fory/lib/gen/number.ts
+++ b/javascript/packages/fory/lib/gen/number.ts
@@ -158,7 +158,7 @@ CodegenRegistry.register(TypeId.TAGGED_UINT64,
 
 CodegenRegistry.register(TypeId.VARINT64,
   buildNumberSerializer(
-    (builder, accessor) => builder.writer.varUInt64(accessor),
-    builder => builder.reader.varUInt64()
+    (builder, accessor) => builder.writer.varInt64(accessor),
+    builder => builder.reader.varInt64()
   )
 );
diff --git a/javascript/packages/fory/lib/meta/TypeMeta.ts 
b/javascript/packages/fory/lib/meta/TypeMeta.ts
index d132b5afe..63fbf99c3 100644
--- a/javascript/packages/fory/lib/meta/TypeMeta.ts
+++ b/javascript/packages/fory/lib/meta/TypeMeta.ts
@@ -25,17 +25,17 @@ import { TypeId } from "../type";
 import { x64hash128 } from "../murmurHash3";
 import { fromString } from "../platformBuffer";
 
-const fieldEncoder = new MetaStringEncoder("$", ".");
-const fieldDecoder = new MetaStringDecoder("$", ".");
-const pkgEncoder = new MetaStringEncoder("_", ".");
-const pkgDecoder = new MetaStringDecoder("_", ".");
-const typeNameEncoder = new MetaStringEncoder("_", ".");
-const typeNameDecoder = new MetaStringDecoder("_", ".");
+const fieldEncoder = new MetaStringEncoder("$", "_");
+const fieldDecoder = new MetaStringDecoder("$", "_");
+const pkgEncoder = new MetaStringEncoder(".", "_");
+const pkgDecoder = new MetaStringDecoder(".", "_");
+const typeNameEncoder = new MetaStringEncoder("$", ".");
+const typeNameDecoder = new MetaStringDecoder("$", ".");
 
 // Constants from Java implementation
 const COMPRESS_META_FLAG = 1n << 63n;
 const HAS_FIELDS_META_FLAG = 1n << 62n;
-const META_SIZE_MASKS = 0xFFF; // 22 bits
+const META_SIZE_MASKS = 0xFF; // 22 bits
 const NUM_HASH_BITS = 41;
 const BIG_NAME_THRESHOLD = 0b111111;
 
@@ -55,29 +55,6 @@ export const refTrackingAbleTypeId = (typeId: number): 
boolean => {
   return PRIMITIVE_TYPE_IDS.includes(typeId as any) || [TypeId.DURATION, 
TypeId.DATE, TypeId.TIMESTAMP, TypeId.STRING].includes(typeId as any);
 };
 
-export const isInternalTypeId = (typeId: number): boolean => {
-  return [
-    TypeId.STRING,
-    TypeId.TIMESTAMP,
-    TypeId.DURATION,
-    TypeId.DECIMAL,
-    TypeId.BINARY,
-    TypeId.BOOL_ARRAY,
-    TypeId.INT8_ARRAY,
-    TypeId.INT16_ARRAY,
-    TypeId.INT32_ARRAY,
-    TypeId.INT64_ARRAY,
-    TypeId.FLOAT8_ARRAY,
-    TypeId.FLOAT16_ARRAY,
-    TypeId.BFLOAT16_ARRAY,
-    TypeId.FLOAT32_ARRAY,
-    TypeId.FLOAT64_ARRAY,
-    TypeId.UINT16_ARRAY,
-    TypeId.UINT32_ARRAY,
-    TypeId.UINT64_ARRAY,
-  ].includes(typeId as any);
-};
-
 function getPrimitiveTypeSize(typeId: number) {
   switch (typeId) {
     case TypeId.BOOL:
@@ -370,7 +347,9 @@ export class TypeMeta {
     } else {
       // Read field name
       const encoding = FieldInfo.u8ToEncoding(encodingFlags);
+
       fieldName = fieldDecoder.decode(reader, size + 1, encoding || 
Encoding.UTF_8);
+      fieldName = TypeMeta.lowerUnderscoreToLowerCamelCase(fieldName);
     }
 
     return new FieldInfo(fieldName, typeId, userTypeId, trackingRef, nullable, 
options, fieldId);
@@ -525,7 +504,7 @@ export class TypeMeta {
   }
 
   writeFieldName(writer: BinaryWriter, fieldName: string) {
-    const name = this.lowerCamelToLowerUnderscore(fieldName);
+    const name = TypeMeta.lowerCamelToLowerUnderscore(fieldName);
     const metaString = fieldEncoder.encodeByEncodings(name, fieldNameEncoding);
     const encoded = metaString.getBytes();
     const encoding = fieldNameEncoding.indexOf(metaString.getEncoding());
@@ -546,7 +525,7 @@ export class TypeMeta {
         encodingFlags = 3; // TAG_ID encoding
       } else {
         // Convert camelCase to snake_case for xlang compatibility
-        const fieldName = 
this.lowerCamelToLowerUnderscore(fieldInfo.getFieldName());
+        const fieldName = 
TypeMeta.lowerCamelToLowerUnderscore(fieldInfo.getFieldName());
         const metaString = fieldEncoder.encodeByEncodings(fieldName, 
fieldNameEncoding);
         encodingFlags = fieldNameEncoding.indexOf(metaString.getEncoding());
         encoded = metaString.getBytes();
@@ -573,8 +552,63 @@ export class TypeMeta {
     }
   }
 
-  private lowerCamelToLowerUnderscore(str: string): string {
-    return str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
+  static lowerUnderscoreToLowerCamelCase(lowerUnderscore: string) {
+    let result = "";
+    const length = lowerUnderscore.length;
+
+    let fromIndex = 0;
+    let index;
+
+    while ((index = lowerUnderscore.indexOf("_", fromIndex)) !== -1) {
+      // 拼接下划线前的内容
+      result += lowerUnderscore.substring(fromIndex, index);
+
+      if (length > index + 1) {
+        const symbol = lowerUnderscore.charAt(index + 1);
+        // 判断是否为小写字母
+        if (symbol >= "a" && symbol <= "z") {
+          result += symbol.toUpperCase();
+          fromIndex = index + 2;
+          continue;
+        }
+      }
+
+      fromIndex = index + 1;
+    }
+
+    // 处理剩余部分
+    if (fromIndex < length) {
+      result += lowerUnderscore.substring(fromIndex, length);
+    }
+
+    return result;
+  }
+
+  static lowerCamelToLowerUnderscore(lowerCamel: string) {
+    let result = "";
+    const length = lowerCamel.length;
+    let fromIndex = 0;
+
+    for (let i = 0; i < length; i++) {
+      const symbol = lowerCamel.charAt(i);
+
+      // 检查是否为大写字母
+      if (symbol >= "A" && symbol <= "Z") {
+        // 拼接从上一个索引到当前大写字母前的部分,加下划线,加小写化后的字母
+        result += lowerCamel.substring(fromIndex, i);
+        result += "_";
+        result += symbol.toLowerCase();
+        // 更新起始索引
+        fromIndex = i + 1;
+      }
+    }
+
+    // 处理剩余部分
+    if (fromIndex < length) {
+      result += lowerCamel.substring(fromIndex, length);
+    }
+
+    return result;
   }
 
   private prependHeader(buffer: Uint8Array, isCompressed: boolean, 
hasFieldsMeta: boolean): Uint8Array {
@@ -626,7 +660,14 @@ export class TypeMeta {
     return result.join("");
   }
 
-  static groupFieldsByType<T extends { fieldName: string; nullable?: boolean; 
typeId: number }>(typeInfos: Array<T>): Array<T> {
+  static getFieldSortKey(i: { fieldName: string; fieldId?: number }) {
+    if (i.fieldId !== undefined && i.fieldId !== null) {
+      return `${i.fieldId}`;
+    }
+    return TypeMeta.toSnakeCase(i.fieldName);
+  }
+
+  static groupFieldsByType<T extends { fieldName: string; nullable?: boolean; 
typeId: number; fieldId?: number }>(typeInfos: Array<T>): Array<T> {
     const primitiveFields: Array<T> = [];
     const nullablePrimitiveFields: Array<T> = [];
     const internalTypeFields: Array<T> = [];
@@ -650,7 +691,7 @@ export class TypeMeta {
       }
 
       // Categorize based on type_id
-      if (isInternalTypeId(typeId)) {
+      if (TypeId.isBuiltin(typeId)) {
         internalTypeFields.push(typeInfo);
       } else if (typeId === TypeId.LIST) {
         listFields.push(typeInfo);
@@ -664,38 +705,52 @@ export class TypeMeta {
     }
 
     // Sort functions
-    const numericSorter = (a: T, b: T) => {
+    const primitiveComparator = (a: T, b: T) => {
       // Sort by type_id descending, then by name ascending
-
-      const sizea = getPrimitiveTypeSize(a.typeId);
-      const sizeb = getPrimitiveTypeSize(b.typeId);
-      if (sizea !== sizeb) {
-        return sizeb - sizea;
+      const t1Compress = TypeId.isCompressedType(a.typeId);
+      const t2Compress = TypeId.isCompressedType(b.typeId);
+
+      if ((t1Compress && t2Compress) || (!t1Compress && !t2Compress)) {
+        const sizea = getPrimitiveTypeSize(a.typeId);
+        const sizeb = getPrimitiveTypeSize(b.typeId);
+        // return nameSorter(a, b);
+
+        let c = sizeb - sizea;
+        if (c === 0) {
+          c = b.typeId - a.typeId;
+          // noinspection Duplicates
+          if (c == 0) {
+            return nameSorter(a, b);
+          }
+          return c;
+        }
+        return c;
       }
-      if (a.typeId !== b.typeId) {
-        return b.typeId - a.typeId;
+      if (t1Compress) {
+        return 1;
       }
-      return nameSorter(a, b);
+      // t2 compress
+      return -1;
     };
 
     const typeIdThenNameSorter = (a: T, b: T) => {
       if (a.typeId !== b.typeId) {
-        return b.typeId - a.typeId;
+        return a.typeId - b.typeId;
       }
       return nameSorter(a, b);
     };
 
     const nameSorter = (a: T, b: T) => {
-      return 
TypeMeta.toSnakeCase(a.fieldName).localeCompare(TypeMeta.toSnakeCase(b.fieldName));
+      return 
TypeMeta.getFieldSortKey(a).localeCompare(TypeMeta.getFieldSortKey(b));
     };
 
     // Sort each group
-    primitiveFields.sort(numericSorter);
-    nullablePrimitiveFields.sort(numericSorter);
+    primitiveFields.sort(primitiveComparator);
+    nullablePrimitiveFields.sort(primitiveComparator);
     internalTypeFields.sort(typeIdThenNameSorter);
-    listFields.sort(nameSorter);
-    setFields.sort(nameSorter);
-    mapFields.sort(nameSorter);
+    listFields.sort(typeIdThenNameSorter);
+    setFields.sort(typeIdThenNameSorter);
+    mapFields.sort(typeIdThenNameSorter);
     otherFields.sort(typeIdThenNameSorter);
 
     return [
diff --git a/javascript/packages/fory/lib/type.ts 
b/javascript/packages/fory/lib/type.ts
index 10d54d943..549f972e8 100644
--- a/javascript/packages/fory/lib/type.ts
+++ b/javascript/packages/fory/lib/type.ts
@@ -181,6 +181,19 @@ export const TypeId = {
       TypeId.TYPED_UNION,
     ].includes(id as any);
   },
+  isCompressedType(typeId: number) {
+    switch (typeId) {
+      case TypeId.VARINT32:
+      case TypeId.VAR_UINT32:
+      case TypeId.VARINT64:
+      case TypeId.VAR_UINT64:
+      case TypeId.TAGGED_INT64:
+      case TypeId.TAGGED_UINT64:
+        return true;
+      default:
+        return false;
+    }
+  },
 } as const;
 
 export enum ConfigFlags {
diff --git a/javascript/test/crossLanguage.test.ts 
b/javascript/test/crossLanguage.test.ts
index 5c16b7782..3f4f0b2a7 100644
--- a/javascript/test/crossLanguage.test.ts
+++ b/javascript/test/crossLanguage.test.ts
@@ -183,7 +183,7 @@ describe("bool", () => {
     writeToFile(writer.dump() as Buffer);
   });
   test("test_murmurhash3", () => {
-  const { x64hash128 } = require("../packages/fory/lib/murmurHash3");
+    const { x64hash128 } = require("../packages/fory/lib/murmurHash3");
     const reader = new BinaryReader({});
     reader.reset(content);
     let dataview = x64hash128(new Uint8Array([1, 2, 8]), 47);
@@ -898,7 +898,7 @@ describe("bool", () => {
       f1: Type.string()
     })
     class OneStringFieldStruct {
-      @ForyField({nullable: true})
+      @ForyField({ nullable: true })
       f1: string | null = null;
     }
     fory.registerSerializer(OneStringFieldStruct);
@@ -921,7 +921,7 @@ describe("bool", () => {
       f1: Type.string()
     })
     class OneStringFieldStruct {
-      @ForyField({nullable: true})
+      @ForyField({ nullable: true })
       f1: string | null = null;
     }
     fory.registerSerializer(OneStringFieldStruct);
@@ -1115,28 +1115,165 @@ describe("bool", () => {
     writeToFile(serializedData as Buffer);
   });
 
-  test("test_nullable_field_schema_consistent_not_null", () => {
-    if (Boolean("1")) { return; }
-    const fory = new Fory({
-      mode: Mode.Compatible
-    });
+  const buildClass = (id = 402) => {
+    @Type.struct({ typeId: id })
+    class NullableComprehensiveCompatible {
+      // Base non-nullable primitive fields
+      @Type.int8()
+      byteField: number = 0;
+      @Type.int16()
+      shortField: number = 0;
+      @Type.varInt32()
+      intField: number = 0;
+      @Type.varInt64()
+      longField: number = 0;
+      @Type.float32()
+      floatField: number = 0;
+      @Type.float64()
+      doubleField: number = 0;
+      @Type.bool()
+      boolField: boolean = false;
+
+      // Base non-nullable boxed fields (not nullable by default in xlang)
+      @Type.varInt32()
+      boxedInt: number = 0;
+      @Type.varInt64()
+      boxedLong: number = 0;
+      @Type.float32()
+      boxedFloat: number = 0;
+      @Type.float64()
+      boxedDouble: number = 0;
+      @Type.bool()
+      boxedBool = false;
+
+      // Base non-nullable reference fields
+      @Type.string()
+      stringField: string = '';
+      @Type.array(Type.string())
+      listField: string[] = [];
+      @Type.set(Type.string())
+      setField: Set<string> = new Set();
+      @Type.map(Type.string(), Type.string())
+      mapField: Map<string, string> = new Map();
+
+      // Nullable group 1 - boxed types with @ForyField(nullable=true)
+      @ForyField({ nullable: true })
+      @Type.varInt32()
+      nullableInt1: number | null = null;
 
-    @Type.struct(401, {
-      intField: Type.int32(),
-      stringField: Type.string(),
-      nullableInt: Type.int32(),
-      nullableString: Type.string()
-    })
-    class NullableStruct {
+      @ForyField({ nullable: true })
+      @Type.varInt64()
+      nullableLong1: number | null = null;
+
+      @ForyField({ nullable: true })
+      @Type.float32()
+      nullableFloat1: number | null = null;
+
+      @ForyField({ nullable: true })
+      @Type.float64()
+      nullableDouble1: number | null = null;
+
+      @ForyField({ nullable: true })
+      @Type.bool()
+      nullableBool1: boolean | null = null;
+
+      // Nullable group 2 - reference types with @ForyField(nullable=true)
+      @ForyField({ nullable: true })
+      @Type.string()
+      nullableString2: string | null = null;
+
+      @ForyField({ nullable: true })
+      @Type.array(Type.string())
+      nullableList2: string[] | null = null;
+
+      @ForyField({ nullable: true })
+      @Type.set(Type.string())
+      nullableSet2: Set<string> | null = null;
+
+      @ForyField({ nullable: true })
+      @Type.map(Type.string(), Type.string())
+      nullableMap2: Map<string, string> | null = null;
+    }
+    return NullableComprehensiveCompatible;
+  }
+
+  const buildClassConsistent = (id = 401) => {
+    @Type.struct({ typeId: id })
+    class NullableComprehensiveConsistent {
+      // Base non-nullable primitive fields
+      @Type.int8()
+      byteField: number = 0;
+      @Type.int16()
+      shortField: number = 0;
+      @Type.varInt32()
       intField: number = 0;
-      stringField: string = "";
+      @Type.varInt64()
+      longField: number = 0;
+      @Type.float32()
+      floatField: number = 0;
+      @Type.float64()
+      doubleField: number = 0;
+      @Type.bool()
+      boolField: boolean = false;
+
+      // Base non-nullable reference fields
+      @Type.string()
+      stringField: string = '';
+      @Type.array(Type.string())
+      listField: string[] = [];
+      @Type.set(Type.string())
+      setField: Set<string> = new Set();
+      @Type.map(Type.string(), Type.string())
+      mapField: Map<string, string> = new Map();
+
+      // Nullable group 1 - boxed types with @ForyField(nullable=true)
+      @ForyField({ nullable: true })
+      @Type.varInt32()
       nullableInt: number | null = null;
+
+      @ForyField({ nullable: true })
+      @Type.varInt64()
+      nullableLong: number | null = null;
+
+      @ForyField({ nullable: true })
+      @Type.float32()
+      nullableFloat: number | null = null;
+
+      @ForyField({ nullable: true })
+      @Type.float64()
+      nullableDouble: number | null = null;
+
+      @ForyField({ nullable: true })
+      @Type.bool()
+      nullableBool: boolean | null = null;
+
+      // Nullable group 2 - reference types with @ForyField(nullable=true)
+      @ForyField({ nullable: true })
+      @Type.string()
       nullableString: string | null = null;
+
+      @ForyField({ nullable: true })
+      @Type.array(Type.string())
+      nullableList: string[] | null = null;
+
+      @ForyField({ nullable: true })
+      @Type.set(Type.string())
+      nullableSet: Set<string> | null = null;
+
+      @ForyField({ nullable: true })
+      @Type.map(Type.string(), Type.string())
+      nullableMap: Map<string, string> | null = null;
     }
-    fory.registerSerializer(NullableStruct);
+    return NullableComprehensiveConsistent
 
-    const reader = new BinaryReader({});
-    reader.reset(content);
+  }
+
+  test("test_nullable_field_schema_consistent_not_null", () => {
+    const fory = new Fory({
+      mode: Mode.SchemaConsistent
+    });
+
+    fory.registerSerializer(buildClassConsistent(401));
 
     // Deserialize struct from Java
     let cursor = 0;
@@ -1148,25 +1285,12 @@ describe("bool", () => {
     writeToFile(serializedData as Buffer);
   });
 
+
   test("test_nullable_field_schema_consistent_null", () => {
-    if (Boolean("1")) { return; }
     const fory = new Fory({
-      mode: Mode.Compatible
+      mode: Mode.SchemaConsistent
     });
-
-    @Type.struct(401, {
-      intField: Type.int32(),
-      stringField: Type.string(),
-      nullableInt: Type.int32(),
-      nullableString: Type.string()
-    })
-    class NullableStruct {
-      intField: number = 0;
-      stringField: string = "";
-      nullableInt: number | null = null;
-      nullableString: string | null = null;
-    }
-    fory.registerSerializer(NullableStruct);
+    fory.registerSerializer(buildClassConsistent());
 
     const reader = new BinaryReader({});
     reader.reset(content);
@@ -1181,28 +1305,13 @@ describe("bool", () => {
     writeToFile(serializedData as Buffer);
   });
 
+
   test("test_nullable_field_compatible_not_null", () => {
-    if (Boolean("1")) { return; }
     const fory = new Fory({
       mode: Mode.Compatible
     });
 
-    @Type.struct(402, {
-      intField: Type.int32(),
-      stringField: Type.string(),
-      nullableInt: Type.int32(),
-      nullableString: Type.string()
-    })
-    class NullableStruct {
-      intField: number = 0;
-      stringField: string = "";
-      nullableInt: number | null = null;
-      nullableString: string | null = null;
-    }
-    fory.registerSerializer(NullableStruct);
-
-    const reader = new BinaryReader({});
-    reader.reset(content);
+    fory.registerSerializer(buildClass());
 
     // Deserialize struct from Java
     let cursor = 0;
@@ -1215,33 +1324,23 @@ describe("bool", () => {
   });
 
   test("test_nullable_field_compatible_null", () => {
-    if (Boolean("1")) { return; }
     const fory = new Fory({
       mode: Mode.Compatible
     });
 
-    @Type.struct(402, {
-      intField: Type.int32(),
-      stringField: Type.string(),
-      nullableInt: Type.int32(),
-      nullableString: Type.string()
-    })
-    class NullableStruct {
-      intField: number = 0;
-      stringField: string = "";
-      nullableInt: number | null = null;
-      nullableString: string | null = null;
-    }
-    fory.registerSerializer(NullableStruct);
+    fory.registerSerializer(buildClass());
 
     const reader = new BinaryReader({});
     reader.reset(content);
 
     // Deserialize struct from Java
     let cursor = 0;
-    const deserializedStruct = fory.deserialize(content.subarray(cursor));
+    const deserializedStruct: InstanceType<ReturnType<typeof buildClass>> | 
null = fory.deserialize(content.subarray(cursor));
     cursor += fory.binaryReader.getCursor();
 
+    if (deserializedStruct === null) {
+      throw new Error("deserializedStruct is null");
+    }
     // Serialize the deserialized struct back
     const serializedData = fory.serialize(deserializedStruct);
     writeToFile(serializedData as Buffer);
@@ -1475,6 +1574,6 @@ describe("bool", () => {
 
     // Serialize the deserialized struct back
     const serializedData = fory.serialize(deserializedStruct);
-   // writeToFile(serializedData as Buffer);
+    // writeToFile(serializedData as Buffer);
   });
 });


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

Reply via email to