This is an automated email from the ASF dual-hosted git repository. chaokunyang pushed a commit to branch releases-0.11.0 in repository https://gitbox.apache.org/repos/asf/fory.git
commit 0fcf012060acc405f65127df9749bdc31bbb9aba Author: Steven Schlansker <[email protected]> AuthorDate: Fri Jun 6 18:04:37 2025 -0700 fix(java): row encoder interface synthesis handle `(Optional) null` correctly (#2306) ## What does this PR do? fix incorrect handling of lazy-read `(Optional) null` values --- .../fory/format/encoder/RowEncoderBuilder.java | 6 ++++-- .../format/encoder/ImplementInterfaceTest.java | 23 ++++++++++++++++++++++ .../apache/fory/format/encoder/OptionalTest.java | 12 +++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/java/fory-format/src/main/java/org/apache/fory/format/encoder/RowEncoderBuilder.java b/java/fory-format/src/main/java/org/apache/fory/format/encoder/RowEncoderBuilder.java index cf351365..a82f368c 100644 --- a/java/fory-format/src/main/java/org/apache/fory/format/encoder/RowEncoderBuilder.java +++ b/java/fory-format/src/main/java/org/apache/fory/format/encoder/RowEncoderBuilder.java @@ -315,7 +315,6 @@ public class RowEncoderBuilder extends BaseBinaryEncoderBuilder { implClass.setClassName(generatedBeanImplName); implClass.implementsInterfaces(implClass.type(beanClass)); implClass.addField(true, implClass.type(BinaryRow.class), "row", null); - implClass.addConstructor("this.row = row;", BinaryRow.class, "row"); int numFields = schema.getFields().size(); for (int i = 0; i < numFields; i++) { @@ -330,7 +329,8 @@ public class RowEncoderBuilder extends BaseBinaryEncoderBuilder { getterImpl = new Expression.Return(decodeValue); } else { String fieldName = "f" + i + "_" + d.getName(); - implClass.addField(fieldType.getRawType(), fieldName); + implClass.addField( + false, ctx.type(fieldType.getRawType()), fieldName, nullValue(fieldType)); Expression fieldRef = new Expression.Reference(fieldName, fieldType, true); Expression storeValue = @@ -353,6 +353,8 @@ public class RowEncoderBuilder extends BaseBinaryEncoderBuilder { implClass.addMethod( d.getName(), getterImpl.genCode(implClass).code(), fieldType.getRawType()); } + // Note: adding constructor captures init code, so must happen after all fields are collected + implClass.addConstructor("this.row = row;", BinaryRow.class, "row"); return implClass; } diff --git a/java/fory-format/src/test/java/org/apache/fory/format/encoder/ImplementInterfaceTest.java b/java/fory-format/src/test/java/org/apache/fory/format/encoder/ImplementInterfaceTest.java index df18bede..70af5f1b 100644 --- a/java/fory-format/src/test/java/org/apache/fory/format/encoder/ImplementInterfaceTest.java +++ b/java/fory-format/src/test/java/org/apache/fory/format/encoder/ImplementInterfaceTest.java @@ -19,6 +19,7 @@ package org.apache.fory.format.encoder; +import java.util.Optional; import lombok.Data; import org.apache.fory.annotation.ForyField; import org.apache.fory.format.row.binary.BinaryRow; @@ -128,4 +129,26 @@ public class ImplementInterfaceTest { throw new AssertionError(); } } + + public interface OptionalType { + Optional<String> f1(); + } + + static class OptionalTypeImpl implements OptionalType { + @Override + public Optional<String> f1() { + return null; + } + } + + @Test + public void testNullOptional() { + final OptionalType bean1 = new OptionalTypeImpl(); + final RowEncoder<OptionalType> encoder = Encoders.bean(OptionalType.class); + final BinaryRow row = encoder.toRow(bean1); + final MemoryBuffer buffer = MemoryUtils.wrap(row.toBytes()); + row.pointTo(buffer, 0, buffer.size()); + final OptionalType deserializedBean = encoder.fromRow(row); + Assert.assertEquals(deserializedBean.f1(), Optional.empty()); + } } diff --git a/java/fory-format/src/test/java/org/apache/fory/format/encoder/OptionalTest.java b/java/fory-format/src/test/java/org/apache/fory/format/encoder/OptionalTest.java index 3093cd3d..99e1f263 100644 --- a/java/fory-format/src/test/java/org/apache/fory/format/encoder/OptionalTest.java +++ b/java/fory-format/src/test/java/org/apache/fory/format/encoder/OptionalTest.java @@ -53,6 +53,18 @@ public class OptionalTest { Assert.assertEquals(deserializedBean, bean); } + @Test + public void testOptionalNull() { + final OptionalType bean = new OptionalType(); + final RowEncoder<OptionalType> encoder = Encoders.bean(OptionalType.class); + final BinaryRow row = encoder.toRow(bean); + final MemoryBuffer buffer = MemoryUtils.wrap(row.toBytes()); + row.pointTo(buffer, 0, buffer.size()); + final OptionalType deserializedBean = encoder.fromRow(row); + Assert.assertEquals(deserializedBean.f1, Optional.empty()); + Assert.assertEquals(deserializedBean.f2, Optional.empty()); + } + @Test public void testOptionalPresent() { final OptionalType bean = new OptionalType(); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
