diff --git a/pbj-core/pbj-compiler/src/main/java/com/hedera/pbj/compiler/impl/SingleField.java b/pbj-core/pbj-compiler/src/main/java/com/hedera/pbj/compiler/impl/SingleField.java index 8fc30966..eaf81f69 100644 --- a/pbj-core/pbj-compiler/src/main/java/com/hedera/pbj/compiler/impl/SingleField.java +++ b/pbj-core/pbj-compiler/src/main/java/com/hedera/pbj/compiler/impl/SingleField.java @@ -216,7 +216,7 @@ public void addAllNeededImports( @Override public String parseCode() { if (type == FieldType.MESSAGE) { - return "%s.PROTOBUF.parse(input, strictMode, maxDepth - 1)".formatted(messageType); + return "%s.PROTOBUF.parse(input, strictMode, parseUnknownFields, maxDepth - 1)".formatted(messageType); } else { return "input"; } diff --git a/pbj-integration-tests/src/main/proto/bytes.proto b/pbj-integration-tests/src/main/proto/bytes.proto index be48d255..da1ee265 100644 --- a/pbj-integration-tests/src/main/proto/bytes.proto +++ b/pbj-integration-tests/src/main/proto/bytes.proto @@ -13,3 +13,13 @@ option java_multiple_files = true; message MessageWithBytes { bytes bytesField = 1; } + +/** + * Sample protobuf with MessageWithBytes as a oneof child message field. + */ +message MessageWithBytesWrapper { + oneof message_valid { + MessageWithBytes messageWithBytes = 1; + bool valid = 2; + } +} diff --git a/pbj-integration-tests/src/test/java/com/hedera/pbj/integration/test/UnknownFieldsTest.java b/pbj-integration-tests/src/test/java/com/hedera/pbj/integration/test/UnknownFieldsTest.java index 357e0db3..c631cabc 100644 --- a/pbj-integration-tests/src/test/java/com/hedera/pbj/integration/test/UnknownFieldsTest.java +++ b/pbj-integration-tests/src/test/java/com/hedera/pbj/integration/test/UnknownFieldsTest.java @@ -3,8 +3,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.hedera.pbj.integration.EverythingTestData; +import com.hedera.pbj.runtime.OneOf; import com.hedera.pbj.runtime.ProtoConstants; import com.hedera.pbj.runtime.UnknownField; import com.hedera.pbj.runtime.io.buffer.BufferedData; @@ -12,6 +14,7 @@ import com.hedera.pbj.test.proto.pbj.Everything; import com.hedera.pbj.test.proto.pbj.MessageWithBytes; import com.hedera.pbj.test.proto.pbj.MessageWithBytesAndString; +import com.hedera.pbj.test.proto.pbj.MessageWithBytesWrapper; import org.junit.jupiter.api.Test; import pbj.integration.tests.pbj.integration.tests.MessageWithEverythingUnknownFields; @@ -95,4 +98,40 @@ void testEverythingRoundTrip() throws Exception { // and ensure it's equal to what we started with: assertEquals(EverythingTestData.EVERYTHING, everything); } + + @Test + public void testUnknownFieldsInInnerMessage() throws Exception { + // write MessageWithBytesAndString + MessageWithBytesAndString messageWithBytesAndString = new MessageWithBytesAndString(TEST_BYTES, TEST_STRING); + final Bytes messageWithBytesAndStringBytes = + MessageWithBytesAndString.PROTOBUF.toBytes(messageWithBytesAndString); + + // then read it as MessageWithBytes with unknown fields + final MessageWithBytes messageWithBytes = MessageWithBytes.PROTOBUF.parse( + messageWithBytesAndStringBytes.toReadableSequentialData(), false, true, 16); + + final MessageWithBytesWrapper messageWithBytesWrapper = new MessageWithBytesWrapper( + new OneOf<>(MessageWithBytesWrapper.MessageValidOneOfType.MESSAGE_WITH_BYTES, messageWithBytes)); + assertFalse( + messageWithBytesWrapper.messageWithBytes().getUnknownFields().isEmpty()); + assertEquals( + 1, messageWithBytesWrapper.messageWithBytes().getUnknownFields().size()); + + // write to bytes to simulate sending over the wire + final Bytes messageWithBytesWrapperBytes = MessageWithBytesWrapper.PROTOBUF.toBytes(messageWithBytesWrapper); + + // parse bytes back as a receiving user would and confirm unknown fields exist in inner message + final MessageWithBytesWrapper parsedWrapper = MessageWithBytesWrapper.PROTOBUF.parse( + messageWithBytesWrapperBytes.toReadableSequentialData(), false, true, 16); + MessageWithBytes parsedBytes = parsedWrapper.messageWithBytes(); + assertFalse(parsedBytes.getUnknownFields().isEmpty()); + assertEquals(1, parsedBytes.getUnknownFields().size()); + + // now confirm that user can retrieve unknown fields when using expanded message MessageWithBytesAndString + final Bytes messageWithBytesBytes = MessageWithBytes.PROTOBUF.toBytes(parsedBytes); + final MessageWithBytesAndString messageWithBytesAndStringParsed = + MessageWithBytesAndString.PROTOBUF.parse(messageWithBytesBytes.toReadableSequentialData()); + assertTrue(messageWithBytesAndStringParsed.getUnknownFields().isEmpty()); + assertEquals(messageWithBytesAndString, messageWithBytesAndStringParsed); + } }