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 ed60ff44b fix(java): fix deserializing EXT type bug (#2700)
ed60ff44b is described below

commit ed60ff44b93700a790613d77a171f10835305b69
Author: zhou yong kang <[email protected]>
AuthorDate: Sat Oct 4 21:26:25 2025 +0800

    fix(java): fix deserializing EXT type bug (#2700)
    
    <!--
    **Thanks for contributing to Apache Fory™.**
    
    **If this is your first time opening a PR on fory, you can refer to
    
[CONTRIBUTING.md](https://github.com/apache/fory/blob/main/CONTRIBUTING.md).**
    
    Contribution Checklist
    
    - The **Apache Fory™** community has requirements on the naming of pr
    titles. You can also find instructions in
    [CONTRIBUTING.md](https://github.com/apache/fory/blob/main/CONTRIBUTING.md).
    
    - Apache Fory™ has a strong focus on performance. If the PR you submit
    will have an impact on performance, please benchmark it first and
    provide the benchmark result here.
    -->
    
    ## Why?
    
    <!-- Describe the purpose of this PR. -->
    When the call order is register(Class, int)→ registerSerializer(Class,
    Serializer), the internal state of the framework is inconsistent:
    register() creates the initial TypeInfo and assigns xtypeId
    registerSerializer() to update classInfo.xtypeId and serializer
    But the old map in xtypeIdToClassMap is not cleaned
    Causes a Type id 26387 not registered exception to be thrown during
    deserialization.
    
    ## What does this PR do?
    
    <!-- Describe the details of this PR. -->
    Before update: Remove the old xtypeId map
    After update: Adding a new xtypeId map ensures idempotence registration
    ## Related issues
    Close #2698
    <!--
    Is there any related issue? If this PR closes them you say say
    fix/closes:
    
    - #xxxx0
    - #xxxx1
    - Fixes #xxxx2
    -->
    
    ## Does this PR introduce any user-facing change?
    
    <!--
    If any user-facing interface changes, please [open an
    issue](https://github.com/apache/fory/issues/new/choose) describing the
    need to do so and update the document if necessary.
    
    Delete section if not applicable.
    -->
    
    - [x] Does this PR introduce any public API change?
    - [x] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
    
    <!--
    When the PR has an impact on performance (if you don't know whether the
    PR will have an impact on performance, you can submit the PR first, and
    if it will have impact on performance, the code reviewer will explain
    it), be sure to attach a benchmark data here.
    
    Delete section if not applicable.
    -->
---
 .../org/apache/fory/resolver/XtypeResolver.java    |  22 ++++-
 .../org/apache/fory/serializer/RegisterTest.java   | 103 +++++++++++++++++++++
 2 files changed, 120 insertions(+), 5 deletions(-)

diff --git 
a/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java 
b/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java
index 7b5da3a41..258c0df34 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java
@@ -346,19 +346,31 @@ public class XtypeResolver extends TypeResolver {
     if 
(!serializer.getClass().getPackage().getName().startsWith("org.apache.fory")) {
       SerializationUtils.validate(type, serializer.getClass());
     }
-    int xtypeId = classInfo.xtypeId;
-    int foryId = xtypeId & 0xff;
+    int oldXtypeId = classInfo.xtypeId;
+    int foryId = oldXtypeId & 0xff;
+
+    if (oldXtypeId != 0 && xtypeIdToClassMap.get(oldXtypeId) == classInfo) {
+      xtypeIdToClassMap.remove(oldXtypeId);
+      registeredTypeIds.remove(oldXtypeId);
+    }
+
     if (foryId != Types.EXT && foryId != Types.NAMED_EXT) {
       if (foryId == Types.STRUCT || foryId == Types.COMPATIBLE_STRUCT) {
-        classInfo.xtypeId = (xtypeId & 0xffffff00) | Types.EXT;
+        classInfo.xtypeId = (oldXtypeId & 0xffffff00) | Types.EXT;
       } else if (foryId == Types.NAMED_STRUCT || foryId == 
Types.NAMED_COMPATIBLE_STRUCT) {
-        classInfo.xtypeId = (xtypeId & 0xffffff00) | Types.NAMED_EXT;
+        classInfo.xtypeId = (oldXtypeId & 0xffffff00) | Types.NAMED_EXT;
       } else {
         throw new IllegalArgumentException(
-            String.format("Can't register serializer for type %s with id %s", 
type, xtypeId));
+            String.format("Can't register serializer for type %s with id %s", 
type, oldXtypeId));
       }
     }
     classInfo.serializer = serializer;
+
+    int newXtypeId = classInfo.xtypeId;
+    if (newXtypeId != 0) {
+      xtypeIdToClassMap.put(newXtypeId, classInfo);
+      registeredTypeIds.add(newXtypeId);
+    }
   }
 
   private ClassInfo checkClassRegistration(Class<?> type) {
diff --git 
a/java/fory-core/src/test/java/org/apache/fory/serializer/RegisterTest.java 
b/java/fory-core/src/test/java/org/apache/fory/serializer/RegisterTest.java
index 8d4a560d7..941092dbc 100644
--- a/java/fory-core/src/test/java/org/apache/fory/serializer/RegisterTest.java
+++ b/java/fory-core/src/test/java/org/apache/fory/serializer/RegisterTest.java
@@ -24,6 +24,7 @@ import org.apache.fory.ForyTestBase;
 import org.apache.fory.config.CompatibleMode;
 import org.apache.fory.config.ForyBuilder;
 import org.apache.fory.config.Language;
+import org.apache.fory.memory.MemoryBuffer;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -67,4 +68,106 @@ public class RegisterTest extends ForyTestBase {
   }
 
   public static class B {}
+
+  @Test
+  public void testRegisterThenRegisterSerializer() {
+    Fory fory =
+        Fory.builder()
+            .withLanguage(Language.XLANG)
+            .withCompatibleMode(CompatibleMode.COMPATIBLE)
+            .withCodegen(false)
+            .build();
+
+    fory.register(MyExt.class, 103);
+
+    fory.registerSerializer(MyExt.class, MyExtSerializer.class);
+
+    MyExt original = new MyExt();
+    original.id = "test-123";
+
+    byte[] bytes = fory.serialize(original);
+    MyExt deserialized = (MyExt) fory.deserialize(bytes);
+
+    Assert.assertNotNull(deserialized);
+    Assert.assertEquals(deserialized.id, "test-123");
+  }
+
+  @Test
+  public void testRegisterSerializerThenRegister() {
+    Fory fory =
+        Fory.builder()
+            .withLanguage(Language.XLANG)
+            .withCompatibleMode(CompatibleMode.COMPATIBLE)
+            .withCodegen(false)
+            .build();
+    fory.register(MyExt.class, "test.pkg", "MyExt");
+    fory.registerSerializer(MyExt.class, MyExtSerializer.class);
+
+    MyExt original = new MyExt();
+    original.id = "reverse-order-test";
+
+    byte[] bytes = fory.serialize(original);
+    MyExt deserialized = (MyExt) fory.deserialize(bytes);
+
+    Assert.assertNotNull(deserialized);
+    Assert.assertEquals(deserialized.id, "reverse-order-test");
+  }
+
+  @Test
+  public void testMultipleRegisterSerializer() {
+    Fory fory =
+        Fory.builder()
+            .withLanguage(Language.XLANG)
+            .withCompatibleMode(CompatibleMode.COMPATIBLE)
+            .withCodegen(false)
+            .build();
+
+    fory.register(MyExt.class, 104);
+
+    fory.registerSerializer(MyExt.class, MyExtSerializer.class);
+    fory.registerSerializer(MyExt.class, MyExtSerializer.class);
+
+    MyExt original = new MyExt();
+    original.id = "idempotent-test";
+
+    byte[] bytes = fory.serialize(original);
+    MyExt deserialized = (MyExt) fory.deserialize(bytes);
+
+    Assert.assertNotNull(deserialized);
+    Assert.assertEquals(deserialized.id, "idempotent-test");
+  }
+
+  public static class MyExt {
+    public String id;
+  }
+
+  public static class MyExtSerializer extends Serializer<MyExt> {
+    public MyExtSerializer(Fory fory) {
+      super(fory, MyExt.class);
+    }
+
+    @Override
+    public void write(MemoryBuffer buffer, MyExt value) {
+      fory.writeJavaString(buffer, value.id);
+    }
+
+    @Override
+    public void xwrite(MemoryBuffer buffer, MyExt value) {
+      fory.writeString(buffer, value.id);
+    }
+
+    @Override
+    public MyExt read(MemoryBuffer buffer) {
+      MyExt result = new MyExt();
+      result.id = fory.readJavaString(buffer);
+      return result;
+    }
+
+    @Override
+    public MyExt xread(MemoryBuffer buffer) {
+      MyExt result = new MyExt();
+      result.id = fory.readString(buffer);
+      return result;
+    }
+  }
 }


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

Reply via email to