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 390b623b9 feat(java): support deserialize non exist enum variant to
default (#2787)
390b623b9 is described below
commit 390b623b95d025ecc5e32366c0361ed6e66298fd
Author: Shawn Yang <[email protected]>
AuthorDate: Mon Oct 20 21:08:13 2025 +0800
feat(java): support deserialize non exist enum variant to default (#2787)
## Why?
<!-- Describe the purpose of this PR. -->
## What does this PR do?
<!-- Describe the details of this PR. -->
## Related issues
Closes #2720
https://github.com/apache/fory/pull/2719
## 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.
-->
- [ ] Does this PR introduce any public API change?
- [ ] 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.
-->
---
.../main/java/org/apache/fory/config/Config.java | 10 ++-
.../java/org/apache/fory/config/ForyBuilder.java | 16 +++-
.../fory/config/UnknownEnumValueStrategy.java | 35 +++++++++
.../main/java/org/apache/fory/meta/ClassDef.java | 5 +-
.../org/apache/fory/serializer/EnumSerializer.java | 30 ++++---
.../fory-core/native-image.properties | 1 +
.../test/java/org/apache/fory/xlang/EnumTest.java | 91 ++++++++++++++++++++++
7 files changed, 170 insertions(+), 18 deletions(-)
diff --git a/java/fory-core/src/main/java/org/apache/fory/config/Config.java
b/java/fory-core/src/main/java/org/apache/fory/config/Config.java
index 4575f3dfc..3f7c8f8bd 100644
--- a/java/fory-core/src/main/java/org/apache/fory/config/Config.java
+++ b/java/fory-core/src/main/java/org/apache/fory/config/Config.java
@@ -62,7 +62,7 @@ public class Config implements Serializable {
private final boolean deserializeNonexistentClass;
private final boolean scalaOptimizationEnabled;
private transient int configHash;
- private final boolean deserializeNonexistentEnumValueAsNull;
+ private final UnknownEnumValueStrategy unknownEnumValueStrategy;
private final boolean serializeEnumByName;
private final int bufferSizeLimitBytes;
private final int maxDepth;
@@ -103,7 +103,7 @@ public class Config implements Serializable {
}
asyncCompilationEnabled = builder.asyncCompilationEnabled;
scalaOptimizationEnabled = builder.scalaOptimizationEnabled;
- deserializeNonexistentEnumValueAsNull =
builder.deserializeNonexistentEnumValueAsNull;
+ unknownEnumValueStrategy = builder.unknownEnumValueStrategy;
serializeEnumByName = builder.serializeEnumByName;
bufferSizeLimitBytes = builder.bufferSizeLimitBytes;
maxDepth = builder.maxDepth;
@@ -144,7 +144,11 @@ public class Config implements Serializable {
/** ignore Enum Deserialize array out of bounds return null. */
public boolean deserializeNonexistentEnumValueAsNull() {
- return deserializeNonexistentEnumValueAsNull;
+ return unknownEnumValueStrategy == UnknownEnumValueStrategy.RETURN_NULL;
+ }
+
+ public UnknownEnumValueStrategy getUnknownEnumValueStrategy() {
+ return unknownEnumValueStrategy;
}
/** deserialize and serialize enum by name. */
diff --git
a/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java
b/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java
index 7184b00ea..38de05e15 100644
--- a/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java
+++ b/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java
@@ -84,7 +84,7 @@ public final class ForyBuilder {
boolean registerGuavaTypes = true;
boolean scalaOptimizationEnabled = false;
boolean suppressClassRegistrationWarnings = true;
- boolean deserializeNonexistentEnumValueAsNull = false;
+ UnknownEnumValueStrategy unknownEnumValueStrategy =
UnknownEnumValueStrategy.NOT_ALLOWED;
boolean serializeEnumByName = false;
int bufferSizeLimitBytes = 128 * 1024;
MetaCompressor metaCompressor = new DeflaterMetaCompressor();
@@ -135,7 +135,19 @@ public final class ForyBuilder {
/** ignore Enum Deserialize array out of bounds. */
public ForyBuilder deserializeNonexistentEnumValueAsNull(
boolean deserializeNonexistentEnumValueAsNull) {
- this.deserializeNonexistentEnumValueAsNull =
deserializeNonexistentEnumValueAsNull;
+ this.unknownEnumValueStrategy = UnknownEnumValueStrategy.RETURN_NULL;
+ return this;
+ }
+
+ /**
+ * Sets the strategy applied when deserialization encounters an enum value
that cannot be
+ * resolved.
+ *
+ * @param action policy to apply for unknown enum values
+ * @return this builder instance for chaining
+ */
+ public ForyBuilder withUnknownEnumValueStrategy(UnknownEnumValueStrategy
action) {
+ this.unknownEnumValueStrategy = action;
return this;
}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/config/UnknownEnumValueStrategy.java
b/java/fory-core/src/main/java/org/apache/fory/config/UnknownEnumValueStrategy.java
new file mode 100644
index 000000000..19ffd86d5
--- /dev/null
+++
b/java/fory-core/src/main/java/org/apache/fory/config/UnknownEnumValueStrategy.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fory.config;
+
+/**
+ * Strategy describing how deserialization handles enum values that cannot be
resolved from the
+ * serialized payload.
+ */
+public enum UnknownEnumValueStrategy {
+ /** Throw an exception and stop deserialization. */
+ NOT_ALLOWED,
+ /** Return {@code null} instead of the unknown enum constant. */
+ RETURN_NULL,
+ /** Fallback to the first declared enum constant. */
+ RETURN_FIRST_VARIANT,
+ /** Fallback to the last declared enum constant. */
+ RETURN_LAST_VARIANT
+}
diff --git a/java/fory-core/src/main/java/org/apache/fory/meta/ClassDef.java
b/java/fory-core/src/main/java/org/apache/fory/meta/ClassDef.java
index 2baf75f3b..9631108d4 100644
--- a/java/fory-core/src/main/java/org/apache/fory/meta/ClassDef.java
+++ b/java/fory-core/src/main/java/org/apache/fory/meta/ClassDef.java
@@ -414,7 +414,7 @@ public class ClassDef implements Serializable {
this.isMonomorphic = isMonomorphic;
this.trackingRef = trackingRef;
this.nullable = nullable;
- this.xtypeId = xtypeId & 0xff;
+ this.xtypeId = xtypeId;
}
public boolean isMonomorphic() {
@@ -560,8 +560,7 @@ public class ClassDef implements Serializable {
int xtypeId,
boolean nullable,
boolean trackingRef) {
- assert xtypeId <= 0xff;
- switch (xtypeId) {
+ switch (xtypeId & 0xff) {
case Types.LIST:
case Types.SET:
return new CollectionFieldType(
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/EnumSerializer.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/EnumSerializer.java
index f314b91cd..cc0a69136 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/EnumSerializer.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/EnumSerializer.java
@@ -115,20 +115,30 @@ public class EnumSerializer extends
ImmutableSerializer<Enum> {
}
private Enum handleNonexistentEnumValue(int value) {
- if (fory.getConfig().deserializeNonexistentEnumValueAsNull()) {
- return null;
- } else {
- throw new IllegalArgumentException(
- String.format("Enum ordinal %s not in %s", value,
Arrays.toString(enumConstants)));
+ switch (fory.getConfig().getUnknownEnumValueStrategy()) {
+ case RETURN_NULL:
+ return null;
+ case RETURN_FIRST_VARIANT:
+ return enumConstants[0];
+ case RETURN_LAST_VARIANT:
+ return enumConstants[enumConstants.length - 1];
+ default:
+ throw new IllegalArgumentException(
+ String.format("Enum ordinal %s not in %s", value,
Arrays.toString(enumConstants)));
}
}
private Enum handleNonexistentEnumValue(String value) {
- if (fory.getConfig().deserializeNonexistentEnumValueAsNull()) {
- return null;
- } else {
- throw new IllegalArgumentException(
- String.format("Enum string %s not in %s", value,
Arrays.toString(enumConstants)));
+ switch (fory.getConfig().getUnknownEnumValueStrategy()) {
+ case RETURN_NULL:
+ return null;
+ case RETURN_FIRST_VARIANT:
+ return enumConstants[0];
+ case RETURN_LAST_VARIANT:
+ return enumConstants[enumConstants.length - 1];
+ default:
+ throw new IllegalArgumentException(
+ String.format("Enum string %s not in %s", value,
Arrays.toString(enumConstants)));
}
}
}
diff --git
a/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/native-image.properties
b/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/native-image.properties
index 3f4efe975..e65de39ac 100644
---
a/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/native-image.properties
+++
b/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/native-image.properties
@@ -223,6 +223,7 @@
Args=--initialize-at-build-time=org.apache.fory.memory.MemoryBuffer,\
org.apache.fory.config.ForyBuilder,\
org.apache.fory.config.Language,\
org.apache.fory.config.LongEncoding,\
+ org.apache.fory.config.UnknownEnumValueStrategy,\
org.apache.fory.logging.ForyLogger,\
org.apache.fory.logging.LoggerFactory,\
org.apache.fory.memory.BoundsChecking,\
diff --git a/java/fory-core/src/test/java/org/apache/fory/xlang/EnumTest.java
b/java/fory-core/src/test/java/org/apache/fory/xlang/EnumTest.java
new file mode 100644
index 000000000..551d91858
--- /dev/null
+++ b/java/fory-core/src/test/java/org/apache/fory/xlang/EnumTest.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fory.xlang;
+
+import org.apache.fory.Fory;
+import org.apache.fory.config.CompatibleMode;
+import org.apache.fory.config.Language;
+import org.apache.fory.config.UnknownEnumValueStrategy;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class EnumTest {
+ enum Color {
+ Green,
+ Red,
+ Blue,
+ White,
+ }
+
+ enum Color2 {
+ Green,
+ Red,
+ }
+
+ static class EnumWrapper {
+ Color color;
+ }
+
+ static class EnumWrapper2 {
+ Color2 color;
+ }
+
+ @Test
+ public void testEnumEnum() {
+ Fory fory1 =
+ Fory.builder()
+ .withLanguage(Language.XLANG)
+ .withCompatibleMode(CompatibleMode.COMPATIBLE)
+ .withCodegen(false)
+ .build();
+ fory1.register(Color.class, 101);
+ fory1.register(Color2.class, 102);
+ fory1.register(EnumWrapper.class, 103);
+ Fory fory2 =
+ Fory.builder()
+ .withLanguage(Language.XLANG)
+ .withCompatibleMode(CompatibleMode.COMPATIBLE)
+
.withUnknownEnumValueStrategy(UnknownEnumValueStrategy.RETURN_FIRST_VARIANT)
+ .withCodegen(false)
+ .build();
+ fory2.register(Color.class, 101);
+ fory2.register(Color2.class, 102);
+ fory2.register(EnumWrapper2.class, 103);
+
+ EnumWrapper enumWrapper = new EnumWrapper();
+ enumWrapper.color = Color.White;
+ byte[] serialize = fory1.serialize(enumWrapper);
+ EnumWrapper2 wrapper2 = (EnumWrapper2) fory2.deserialize(serialize);
+ Assert.assertEquals(wrapper2.color, Color2.Green);
+
+ Fory fory3 =
+ Fory.builder()
+ .withLanguage(Language.XLANG)
+ .withCompatibleMode(CompatibleMode.COMPATIBLE)
+
.withUnknownEnumValueStrategy(UnknownEnumValueStrategy.RETURN_LAST_VARIANT)
+ .withCodegen(false)
+ .build();
+ fory3.register(Color.class, 101);
+ fory3.register(Color2.class, 102);
+ fory3.register(EnumWrapper2.class, 103);
+ EnumWrapper2 wrapper3 = (EnumWrapper2) fory3.deserialize(serialize);
+ Assert.assertEquals(wrapper3.color, Color2.Red);
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]