diff --git a/java/fory-core/src/main/java/org/apache/fory/builder/BaseObjectCodecBuilder.java b/java/fory-core/src/main/java/org/apache/fory/builder/BaseObjectCodecBuilder.java index e7aee5ee69..a05527725b 100644 --- a/java/fory-core/src/main/java/org/apache/fory/builder/BaseObjectCodecBuilder.java +++ b/java/fory-core/src/main/java/org/apache/fory/builder/BaseObjectCodecBuilder.java @@ -1856,17 +1856,15 @@ protected Expression deserializeForNullableField( } else { if (typeRef.isPrimitive() && !nullable) { // Only skip null check if BOTH: local type is primitive AND sender didn't write null flag - Expression value = deserializeForNotNull(buffer, typeRef, null); + Expression value = deserializeForNotNullForField(buffer, descriptor, null); // Should put value expr ahead to avoid generated code in wrong scope. return new ListExpression(value, callback.apply(value)); } - // Pass local field type so readNullable can use default value for primitives when null - Class localFieldType = typeRef.isPrimitive() ? typeRef.getRawType() : null; return readNullableField( buffer, descriptor, callback, - () -> deserializeForNotNull(buffer, typeRef, null), + () -> deserializeForNotNullForField(buffer, descriptor, null), nullable); } } diff --git a/java/fory-core/src/main/java/org/apache/fory/codegen/Expression.java b/java/fory-core/src/main/java/org/apache/fory/codegen/Expression.java index 25ce0b294f..e1311a4c83 100644 --- a/java/fory-core/src/main/java/org/apache/fory/codegen/Expression.java +++ b/java/fory-core/src/main/java/org/apache/fory/codegen/Expression.java @@ -56,6 +56,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.fory.memory.Platform; @@ -430,7 +431,8 @@ public ExprCode doGenCode(CodegenContext ctx) { return new ExprCode( FalseLiteral, new LiteralValue(javaType, "Float.NEGATIVE_INFINITY")); } else { - return new ExprCode(FalseLiteral, new LiteralValue(javaType, String.format("%fF", f))); + return new ExprCode( + FalseLiteral, new LiteralValue(javaType, String.format(Locale.ROOT, "%fF", f))); } } else if (javaType == Double.class) { Double d = (Double) value; @@ -443,7 +445,8 @@ public ExprCode doGenCode(CodegenContext ctx) { return new ExprCode( FalseLiteral, new LiteralValue(javaType, "Double.NEGATIVE_INFINITY")); } else { - return new ExprCode(FalseLiteral, new LiteralValue(javaType, String.format("%fD", d))); + return new ExprCode( + FalseLiteral, new LiteralValue(javaType, String.format(Locale.ROOT, "%fD", d))); } } else if (javaType == Byte.class) { return new ExprCode( diff --git a/java/fory-latest-jdk-tests/src/test/java/org/apache/fory/integration_tests/RecordSerializersTest.java b/java/fory-latest-jdk-tests/src/test/java/org/apache/fory/integration_tests/RecordSerializersTest.java index ff54a6ad36..71a4f515e6 100644 --- a/java/fory-latest-jdk-tests/src/test/java/org/apache/fory/integration_tests/RecordSerializersTest.java +++ b/java/fory-latest-jdk-tests/src/test/java/org/apache/fory/integration_tests/RecordSerializersTest.java @@ -22,13 +22,17 @@ import static org.apache.fory.collection.Collections.ofArrayList; import static org.apache.fory.collection.Collections.ofHashMap; +import java.io.Serializable; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.fory.Fory; +import org.apache.fory.ThreadSafeFory; import org.apache.fory.config.CompatibleMode; +import org.apache.fory.config.ForyBuilder; +import org.apache.fory.config.Language; import org.apache.fory.context.MetaReadContext; import org.apache.fory.context.MetaWriteContext; import org.apache.fory.test.bean.Struct; @@ -42,6 +46,11 @@ public class RecordSerializersTest { public record Foo(int f1, String f2, List f3, char f4) {} + public record NumberCompressedPayload(Long longValue, String stringValue) {} + + public record BoxedPrimitiveRecord(String lensId, Long from, Long to, Integer type) + implements Serializable {} + @Test public void testIsRecord() { Assert.assertTrue(RecordUtils.isRecord(Foo.class)); @@ -80,6 +89,56 @@ public void testSimpleRecord(boolean codegen) { Assert.assertEquals(fory.deserialize(fory.serialize(foo)), foo); } + @Test + public void testNumberCompressedBoxedLongRecordRoundTripAcrossPools() { + ThreadSafeFory writer = newNumberCompressedPool(); + ThreadSafeFory reader = newNumberCompressedPool(); + + NumberCompressedPayload payload = + new NumberCompressedPayload(123_456_789L, "longer string with multibyte: \u00ff\u00fe"); + + byte[] bytes = writer.serialize(payload); + Assert.assertEquals(reader.deserialize(bytes), payload); + } + + @Test + public void testCompatibleCodegenBoxedPrimitiveRecordRoundTrip() { + Fory fory = + Fory.builder() + .withLanguage(Language.JAVA) + .requireClassRegistration(false) + .withCompatibleMode(CompatibleMode.COMPATIBLE) + .withClassVersionCheck(true) + .withCodegen(true) + .build(); + BoxedPrimitiveRecord record = + new BoxedPrimitiveRecord( + "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:11111111-2222-3333-4444-555555555555", + 123456789012345L, + 98765432109876L, + 146); + Assert.assertEquals(fory.deserialize(fory.serialize(record)), record); + } + + private static ThreadSafeFory newNumberCompressedPool() { + return newNumberCompressedBuilder().buildThreadSafeForyPool(4); + } + + private static ForyBuilder newNumberCompressedBuilder() { + return Fory.builder() + .withLanguage(Language.JAVA) + .withCodegen(true) + .withAsyncCompilation(false) + .requireClassRegistration(false) + .suppressClassRegistrationWarnings(true) + .withDeserializeUnknownClass(true) + .withRefTracking(true) + .withCompatibleMode(CompatibleMode.COMPATIBLE) + .withStringCompressed(true) + .withNumberCompressed(true) + .withRefCopy(true); + } + @Test(dataProvider = "codegen") public void testSimpleRecordMetaShared(boolean codegen) { Fory fory =