Support pretty printing proto2 Extensions inside of proto3 Anys in TextFormat.Printer.

In Java, in order to include Extensions inside a proto message, the ExtensionRegistry for the extensions must be included during deserialization, otherwise they are treated as unknown fields.
This change adds support for providing an ExtensionRegistry for TextFormat.Printer which is then used during Any deserialization.

PiperOrigin-RevId: 586807633
pull/14925/head
Protobuf Team Bot 1 year ago committed by Copybara-Service
parent de57b67561
commit c8e01607ae
  1. 30
      java/core/src/main/java/com/google/protobuf/TextFormat.java
  2. 78
      java/core/src/test/java/com/google/protobuf/TextFormatTest.java

@ -283,16 +283,23 @@ public final class TextFormat {
public static final class Printer {
// Printer instance which escapes non-ASCII characters.
private static final Printer DEFAULT = new Printer(true, TypeRegistry.getEmptyTypeRegistry());
private static final Printer DEFAULT =
new Printer(
true, TypeRegistry.getEmptyTypeRegistry(), ExtensionRegistryLite.getEmptyRegistry());
/** Whether to escape non ASCII characters with backslash and octal. */
private final boolean escapeNonAscii;
private final TypeRegistry typeRegistry;
private final ExtensionRegistryLite extensionRegistry;
private Printer(boolean escapeNonAscii, TypeRegistry typeRegistry) {
private Printer(
boolean escapeNonAscii,
TypeRegistry typeRegistry,
ExtensionRegistryLite extensionRegistry) {
this.escapeNonAscii = escapeNonAscii;
this.typeRegistry = typeRegistry;
this.extensionRegistry = extensionRegistry;
}
/**
@ -305,7 +312,7 @@ public final class TextFormat {
* with the escape mode set to the given parameter.
*/
public Printer escapingNonAscii(boolean escapeNonAscii) {
return new Printer(escapeNonAscii, typeRegistry);
return new Printer(escapeNonAscii, typeRegistry, extensionRegistry);
}
/**
@ -318,7 +325,20 @@ public final class TextFormat {
if (this.typeRegistry != TypeRegistry.getEmptyTypeRegistry()) {
throw new IllegalArgumentException("Only one typeRegistry is allowed.");
}
return new Printer(escapeNonAscii, typeRegistry);
return new Printer(escapeNonAscii, typeRegistry, extensionRegistry);
}
/**
* Creates a new {@link Printer} using the given extensionRegistry. The new Printer clones all
* other configurations from the current {@link Printer}.
*
* @throws IllegalArgumentException if a registry is already set.
*/
public Printer usingExtensionRegistry(ExtensionRegistryLite extensionRegistry) {
if (this.extensionRegistry != ExtensionRegistryLite.getEmptyRegistry()) {
throw new IllegalArgumentException("Only one extensionRegistry is allowed.");
}
return new Printer(escapeNonAscii, typeRegistry, extensionRegistry);
}
/**
@ -377,7 +397,7 @@ public final class TextFormat {
return false;
}
contentBuilder = DynamicMessage.getDefaultInstance(contentType).newBuilderForType();
contentBuilder.mergeFrom((ByteString) value);
contentBuilder.mergeFrom((ByteString) value, extensionRegistry);
} catch (InvalidProtocolBufferException e) {
// The value of Any is malformed. We cannot print it out nicely, so fallback to printing out
// the type_url and value as bytes. Note that we fail open here to be consistent with

@ -11,6 +11,7 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.protobuf.TestUtil.TEST_REQUIRED_INITIALIZED;
import static com.google.protobuf.TestUtil.TEST_REQUIRED_UNINITIALIZED;
import static protobuf_unittest.UnittestProto.optionalInt32Extension;
import static org.junit.Assert.assertThrows;
import com.google.protobuf.DescriptorProtos.DescriptorProto;
@ -623,6 +624,83 @@ public class TextFormatTest {
assertThat(actual).isEqualTo(expected);
}
@Test
public void testPrintAny_anyWithDynamicMessageContainingExtensionTreatedAsUnknown()
throws Exception {
Descriptor descriptor =
createDescriptorForAny(
FieldDescriptorProto.newBuilder()
.setName("type_url")
.setNumber(1)
.setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
.setType(FieldDescriptorProto.Type.TYPE_STRING)
.build(),
FieldDescriptorProto.newBuilder()
.setName("value")
.setNumber(2)
.setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
.setType(FieldDescriptorProto.Type.TYPE_BYTES)
.build());
DynamicMessage testAny =
DynamicMessage.newBuilder(descriptor)
.setField(
descriptor.findFieldByNumber(1),
"type.googleapis.com/" + TestAllExtensions.getDescriptor().getFullName())
.setField(
descriptor.findFieldByNumber(2),
TestAllExtensions.newBuilder()
.setExtension(optionalInt32Extension, 12345)
.build()
.toByteString())
.build();
String actual =
TextFormat.printer()
.usingTypeRegistry(TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build())
.printToString(testAny);
String expected = "[type.googleapis.com/protobuf_unittest.TestAllExtensions] {\n 1: 12345\n}\n";
assertThat(actual).isEqualTo(expected);
}
@Test
public void testPrintAny_anyWithDynamicMessageContainingExtensionWithRegistry() throws Exception {
Descriptor descriptor =
createDescriptorForAny(
FieldDescriptorProto.newBuilder()
.setName("type_url")
.setNumber(1)
.setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
.setType(FieldDescriptorProto.Type.TYPE_STRING)
.build(),
FieldDescriptorProto.newBuilder()
.setName("value")
.setNumber(2)
.setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
.setType(FieldDescriptorProto.Type.TYPE_BYTES)
.build());
DynamicMessage testAny =
DynamicMessage.newBuilder(descriptor)
.setField(
descriptor.findFieldByNumber(1),
"type.googleapis.com/" + TestAllExtensions.getDescriptor().getFullName())
.setField(
descriptor.findFieldByNumber(2),
TestAllExtensions.newBuilder()
.setExtension(optionalInt32Extension, 12345)
.build()
.toByteString())
.build();
String actual =
TextFormat.printer()
.usingTypeRegistry(TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build())
.usingExtensionRegistry(TestUtil.getFullExtensionRegistry())
.printToString(testAny);
String expected =
"[type.googleapis.com/protobuf_unittest.TestAllExtensions] {\n"
+ " [protobuf_unittest.optional_int32_extension]: 12345\n"
+ "}\n";
assertThat(actual).isEqualTo(expected);
}
@Test
public void testPrintAny_anyFromWithNoValueField() throws Exception {
Descriptor descriptor =

Loading…
Cancel
Save