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]