| Index: third_party/protobuf/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
|
| diff --git a/third_party/protobuf/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java b/third_party/protobuf/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
|
| index 76f3437a6b7a4e628e33269e9cbf7e73756deb88..ac712c94296be1fe92370c1d7a0323ac3bf60b99 100644
|
| --- a/third_party/protobuf/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
|
| +++ b/third_party/protobuf/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
|
| @@ -49,6 +49,7 @@ import com.google.protobuf.Descriptors.EnumDescriptor;
|
| import com.google.protobuf.Descriptors.EnumValueDescriptor;
|
| import com.google.protobuf.Descriptors.FieldDescriptor;
|
| import com.google.protobuf.Descriptors.FileDescriptor;
|
| +import com.google.protobuf.Descriptors.OneofDescriptor;
|
| import com.google.protobuf.DoubleValue;
|
| import com.google.protobuf.Duration;
|
| import com.google.protobuf.DynamicMessage;
|
| @@ -60,13 +61,13 @@ import com.google.protobuf.InvalidProtocolBufferException;
|
| import com.google.protobuf.ListValue;
|
| import com.google.protobuf.Message;
|
| import com.google.protobuf.MessageOrBuilder;
|
| +import com.google.protobuf.NullValue;
|
| import com.google.protobuf.StringValue;
|
| import com.google.protobuf.Struct;
|
| import com.google.protobuf.Timestamp;
|
| import com.google.protobuf.UInt32Value;
|
| import com.google.protobuf.UInt64Value;
|
| import com.google.protobuf.Value;
|
| -
|
| import java.io.IOException;
|
| import java.io.Reader;
|
| import java.io.StringReader;
|
| @@ -92,18 +93,17 @@ import java.util.logging.Logger;
|
| * as well.
|
| */
|
| public class JsonFormat {
|
| - private static final Logger logger =
|
| - Logger.getLogger(JsonFormat.class.getName());
|
| + private static final Logger logger = Logger.getLogger(JsonFormat.class.getName());
|
|
|
| private JsonFormat() {}
|
| -
|
| +
|
| /**
|
| * Creates a {@link Printer} with default configurations.
|
| */
|
| public static Printer printer() {
|
| - return new Printer(TypeRegistry.getEmptyTypeRegistry(), false, false);
|
| + return new Printer(TypeRegistry.getEmptyTypeRegistry(), false, false, false);
|
| }
|
| -
|
| +
|
| /**
|
| * A Printer converts protobuf message to JSON format.
|
| */
|
| @@ -111,27 +111,34 @@ public class JsonFormat {
|
| private final TypeRegistry registry;
|
| private final boolean includingDefaultValueFields;
|
| private final boolean preservingProtoFieldNames;
|
| + private final boolean omittingInsignificantWhitespace;
|
|
|
| private Printer(
|
| TypeRegistry registry,
|
| boolean includingDefaultValueFields,
|
| - boolean preservingProtoFieldNames) {
|
| + boolean preservingProtoFieldNames,
|
| + boolean omittingInsignificantWhitespace) {
|
| this.registry = registry;
|
| this.includingDefaultValueFields = includingDefaultValueFields;
|
| this.preservingProtoFieldNames = preservingProtoFieldNames;
|
| + this.omittingInsignificantWhitespace = omittingInsignificantWhitespace;
|
| }
|
| -
|
| +
|
| /**
|
| * Creates a new {@link Printer} using the given registry. The new Printer
|
| * clones all other configurations from the current {@link Printer}.
|
| - *
|
| + *
|
| * @throws IllegalArgumentException if a registry is already set.
|
| */
|
| public Printer usingTypeRegistry(TypeRegistry registry) {
|
| if (this.registry != TypeRegistry.getEmptyTypeRegistry()) {
|
| throw new IllegalArgumentException("Only one registry is allowed.");
|
| }
|
| - return new Printer(registry, includingDefaultValueFields, preservingProtoFieldNames);
|
| + return new Printer(
|
| + registry,
|
| + includingDefaultValueFields,
|
| + preservingProtoFieldNames,
|
| + omittingInsignificantWhitespace);
|
| }
|
|
|
| /**
|
| @@ -141,7 +148,8 @@ public class JsonFormat {
|
| * {@link Printer}.
|
| */
|
| public Printer includingDefaultValueFields() {
|
| - return new Printer(registry, true, preservingProtoFieldNames);
|
| + return new Printer(
|
| + registry, true, preservingProtoFieldNames, omittingInsignificantWhitespace);
|
| }
|
|
|
| /**
|
| @@ -151,30 +159,54 @@ public class JsonFormat {
|
| * current {@link Printer}.
|
| */
|
| public Printer preservingProtoFieldNames() {
|
| - return new Printer(registry, includingDefaultValueFields, true);
|
| + return new Printer(
|
| + registry, includingDefaultValueFields, true, omittingInsignificantWhitespace);
|
| + }
|
| +
|
| +
|
| + /**
|
| + * Create a new {@link Printer} that will omit all insignificant whitespace
|
| + * in the JSON output. This new Printer clones all other configurations from the
|
| + * current Printer. Insignificant whitespace is defined by the JSON spec as whitespace
|
| + * that appear between JSON structural elements:
|
| + * <pre>
|
| + * ws = *(
|
| + * %x20 / ; Space
|
| + * %x09 / ; Horizontal tab
|
| + * %x0A / ; Line feed or New line
|
| + * %x0D ) ; Carriage return
|
| + * </pre>
|
| + * See <a href="https://tools.ietf.org/html/rfc7159">https://tools.ietf.org/html/rfc7159</a>
|
| + * current {@link Printer}.
|
| + */
|
| + public Printer omittingInsignificantWhitespace() {
|
| + return new Printer(registry, includingDefaultValueFields, preservingProtoFieldNames, true);
|
| }
|
| -
|
| +
|
| /**
|
| * Converts a protobuf message to JSON format.
|
| - *
|
| + *
|
| * @throws InvalidProtocolBufferException if the message contains Any types
|
| * that can't be resolved.
|
| * @throws IOException if writing to the output fails.
|
| */
|
| - public void appendTo(MessageOrBuilder message, Appendable output)
|
| - throws IOException {
|
| + public void appendTo(MessageOrBuilder message, Appendable output) throws IOException {
|
| // TODO(xiaofeng): Investigate the allocation overhead and optimize for
|
| // mobile.
|
| - new PrinterImpl(registry, includingDefaultValueFields, preservingProtoFieldNames, output)
|
| + new PrinterImpl(
|
| + registry,
|
| + includingDefaultValueFields,
|
| + preservingProtoFieldNames,
|
| + output,
|
| + omittingInsignificantWhitespace)
|
| .print(message);
|
| }
|
|
|
| /**
|
| * Converts a protobuf message to JSON format. Throws exceptions if there
|
| - * are unknown Any types in the message.
|
| + * are unknown Any types in the message.
|
| */
|
| - public String print(MessageOrBuilder message)
|
| - throws InvalidProtocolBufferException {
|
| + public String print(MessageOrBuilder message) throws InvalidProtocolBufferException {
|
| try {
|
| StringBuilder builder = new StringBuilder();
|
| appendTo(message, builder);
|
| @@ -192,57 +224,75 @@ public class JsonFormat {
|
| * Creates a {@link Parser} with default configuration.
|
| */
|
| public static Parser parser() {
|
| - return new Parser(TypeRegistry.getEmptyTypeRegistry());
|
| + return new Parser(TypeRegistry.getEmptyTypeRegistry(), false, Parser.DEFAULT_RECURSION_LIMIT);
|
| }
|
| -
|
| +
|
| /**
|
| * A Parser parses JSON to protobuf message.
|
| */
|
| public static class Parser {
|
| private final TypeRegistry registry;
|
| -
|
| - private Parser(TypeRegistry registry) {
|
| - this.registry = registry;
|
| + private final boolean ignoringUnknownFields;
|
| + private final int recursionLimit;
|
| +
|
| + // The default parsing recursion limit is aligned with the proto binary parser.
|
| + private static final int DEFAULT_RECURSION_LIMIT = 100;
|
| +
|
| + private Parser(TypeRegistry registry, boolean ignoreUnknownFields, int recursionLimit) {
|
| + this.registry = registry;
|
| + this.ignoringUnknownFields = ignoreUnknownFields;
|
| + this.recursionLimit = recursionLimit;
|
| }
|
| -
|
| +
|
| /**
|
| * Creates a new {@link Parser} using the given registry. The new Parser
|
| * clones all other configurations from this Parser.
|
| - *
|
| + *
|
| * @throws IllegalArgumentException if a registry is already set.
|
| */
|
| public Parser usingTypeRegistry(TypeRegistry registry) {
|
| if (this.registry != TypeRegistry.getEmptyTypeRegistry()) {
|
| throw new IllegalArgumentException("Only one registry is allowed.");
|
| }
|
| - return new Parser(registry);
|
| + return new Parser(registry, ignoringUnknownFields, recursionLimit);
|
| + }
|
| +
|
| + /**
|
| + * Creates a new {@link Parser} configured to not throw an exception when an unknown field is
|
| + * encountered. The new Parser clones all other configurations from this Parser.
|
| + */
|
| + public Parser ignoringUnknownFields() {
|
| + return new Parser(this.registry, true, recursionLimit);
|
| }
|
| -
|
| +
|
| /**
|
| * Parses from JSON into a protobuf message.
|
| - *
|
| + *
|
| * @throws InvalidProtocolBufferException if the input is not valid JSON
|
| * format or there are unknown fields in the input.
|
| */
|
| - public void merge(String json, Message.Builder builder)
|
| - throws InvalidProtocolBufferException {
|
| + public void merge(String json, Message.Builder builder) throws InvalidProtocolBufferException {
|
| // TODO(xiaofeng): Investigate the allocation overhead and optimize for
|
| // mobile.
|
| - new ParserImpl(registry).merge(json, builder);
|
| + new ParserImpl(registry, ignoringUnknownFields, recursionLimit).merge(json, builder);
|
| }
|
| -
|
| +
|
| /**
|
| * Parses from JSON into a protobuf message.
|
| - *
|
| + *
|
| * @throws InvalidProtocolBufferException if the input is not valid JSON
|
| * format or there are unknown fields in the input.
|
| * @throws IOException if reading from the input throws.
|
| */
|
| - public void merge(Reader json, Message.Builder builder)
|
| - throws IOException {
|
| + public void merge(Reader json, Message.Builder builder) throws IOException {
|
| // TODO(xiaofeng): Investigate the allocation overhead and optimize for
|
| // mobile.
|
| - new ParserImpl(registry).merge(json, builder);
|
| + new ParserImpl(registry, ignoringUnknownFields, recursionLimit).merge(json, builder);
|
| + }
|
| +
|
| + // For testing only.
|
| + Parser usingRecursionLimit(int recursionLimit) {
|
| + return new Parser(registry, ignoringUnknownFields, recursionLimit);
|
| }
|
| }
|
|
|
| @@ -255,8 +305,8 @@ public class JsonFormat {
|
| */
|
| public static class TypeRegistry {
|
| private static class EmptyTypeRegistryHolder {
|
| - private static final TypeRegistry EMPTY = new TypeRegistry(
|
| - Collections.<String, Descriptor>emptyMap());
|
| + private static final TypeRegistry EMPTY =
|
| + new TypeRegistry(Collections.<String, Descriptor>emptyMap());
|
| }
|
|
|
| public static TypeRegistry getEmptyTypeRegistry() {
|
| @@ -293,8 +343,7 @@ public class JsonFormat {
|
| */
|
| public Builder add(Descriptor messageType) {
|
| if (types == null) {
|
| - throw new IllegalStateException(
|
| - "A TypeRegistry.Builer can only be used once.");
|
| + throw new IllegalStateException("A TypeRegistry.Builer can only be used once.");
|
| }
|
| addFile(messageType.getFile());
|
| return this;
|
| @@ -306,8 +355,7 @@ public class JsonFormat {
|
| */
|
| public Builder add(Iterable<Descriptor> messageTypes) {
|
| if (types == null) {
|
| - throw new IllegalStateException(
|
| - "A TypeRegistry.Builer can only be used once.");
|
| + throw new IllegalStateException("A TypeRegistry.Builer can only be used once.");
|
| }
|
| for (Descriptor type : messageTypes) {
|
| addFile(type.getFile());
|
| @@ -345,8 +393,7 @@ public class JsonFormat {
|
| }
|
|
|
| if (types.containsKey(message.getFullName())) {
|
| - logger.warning("Type " + message.getFullName()
|
| - + " is added multiple times.");
|
| + logger.warning("Type " + message.getFullName() + " is added multiple times.");
|
| return;
|
| }
|
|
|
| @@ -354,20 +401,58 @@ public class JsonFormat {
|
| }
|
|
|
| private final Set<String> files = new HashSet<String>();
|
| - private Map<String, Descriptor> types =
|
| - new HashMap<String, Descriptor>();
|
| + private Map<String, Descriptor> types = new HashMap<String, Descriptor>();
|
| }
|
| }
|
|
|
| /**
|
| + * An interface for json formatting that can be used in
|
| + * combination with the omittingInsignificantWhitespace() method
|
| + */
|
| + interface TextGenerator {
|
| + void indent();
|
| +
|
| + void outdent();
|
| +
|
| + void print(final CharSequence text) throws IOException;
|
| + }
|
| +
|
| + /**
|
| + * Format the json without indentation
|
| + */
|
| + private static final class CompactTextGenerator implements TextGenerator {
|
| + private final Appendable output;
|
| +
|
| + private CompactTextGenerator(final Appendable output) {
|
| + this.output = output;
|
| + }
|
| +
|
| + /**
|
| + * ignored by compact printer
|
| + */
|
| + public void indent() {}
|
| +
|
| + /**
|
| + * ignored by compact printer
|
| + */
|
| + public void outdent() {}
|
| +
|
| + /**
|
| + * Print text to the output stream.
|
| + */
|
| + public void print(final CharSequence text) throws IOException {
|
| + output.append(text);
|
| + }
|
| + }
|
| + /**
|
| * A TextGenerator adds indentation when writing formatted text.
|
| */
|
| - private static final class TextGenerator {
|
| + private static final class PrettyTextGenerator implements TextGenerator {
|
| private final Appendable output;
|
| private final StringBuilder indent = new StringBuilder();
|
| private boolean atStartOfLine = true;
|
|
|
| - private TextGenerator(final Appendable output) {
|
| + private PrettyTextGenerator(final Appendable output) {
|
| this.output = output;
|
| }
|
|
|
| @@ -387,8 +472,7 @@ public class JsonFormat {
|
| public void outdent() {
|
| final int length = indent.length();
|
| if (length < 2) {
|
| - throw new IllegalArgumentException(
|
| - " Outdent() without matching Indent().");
|
| + throw new IllegalArgumentException(" Outdent() without matching Indent().");
|
| }
|
| indent.delete(length - 2, length);
|
| }
|
| @@ -432,6 +516,8 @@ public class JsonFormat {
|
| private final TextGenerator generator;
|
| // We use Gson to help handle string escapes.
|
| private final Gson gson;
|
| + private final CharSequence blankOrSpace;
|
| + private final CharSequence blankOrNewLine;
|
|
|
| private static class GsonHolder {
|
| private static final Gson DEFAULT_GSON = new GsonBuilder().disableHtmlEscaping().create();
|
| @@ -441,54 +527,60 @@ public class JsonFormat {
|
| TypeRegistry registry,
|
| boolean includingDefaultValueFields,
|
| boolean preservingProtoFieldNames,
|
| - Appendable jsonOutput) {
|
| + Appendable jsonOutput,
|
| + boolean omittingInsignificantWhitespace) {
|
| this.registry = registry;
|
| this.includingDefaultValueFields = includingDefaultValueFields;
|
| this.preservingProtoFieldNames = preservingProtoFieldNames;
|
| - this.generator = new TextGenerator(jsonOutput);
|
| this.gson = GsonHolder.DEFAULT_GSON;
|
| + // json format related properties, determined by printerType
|
| + if (omittingInsignificantWhitespace) {
|
| + this.generator = new CompactTextGenerator(jsonOutput);
|
| + this.blankOrSpace = "";
|
| + this.blankOrNewLine = "";
|
| + } else {
|
| + this.generator = new PrettyTextGenerator(jsonOutput);
|
| + this.blankOrSpace = " ";
|
| + this.blankOrNewLine = "\n";
|
| + }
|
| }
|
|
|
| void print(MessageOrBuilder message) throws IOException {
|
| - WellKnownTypePrinter specialPrinter = wellKnownTypePrinters.get(
|
| - message.getDescriptorForType().getFullName());
|
| + WellKnownTypePrinter specialPrinter =
|
| + wellKnownTypePrinters.get(message.getDescriptorForType().getFullName());
|
| if (specialPrinter != null) {
|
| specialPrinter.print(this, message);
|
| return;
|
| }
|
| print(message, null);
|
| }
|
| -
|
| +
|
| private interface WellKnownTypePrinter {
|
| - void print(PrinterImpl printer, MessageOrBuilder message)
|
| - throws IOException;
|
| - }
|
| -
|
| - private static final Map<String, WellKnownTypePrinter>
|
| - wellKnownTypePrinters = buildWellKnownTypePrinters();
|
| -
|
| - private static Map<String, WellKnownTypePrinter>
|
| - buildWellKnownTypePrinters() {
|
| - Map<String, WellKnownTypePrinter> printers =
|
| - new HashMap<String, WellKnownTypePrinter>();
|
| + void print(PrinterImpl printer, MessageOrBuilder message) throws IOException;
|
| + }
|
| +
|
| + private static final Map<String, WellKnownTypePrinter> wellKnownTypePrinters =
|
| + buildWellKnownTypePrinters();
|
| +
|
| + private static Map<String, WellKnownTypePrinter> buildWellKnownTypePrinters() {
|
| + Map<String, WellKnownTypePrinter> printers = new HashMap<String, WellKnownTypePrinter>();
|
| // Special-case Any.
|
| - printers.put(Any.getDescriptor().getFullName(),
|
| + printers.put(
|
| + Any.getDescriptor().getFullName(),
|
| new WellKnownTypePrinter() {
|
| - @Override
|
| - public void print(PrinterImpl printer, MessageOrBuilder message)
|
| - throws IOException {
|
| - printer.printAny(message);
|
| - }
|
| - });
|
| + @Override
|
| + public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
|
| + printer.printAny(message);
|
| + }
|
| + });
|
| // Special-case wrapper types.
|
| - WellKnownTypePrinter wrappersPrinter = new WellKnownTypePrinter() {
|
| - @Override
|
| - public void print(PrinterImpl printer, MessageOrBuilder message)
|
| - throws IOException {
|
| - printer.printWrapper(message);
|
| -
|
| - }
|
| - };
|
| + WellKnownTypePrinter wrappersPrinter =
|
| + new WellKnownTypePrinter() {
|
| + @Override
|
| + public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
|
| + printer.printWrapper(message);
|
| + }
|
| + };
|
| printers.put(BoolValue.getDescriptor().getFullName(), wrappersPrinter);
|
| printers.put(Int32Value.getDescriptor().getFullName(), wrappersPrinter);
|
| printers.put(UInt32Value.getDescriptor().getFullName(), wrappersPrinter);
|
| @@ -499,70 +591,75 @@ public class JsonFormat {
|
| printers.put(FloatValue.getDescriptor().getFullName(), wrappersPrinter);
|
| printers.put(DoubleValue.getDescriptor().getFullName(), wrappersPrinter);
|
| // Special-case Timestamp.
|
| - printers.put(Timestamp.getDescriptor().getFullName(),
|
| + printers.put(
|
| + Timestamp.getDescriptor().getFullName(),
|
| new WellKnownTypePrinter() {
|
| - @Override
|
| - public void print(PrinterImpl printer, MessageOrBuilder message)
|
| - throws IOException {
|
| - printer.printTimestamp(message);
|
| - }
|
| - });
|
| + @Override
|
| + public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
|
| + printer.printTimestamp(message);
|
| + }
|
| + });
|
| // Special-case Duration.
|
| - printers.put(Duration.getDescriptor().getFullName(),
|
| + printers.put(
|
| + Duration.getDescriptor().getFullName(),
|
| new WellKnownTypePrinter() {
|
| - @Override
|
| - public void print(PrinterImpl printer, MessageOrBuilder message)
|
| - throws IOException {
|
| - printer.printDuration(message);
|
| - }
|
| - });
|
| + @Override
|
| + public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
|
| + printer.printDuration(message);
|
| + }
|
| + });
|
| // Special-case FieldMask.
|
| - printers.put(FieldMask.getDescriptor().getFullName(),
|
| + printers.put(
|
| + FieldMask.getDescriptor().getFullName(),
|
| new WellKnownTypePrinter() {
|
| - @Override
|
| - public void print(PrinterImpl printer, MessageOrBuilder message)
|
| - throws IOException {
|
| - printer.printFieldMask(message);
|
| - }
|
| - });
|
| + @Override
|
| + public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
|
| + printer.printFieldMask(message);
|
| + }
|
| + });
|
| // Special-case Struct.
|
| - printers.put(Struct.getDescriptor().getFullName(),
|
| + printers.put(
|
| + Struct.getDescriptor().getFullName(),
|
| new WellKnownTypePrinter() {
|
| - @Override
|
| - public void print(PrinterImpl printer, MessageOrBuilder message)
|
| - throws IOException {
|
| - printer.printStruct(message);
|
| - }
|
| - });
|
| + @Override
|
| + public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
|
| + printer.printStruct(message);
|
| + }
|
| + });
|
| // Special-case Value.
|
| - printers.put(Value.getDescriptor().getFullName(),
|
| + printers.put(
|
| + Value.getDescriptor().getFullName(),
|
| new WellKnownTypePrinter() {
|
| - @Override
|
| - public void print(PrinterImpl printer, MessageOrBuilder message)
|
| - throws IOException {
|
| - printer.printValue(message);
|
| - }
|
| - });
|
| + @Override
|
| + public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
|
| + printer.printValue(message);
|
| + }
|
| + });
|
| // Special-case ListValue.
|
| - printers.put(ListValue.getDescriptor().getFullName(),
|
| + printers.put(
|
| + ListValue.getDescriptor().getFullName(),
|
| new WellKnownTypePrinter() {
|
| - @Override
|
| - public void print(PrinterImpl printer, MessageOrBuilder message)
|
| - throws IOException {
|
| - printer.printListValue(message);
|
| - }
|
| - });
|
| + @Override
|
| + public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
|
| + printer.printListValue(message);
|
| + }
|
| + });
|
| return printers;
|
| }
|
| -
|
| +
|
| /** Prints google.protobuf.Any */
|
| private void printAny(MessageOrBuilder message) throws IOException {
|
| + if (Any.getDefaultInstance().equals(message)) {
|
| + generator.print("{}");
|
| + return;
|
| + }
|
| Descriptor descriptor = message.getDescriptorForType();
|
| FieldDescriptor typeUrlField = descriptor.findFieldByName("type_url");
|
| FieldDescriptor valueField = descriptor.findFieldByName("value");
|
| // Validates type of the message. Note that we can't just cast the message
|
| - // to com.google.protobuf.Any because it might be a DynamicMessage.
|
| - if (typeUrlField == null || valueField == null
|
| + // to com.google.protobuf.Any because it might be a DynamicMessage.
|
| + if (typeUrlField == null
|
| + || valueField == null
|
| || typeUrlField.getType() != FieldDescriptor.Type.STRING
|
| || valueField.getType() != FieldDescriptor.Type.BYTES) {
|
| throw new InvalidProtocolBufferException("Invalid Any type.");
|
| @@ -571,22 +668,21 @@ public class JsonFormat {
|
| String typeName = getTypeName(typeUrl);
|
| Descriptor type = registry.find(typeName);
|
| if (type == null) {
|
| - throw new InvalidProtocolBufferException(
|
| - "Cannot find type for url: " + typeUrl);
|
| + throw new InvalidProtocolBufferException("Cannot find type for url: " + typeUrl);
|
| }
|
| ByteString content = (ByteString) message.getField(valueField);
|
| - Message contentMessage = DynamicMessage.getDefaultInstance(type)
|
| - .getParserForType().parseFrom(content);
|
| + Message contentMessage =
|
| + DynamicMessage.getDefaultInstance(type).getParserForType().parseFrom(content);
|
| WellKnownTypePrinter printer = wellKnownTypePrinters.get(typeName);
|
| if (printer != null) {
|
| // If the type is one of the well-known types, we use a special
|
| // formatting.
|
| - generator.print("{\n");
|
| + generator.print("{" + blankOrNewLine);
|
| generator.indent();
|
| - generator.print("\"@type\": " + gson.toJson(typeUrl) + ",\n");
|
| - generator.print("\"value\": ");
|
| + generator.print("\"@type\":" + blankOrSpace + gson.toJson(typeUrl) + "," + blankOrNewLine);
|
| + generator.print("\"value\":" + blankOrSpace);
|
| printer.print(this, contentMessage);
|
| - generator.print("\n");
|
| + generator.print(blankOrNewLine);
|
| generator.outdent();
|
| generator.print("}");
|
| } else {
|
| @@ -594,7 +690,7 @@ public class JsonFormat {
|
| print(contentMessage, typeUrl);
|
| }
|
| }
|
| -
|
| +
|
| /** Prints wrapper types (e.g., google.protobuf.Int32Value) */
|
| private void printWrapper(MessageOrBuilder message) throws IOException {
|
| Descriptor descriptor = message.getDescriptorForType();
|
| @@ -606,7 +702,7 @@ public class JsonFormat {
|
| // the whole message.
|
| printSingleFieldValue(valueField, message.getField(valueField));
|
| }
|
| -
|
| +
|
| private ByteString toByteString(MessageOrBuilder message) {
|
| if (message instanceof Message) {
|
| return ((Message) message).toByteString();
|
| @@ -614,26 +710,25 @@ public class JsonFormat {
|
| return ((Message.Builder) message).build().toByteString();
|
| }
|
| }
|
| -
|
| +
|
| /** Prints google.protobuf.Timestamp */
|
| private void printTimestamp(MessageOrBuilder message) throws IOException {
|
| Timestamp value = Timestamp.parseFrom(toByteString(message));
|
| - generator.print("\"" + TimeUtil.toString(value) + "\"");
|
| + generator.print("\"" + Timestamps.toString(value) + "\"");
|
| }
|
| -
|
| +
|
| /** Prints google.protobuf.Duration */
|
| private void printDuration(MessageOrBuilder message) throws IOException {
|
| Duration value = Duration.parseFrom(toByteString(message));
|
| - generator.print("\"" + TimeUtil.toString(value) + "\"");
|
| -
|
| + generator.print("\"" + Durations.toString(value) + "\"");
|
| }
|
| -
|
| +
|
| /** Prints google.protobuf.FieldMask */
|
| private void printFieldMask(MessageOrBuilder message) throws IOException {
|
| FieldMask value = FieldMask.parseFrom(toByteString(message));
|
| - generator.print("\"" + FieldMaskUtil.toString(value) + "\"");
|
| + generator.print("\"" + FieldMaskUtil.toJsonString(value) + "\"");
|
| }
|
| -
|
| +
|
| /** Prints google.protobuf.Struct */
|
| private void printStruct(MessageOrBuilder message) throws IOException {
|
| Descriptor descriptor = message.getDescriptorForType();
|
| @@ -644,7 +739,7 @@ public class JsonFormat {
|
| // Struct is formatted as a map object.
|
| printMapFieldValue(field, message.getField(field));
|
| }
|
| -
|
| +
|
| /** Prints google.protobuf.Value */
|
| private void printValue(MessageOrBuilder message) throws IOException {
|
| // For a Value message, only the value of the field is formatted.
|
| @@ -663,7 +758,7 @@ public class JsonFormat {
|
| printSingleFieldValue(entry.getKey(), entry.getValue());
|
| }
|
| }
|
| -
|
| +
|
| /** Prints google.protobuf.ListValue */
|
| private void printListValue(MessageOrBuilder message) throws IOException {
|
| Descriptor descriptor = message.getDescriptorForType();
|
| @@ -675,26 +770,31 @@ public class JsonFormat {
|
| }
|
|
|
| /** Prints a regular message with an optional type URL. */
|
| - private void print(MessageOrBuilder message, String typeUrl)
|
| - throws IOException {
|
| - generator.print("{\n");
|
| + private void print(MessageOrBuilder message, String typeUrl) throws IOException {
|
| + generator.print("{" + blankOrNewLine);
|
| generator.indent();
|
|
|
| boolean printedField = false;
|
| if (typeUrl != null) {
|
| - generator.print("\"@type\": " + gson.toJson(typeUrl));
|
| + generator.print("\"@type\":" + blankOrSpace + gson.toJson(typeUrl));
|
| printedField = true;
|
| }
|
| Map<FieldDescriptor, Object> fieldsToPrint = null;
|
| if (includingDefaultValueFields) {
|
| fieldsToPrint = new TreeMap<FieldDescriptor, Object>();
|
| for (FieldDescriptor field : message.getDescriptorForType().getFields()) {
|
| - if (field.isOptional()
|
| - && field.getJavaType() == FieldDescriptor.JavaType.MESSAGE
|
| - && !message.hasField(field)) {
|
| - // Always skip empty optional message fields. If not we will recurse indefinitely if
|
| - // a message has itself as a sub-field.
|
| - continue;
|
| + if (field.isOptional()) {
|
| + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE
|
| + && !message.hasField(field)){
|
| + // Always skip empty optional message fields. If not we will recurse indefinitely if
|
| + // a message has itself as a sub-field.
|
| + continue;
|
| + }
|
| + OneofDescriptor oneof = field.getContainingOneof();
|
| + if (oneof != null && !message.hasField(field)) {
|
| + // Skip all oneof fields except the one that is actually set
|
| + continue;
|
| + }
|
| }
|
| fieldsToPrint.put(field, message.getField(field));
|
| }
|
| @@ -704,27 +804,26 @@ public class JsonFormat {
|
| for (Map.Entry<FieldDescriptor, Object> field : fieldsToPrint.entrySet()) {
|
| if (printedField) {
|
| // Add line-endings for the previous field.
|
| - generator.print(",\n");
|
| + generator.print("," + blankOrNewLine);
|
| } else {
|
| printedField = true;
|
| }
|
| printField(field.getKey(), field.getValue());
|
| }
|
| -
|
| +
|
| // Add line-endings for the last field.
|
| if (printedField) {
|
| - generator.print("\n");
|
| + generator.print(blankOrNewLine);
|
| }
|
| generator.outdent();
|
| generator.print("}");
|
| }
|
|
|
| - private void printField(FieldDescriptor field, Object value)
|
| - throws IOException {
|
| + private void printField(FieldDescriptor field, Object value) throws IOException {
|
| if (preservingProtoFieldNames) {
|
| - generator.print("\"" + field.getName() + "\": ");
|
| + generator.print("\"" + field.getName() + "\":" + blankOrSpace);
|
| } else {
|
| - generator.print("\"" + field.getJsonName() + "\": ");
|
| + generator.print("\"" + field.getJsonName() + "\":" + blankOrSpace);
|
| }
|
| if (field.isMapField()) {
|
| printMapFieldValue(field, value);
|
| @@ -734,15 +833,14 @@ public class JsonFormat {
|
| printSingleFieldValue(field, value);
|
| }
|
| }
|
| -
|
| +
|
| @SuppressWarnings("rawtypes")
|
| - private void printRepeatedFieldValue(FieldDescriptor field, Object value)
|
| - throws IOException {
|
| + private void printRepeatedFieldValue(FieldDescriptor field, Object value) throws IOException {
|
| generator.print("[");
|
| boolean printedElement = false;
|
| for (Object element : (List) value) {
|
| if (printedElement) {
|
| - generator.print(", ");
|
| + generator.print("," + blankOrSpace);
|
| } else {
|
| printedElement = true;
|
| }
|
| @@ -750,17 +848,16 @@ public class JsonFormat {
|
| }
|
| generator.print("]");
|
| }
|
| -
|
| +
|
| @SuppressWarnings("rawtypes")
|
| - private void printMapFieldValue(FieldDescriptor field, Object value)
|
| - throws IOException {
|
| + private void printMapFieldValue(FieldDescriptor field, Object value) throws IOException {
|
| Descriptor type = field.getMessageType();
|
| FieldDescriptor keyField = type.findFieldByName("key");
|
| FieldDescriptor valueField = type.findFieldByName("value");
|
| if (keyField == null || valueField == null) {
|
| throw new InvalidProtocolBufferException("Invalid map field.");
|
| }
|
| - generator.print("{\n");
|
| + generator.print("{" + blankOrNewLine);
|
| generator.indent();
|
| boolean printedElement = false;
|
| for (Object element : (List) value) {
|
| @@ -768,36 +865,35 @@ public class JsonFormat {
|
| Object entryKey = entry.getField(keyField);
|
| Object entryValue = entry.getField(valueField);
|
| if (printedElement) {
|
| - generator.print(",\n");
|
| + generator.print("," + blankOrNewLine);
|
| } else {
|
| printedElement = true;
|
| }
|
| // Key fields are always double-quoted.
|
| printSingleFieldValue(keyField, entryKey, true);
|
| - generator.print(": ");
|
| + generator.print(":" + blankOrSpace);
|
| printSingleFieldValue(valueField, entryValue);
|
| }
|
| if (printedElement) {
|
| - generator.print("\n");
|
| + generator.print(blankOrNewLine);
|
| }
|
| generator.outdent();
|
| generator.print("}");
|
| }
|
| -
|
| - private void printSingleFieldValue(FieldDescriptor field, Object value)
|
| - throws IOException {
|
| +
|
| + private void printSingleFieldValue(FieldDescriptor field, Object value) throws IOException {
|
| printSingleFieldValue(field, value, false);
|
| }
|
|
|
| /**
|
| * Prints a field's value in JSON format.
|
| - *
|
| + *
|
| * @param alwaysWithQuotes whether to always add double-quotes to primitive
|
| * types.
|
| */
|
| private void printSingleFieldValue(
|
| - final FieldDescriptor field, final Object value,
|
| - boolean alwaysWithQuotes) throws IOException {
|
| + final FieldDescriptor field, final Object value, boolean alwaysWithQuotes)
|
| + throws IOException {
|
| switch (field.getType()) {
|
| case INT32:
|
| case SINT32:
|
| @@ -851,7 +947,7 @@ public class JsonFormat {
|
| }
|
| }
|
| break;
|
| -
|
| +
|
| case DOUBLE:
|
| Double doubleValue = (Double) value;
|
| if (doubleValue.isNaN()) {
|
| @@ -895,15 +991,13 @@ public class JsonFormat {
|
|
|
| case BYTES:
|
| generator.print("\"");
|
| - generator.print(
|
| - BaseEncoding.base64().encode(((ByteString) value).toByteArray()));
|
| + generator.print(BaseEncoding.base64().encode(((ByteString) value).toByteArray()));
|
| generator.print("\"");
|
| break;
|
|
|
| case ENUM:
|
| // Special-case google.protobuf.NullValue (it's an Enum).
|
| - if (field.getEnumType().getFullName().equals(
|
| - "google.protobuf.NullValue")) {
|
| + if (field.getEnumType().getFullName().equals("google.protobuf.NullValue")) {
|
| // No matter what value it contains, we always print it as "null".
|
| if (alwaysWithQuotes) {
|
| generator.print("\"");
|
| @@ -914,11 +1008,9 @@ public class JsonFormat {
|
| }
|
| } else {
|
| if (((EnumValueDescriptor) value).getIndex() == -1) {
|
| - generator.print(
|
| - String.valueOf(((EnumValueDescriptor) value).getNumber()));
|
| + generator.print(String.valueOf(((EnumValueDescriptor) value).getNumber()));
|
| } else {
|
| - generator.print(
|
| - "\"" + ((EnumValueDescriptor) value).getName() + "\"");
|
| + generator.print("\"" + ((EnumValueDescriptor) value).getName() + "\"");
|
| }
|
| }
|
| break;
|
| @@ -947,40 +1039,40 @@ public class JsonFormat {
|
| } else {
|
| // Pull off the most-significant bit so that BigInteger doesn't think
|
| // the number is negative, then set it again using setBit().
|
| - return BigInteger.valueOf(value & Long.MAX_VALUE)
|
| - .setBit(Long.SIZE - 1).toString();
|
| + return BigInteger.valueOf(value & Long.MAX_VALUE).setBit(Long.SIZE - 1).toString();
|
| }
|
| }
|
| -
|
|
|
| - private static String getTypeName(String typeUrl)
|
| - throws InvalidProtocolBufferException {
|
| + private static String getTypeName(String typeUrl) throws InvalidProtocolBufferException {
|
| String[] parts = typeUrl.split("/");
|
| if (parts.length == 1) {
|
| - throw new InvalidProtocolBufferException(
|
| - "Invalid type url found: " + typeUrl);
|
| + throw new InvalidProtocolBufferException("Invalid type url found: " + typeUrl);
|
| }
|
| return parts[parts.length - 1];
|
| }
|
| -
|
| +
|
| private static class ParserImpl {
|
| private final TypeRegistry registry;
|
| private final JsonParser jsonParser;
|
| -
|
| - ParserImpl(TypeRegistry registry) {
|
| + private final boolean ignoringUnknownFields;
|
| + private final int recursionLimit;
|
| + private int currentDepth;
|
| +
|
| + ParserImpl(TypeRegistry registry, boolean ignoreUnknownFields, int recursionLimit) {
|
| this.registry = registry;
|
| + this.ignoringUnknownFields = ignoreUnknownFields;
|
| this.jsonParser = new JsonParser();
|
| + this.recursionLimit = recursionLimit;
|
| + this.currentDepth = 0;
|
| }
|
| -
|
| - void merge(Reader json, Message.Builder builder)
|
| - throws IOException {
|
| +
|
| + void merge(Reader json, Message.Builder builder) throws IOException {
|
| JsonReader reader = new JsonReader(json);
|
| reader.setLenient(false);
|
| merge(jsonParser.parse(reader), builder);
|
| }
|
| -
|
| - void merge(String json, Message.Builder builder)
|
| - throws InvalidProtocolBufferException {
|
| +
|
| + void merge(String json, Message.Builder builder) throws InvalidProtocolBufferException {
|
| try {
|
| JsonReader reader = new JsonReader(new StringReader(json));
|
| reader.setLenient(false);
|
| @@ -992,35 +1084,36 @@ public class JsonFormat {
|
| throw new InvalidProtocolBufferException(e.getMessage());
|
| }
|
| }
|
| -
|
| +
|
| private interface WellKnownTypeParser {
|
| void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
|
| throws InvalidProtocolBufferException;
|
| }
|
| -
|
| +
|
| private static final Map<String, WellKnownTypeParser> wellKnownTypeParsers =
|
| buildWellKnownTypeParsers();
|
| -
|
| - private static Map<String, WellKnownTypeParser>
|
| - buildWellKnownTypeParsers() {
|
| - Map<String, WellKnownTypeParser> parsers =
|
| - new HashMap<String, WellKnownTypeParser>();
|
| +
|
| + private static Map<String, WellKnownTypeParser> buildWellKnownTypeParsers() {
|
| + Map<String, WellKnownTypeParser> parsers = new HashMap<String, WellKnownTypeParser>();
|
| // Special-case Any.
|
| - parsers.put(Any.getDescriptor().getFullName(), new WellKnownTypeParser() {
|
| - @Override
|
| - public void merge(ParserImpl parser, JsonElement json,
|
| - Message.Builder builder) throws InvalidProtocolBufferException {
|
| - parser.mergeAny(json, builder);
|
| - }
|
| - });
|
| + parsers.put(
|
| + Any.getDescriptor().getFullName(),
|
| + new WellKnownTypeParser() {
|
| + @Override
|
| + public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
|
| + throws InvalidProtocolBufferException {
|
| + parser.mergeAny(json, builder);
|
| + }
|
| + });
|
| // Special-case wrapper types.
|
| - WellKnownTypeParser wrappersPrinter = new WellKnownTypeParser() {
|
| - @Override
|
| - public void merge(ParserImpl parser, JsonElement json,
|
| - Message.Builder builder) throws InvalidProtocolBufferException {
|
| - parser.mergeWrapper(json, builder);
|
| - }
|
| - };
|
| + WellKnownTypeParser wrappersPrinter =
|
| + new WellKnownTypeParser() {
|
| + @Override
|
| + public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
|
| + throws InvalidProtocolBufferException {
|
| + parser.mergeWrapper(json, builder);
|
| + }
|
| + };
|
| parsers.put(BoolValue.getDescriptor().getFullName(), wrappersPrinter);
|
| parsers.put(Int32Value.getDescriptor().getFullName(), wrappersPrinter);
|
| parsers.put(UInt32Value.getDescriptor().getFullName(), wrappersPrinter);
|
| @@ -1031,82 +1124,86 @@ public class JsonFormat {
|
| parsers.put(FloatValue.getDescriptor().getFullName(), wrappersPrinter);
|
| parsers.put(DoubleValue.getDescriptor().getFullName(), wrappersPrinter);
|
| // Special-case Timestamp.
|
| - parsers.put(Timestamp.getDescriptor().getFullName(),
|
| + parsers.put(
|
| + Timestamp.getDescriptor().getFullName(),
|
| new WellKnownTypeParser() {
|
| - @Override
|
| - public void merge(ParserImpl parser, JsonElement json,
|
| - Message.Builder builder) throws InvalidProtocolBufferException {
|
| - parser.mergeTimestamp(json, builder);
|
| - }
|
| - });
|
| + @Override
|
| + public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
|
| + throws InvalidProtocolBufferException {
|
| + parser.mergeTimestamp(json, builder);
|
| + }
|
| + });
|
| // Special-case Duration.
|
| - parsers.put(Duration.getDescriptor().getFullName(),
|
| + parsers.put(
|
| + Duration.getDescriptor().getFullName(),
|
| new WellKnownTypeParser() {
|
| - @Override
|
| - public void merge(ParserImpl parser, JsonElement json,
|
| - Message.Builder builder) throws InvalidProtocolBufferException {
|
| - parser.mergeDuration(json, builder);
|
| - }
|
| - });
|
| + @Override
|
| + public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
|
| + throws InvalidProtocolBufferException {
|
| + parser.mergeDuration(json, builder);
|
| + }
|
| + });
|
| // Special-case FieldMask.
|
| - parsers.put(FieldMask.getDescriptor().getFullName(),
|
| + parsers.put(
|
| + FieldMask.getDescriptor().getFullName(),
|
| new WellKnownTypeParser() {
|
| - @Override
|
| - public void merge(ParserImpl parser, JsonElement json,
|
| - Message.Builder builder) throws InvalidProtocolBufferException {
|
| - parser.mergeFieldMask(json, builder);
|
| - }
|
| - });
|
| + @Override
|
| + public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
|
| + throws InvalidProtocolBufferException {
|
| + parser.mergeFieldMask(json, builder);
|
| + }
|
| + });
|
| // Special-case Struct.
|
| - parsers.put(Struct.getDescriptor().getFullName(),
|
| + parsers.put(
|
| + Struct.getDescriptor().getFullName(),
|
| new WellKnownTypeParser() {
|
| - @Override
|
| - public void merge(ParserImpl parser, JsonElement json,
|
| - Message.Builder builder) throws InvalidProtocolBufferException {
|
| - parser.mergeStruct(json, builder);
|
| - }
|
| - });
|
| + @Override
|
| + public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
|
| + throws InvalidProtocolBufferException {
|
| + parser.mergeStruct(json, builder);
|
| + }
|
| + });
|
| // Special-case ListValue.
|
| - parsers.put(ListValue.getDescriptor().getFullName(),
|
| + parsers.put(
|
| + ListValue.getDescriptor().getFullName(),
|
| new WellKnownTypeParser() {
|
| - @Override
|
| - public void merge(ParserImpl parser, JsonElement json,
|
| - Message.Builder builder) throws InvalidProtocolBufferException {
|
| - parser.mergeListValue(json, builder);
|
| - }
|
| - });
|
| + @Override
|
| + public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
|
| + throws InvalidProtocolBufferException {
|
| + parser.mergeListValue(json, builder);
|
| + }
|
| + });
|
| // Special-case Value.
|
| - parsers.put(Value.getDescriptor().getFullName(),
|
| + parsers.put(
|
| + Value.getDescriptor().getFullName(),
|
| new WellKnownTypeParser() {
|
| - @Override
|
| - public void merge(ParserImpl parser, JsonElement json,
|
| - Message.Builder builder) throws InvalidProtocolBufferException {
|
| - parser.mergeValue(json, builder);
|
| - }
|
| - });
|
| + @Override
|
| + public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
|
| + throws InvalidProtocolBufferException {
|
| + parser.mergeValue(json, builder);
|
| + }
|
| + });
|
| return parsers;
|
| }
|
| -
|
| +
|
| private void merge(JsonElement json, Message.Builder builder)
|
| throws InvalidProtocolBufferException {
|
| - WellKnownTypeParser specialParser = wellKnownTypeParsers.get(
|
| - builder.getDescriptorForType().getFullName());
|
| + WellKnownTypeParser specialParser =
|
| + wellKnownTypeParsers.get(builder.getDescriptorForType().getFullName());
|
| if (specialParser != null) {
|
| specialParser.merge(this, json, builder);
|
| return;
|
| }
|
| mergeMessage(json, builder, false);
|
| }
|
| -
|
| +
|
| // Maps from camel-case field names to FieldDescriptor.
|
| private final Map<Descriptor, Map<String, FieldDescriptor>> fieldNameMaps =
|
| new HashMap<Descriptor, Map<String, FieldDescriptor>>();
|
| -
|
| - private Map<String, FieldDescriptor> getFieldNameMap(
|
| - Descriptor descriptor) {
|
| +
|
| + private Map<String, FieldDescriptor> getFieldNameMap(Descriptor descriptor) {
|
| if (!fieldNameMaps.containsKey(descriptor)) {
|
| - Map<String, FieldDescriptor> fieldNameMap =
|
| - new HashMap<String, FieldDescriptor>();
|
| + Map<String, FieldDescriptor> fieldNameMap = new HashMap<String, FieldDescriptor>();
|
| for (FieldDescriptor field : descriptor.getFields()) {
|
| fieldNameMap.put(field.getName(), field);
|
| fieldNameMap.put(field.getJsonName(), field);
|
| @@ -1116,64 +1213,67 @@ public class JsonFormat {
|
| }
|
| return fieldNameMaps.get(descriptor);
|
| }
|
| -
|
| - private void mergeMessage(JsonElement json, Message.Builder builder,
|
| - boolean skipTypeUrl) throws InvalidProtocolBufferException {
|
| +
|
| + private void mergeMessage(JsonElement json, Message.Builder builder, boolean skipTypeUrl)
|
| + throws InvalidProtocolBufferException {
|
| if (!(json instanceof JsonObject)) {
|
| - throw new InvalidProtocolBufferException(
|
| - "Expect message object but got: " + json);
|
| + throw new InvalidProtocolBufferException("Expect message object but got: " + json);
|
| }
|
| JsonObject object = (JsonObject) json;
|
| - Map<String, FieldDescriptor> fieldNameMap =
|
| - getFieldNameMap(builder.getDescriptorForType());
|
| + Map<String, FieldDescriptor> fieldNameMap = getFieldNameMap(builder.getDescriptorForType());
|
| for (Map.Entry<String, JsonElement> entry : object.entrySet()) {
|
| if (skipTypeUrl && entry.getKey().equals("@type")) {
|
| continue;
|
| }
|
| FieldDescriptor field = fieldNameMap.get(entry.getKey());
|
| if (field == null) {
|
| + if (ignoringUnknownFields) {
|
| + continue;
|
| + }
|
| throw new InvalidProtocolBufferException(
|
| - "Cannot find field: " + entry.getKey() + " in message "
|
| - + builder.getDescriptorForType().getFullName());
|
| + "Cannot find field: "
|
| + + entry.getKey()
|
| + + " in message "
|
| + + builder.getDescriptorForType().getFullName());
|
| }
|
| mergeField(field, entry.getValue(), builder);
|
| }
|
| }
|
| -
|
| +
|
| private void mergeAny(JsonElement json, Message.Builder builder)
|
| throws InvalidProtocolBufferException {
|
| Descriptor descriptor = builder.getDescriptorForType();
|
| FieldDescriptor typeUrlField = descriptor.findFieldByName("type_url");
|
| FieldDescriptor valueField = descriptor.findFieldByName("value");
|
| // Validates type of the message. Note that we can't just cast the message
|
| - // to com.google.protobuf.Any because it might be a DynamicMessage.
|
| - if (typeUrlField == null || valueField == null
|
| + // to com.google.protobuf.Any because it might be a DynamicMessage.
|
| + if (typeUrlField == null
|
| + || valueField == null
|
| || typeUrlField.getType() != FieldDescriptor.Type.STRING
|
| || valueField.getType() != FieldDescriptor.Type.BYTES) {
|
| throw new InvalidProtocolBufferException("Invalid Any type.");
|
| }
|
| -
|
| +
|
| if (!(json instanceof JsonObject)) {
|
| - throw new InvalidProtocolBufferException(
|
| - "Expect message object but got: " + json);
|
| + throw new InvalidProtocolBufferException("Expect message object but got: " + json);
|
| }
|
| JsonObject object = (JsonObject) json;
|
| + if (object.entrySet().isEmpty()) {
|
| + return; // builder never modified, so it will end up building the default instance of Any
|
| + }
|
| JsonElement typeUrlElement = object.get("@type");
|
| if (typeUrlElement == null) {
|
| - throw new InvalidProtocolBufferException(
|
| - "Missing type url when parsing: " + json);
|
| + throw new InvalidProtocolBufferException("Missing type url when parsing: " + json);
|
| }
|
| String typeUrl = typeUrlElement.getAsString();
|
| Descriptor contentType = registry.find(getTypeName(typeUrl));
|
| if (contentType == null) {
|
| - throw new InvalidProtocolBufferException(
|
| - "Cannot resolve type: " + typeUrl);
|
| + throw new InvalidProtocolBufferException("Cannot resolve type: " + typeUrl);
|
| }
|
| builder.setField(typeUrlField, typeUrl);
|
| Message.Builder contentBuilder =
|
| DynamicMessage.getDefaultInstance(contentType).newBuilderForType();
|
| - WellKnownTypeParser specialParser =
|
| - wellKnownTypeParsers.get(contentType.getFullName());
|
| + WellKnownTypeParser specialParser = wellKnownTypeParsers.get(contentType.getFullName());
|
| if (specialParser != null) {
|
| JsonElement value = object.get("value");
|
| if (value != null) {
|
| @@ -1184,35 +1284,33 @@ public class JsonFormat {
|
| }
|
| builder.setField(valueField, contentBuilder.build().toByteString());
|
| }
|
| -
|
| +
|
| private void mergeFieldMask(JsonElement json, Message.Builder builder)
|
| throws InvalidProtocolBufferException {
|
| - FieldMask value = FieldMaskUtil.fromString(json.getAsString());
|
| + FieldMask value = FieldMaskUtil.fromJsonString(json.getAsString());
|
| builder.mergeFrom(value.toByteString());
|
| }
|
| -
|
| +
|
| private void mergeTimestamp(JsonElement json, Message.Builder builder)
|
| throws InvalidProtocolBufferException {
|
| try {
|
| - Timestamp value = TimeUtil.parseTimestamp(json.getAsString());
|
| + Timestamp value = Timestamps.parse(json.getAsString());
|
| builder.mergeFrom(value.toByteString());
|
| } catch (ParseException e) {
|
| - throw new InvalidProtocolBufferException(
|
| - "Failed to parse timestamp: " + json);
|
| + throw new InvalidProtocolBufferException("Failed to parse timestamp: " + json);
|
| }
|
| }
|
| -
|
| +
|
| private void mergeDuration(JsonElement json, Message.Builder builder)
|
| throws InvalidProtocolBufferException {
|
| try {
|
| - Duration value = TimeUtil.parseDuration(json.getAsString());
|
| + Duration value = Durations.parse(json.getAsString());
|
| builder.mergeFrom(value.toByteString());
|
| } catch (ParseException e) {
|
| - throw new InvalidProtocolBufferException(
|
| - "Failed to parse duration: " + json);
|
| + throw new InvalidProtocolBufferException("Failed to parse duration: " + json);
|
| }
|
| }
|
| -
|
| +
|
| private void mergeStruct(JsonElement json, Message.Builder builder)
|
| throws InvalidProtocolBufferException {
|
| Descriptor descriptor = builder.getDescriptorForType();
|
| @@ -1232,21 +1330,18 @@ public class JsonFormat {
|
| }
|
| mergeRepeatedField(field, json, builder);
|
| }
|
| -
|
| +
|
| private void mergeValue(JsonElement json, Message.Builder builder)
|
| throws InvalidProtocolBufferException {
|
| Descriptor type = builder.getDescriptorForType();
|
| if (json instanceof JsonPrimitive) {
|
| JsonPrimitive primitive = (JsonPrimitive) json;
|
| if (primitive.isBoolean()) {
|
| - builder.setField(type.findFieldByName("bool_value"),
|
| - primitive.getAsBoolean());
|
| + builder.setField(type.findFieldByName("bool_value"), primitive.getAsBoolean());
|
| } else if (primitive.isNumber()) {
|
| - builder.setField(type.findFieldByName("number_value"),
|
| - primitive.getAsDouble());
|
| + builder.setField(type.findFieldByName("number_value"), primitive.getAsDouble());
|
| } else {
|
| - builder.setField(type.findFieldByName("string_value"),
|
| - primitive.getAsString());
|
| + builder.setField(type.findFieldByName("string_value"), primitive.getAsString());
|
| }
|
| } else if (json instanceof JsonObject) {
|
| FieldDescriptor field = type.findFieldByName("struct_value");
|
| @@ -1258,24 +1353,26 @@ public class JsonFormat {
|
| Message.Builder listBuilder = builder.newBuilderForField(field);
|
| merge(json, listBuilder);
|
| builder.setField(field, listBuilder.build());
|
| + } else if (json instanceof JsonNull) {
|
| + builder.setField(
|
| + type.findFieldByName("null_value"), NullValue.NULL_VALUE.getValueDescriptor());
|
| } else {
|
| throw new IllegalStateException("Unexpected json data: " + json);
|
| }
|
| }
|
| -
|
| +
|
| private void mergeWrapper(JsonElement json, Message.Builder builder)
|
| throws InvalidProtocolBufferException {
|
| Descriptor type = builder.getDescriptorForType();
|
| FieldDescriptor field = type.findFieldByName("value");
|
| if (field == null) {
|
| - throw new InvalidProtocolBufferException(
|
| - "Invalid wrapper type: " + type.getFullName());
|
| + throw new InvalidProtocolBufferException("Invalid wrapper type: " + type.getFullName());
|
| }
|
| builder.setField(field, parseFieldValue(field, json, builder));
|
| }
|
| -
|
| - private void mergeField(FieldDescriptor field, JsonElement json,
|
| - Message.Builder builder) throws InvalidProtocolBufferException {
|
| +
|
| + private void mergeField(FieldDescriptor field, JsonElement json, Message.Builder builder)
|
| + throws InvalidProtocolBufferException {
|
| if (field.isRepeated()) {
|
| if (builder.getRepeatedFieldCount(field) > 0) {
|
| throw new InvalidProtocolBufferException(
|
| @@ -1290,8 +1387,11 @@ public class JsonFormat {
|
| && builder.getOneofFieldDescriptor(field.getContainingOneof()) != null) {
|
| FieldDescriptor other = builder.getOneofFieldDescriptor(field.getContainingOneof());
|
| throw new InvalidProtocolBufferException(
|
| - "Cannot set field " + field.getFullName() + " because another field "
|
| - + other.getFullName() + " belonging to the same oneof has already been set ");
|
| + "Cannot set field "
|
| + + field.getFullName()
|
| + + " because another field "
|
| + + other.getFullName()
|
| + + " belonging to the same oneof has already been set ");
|
| }
|
| }
|
| if (field.isRepeated() && json instanceof JsonNull) {
|
| @@ -1310,44 +1410,38 @@ public class JsonFormat {
|
| }
|
| }
|
| }
|
| -
|
| - private void mergeMapField(FieldDescriptor field, JsonElement json,
|
| - Message.Builder builder) throws InvalidProtocolBufferException {
|
| +
|
| + private void mergeMapField(FieldDescriptor field, JsonElement json, Message.Builder builder)
|
| + throws InvalidProtocolBufferException {
|
| if (!(json instanceof JsonObject)) {
|
| - throw new InvalidProtocolBufferException(
|
| - "Expect a map object but found: " + json);
|
| + throw new InvalidProtocolBufferException("Expect a map object but found: " + json);
|
| }
|
| Descriptor type = field.getMessageType();
|
| FieldDescriptor keyField = type.findFieldByName("key");
|
| FieldDescriptor valueField = type.findFieldByName("value");
|
| if (keyField == null || valueField == null) {
|
| - throw new InvalidProtocolBufferException(
|
| - "Invalid map field: " + field.getFullName());
|
| + throw new InvalidProtocolBufferException("Invalid map field: " + field.getFullName());
|
| }
|
| JsonObject object = (JsonObject) json;
|
| for (Map.Entry<String, JsonElement> entry : object.entrySet()) {
|
| Message.Builder entryBuilder = builder.newBuilderForField(field);
|
| - Object key = parseFieldValue(
|
| - keyField, new JsonPrimitive(entry.getKey()), entryBuilder);
|
| - Object value = parseFieldValue(
|
| - valueField, entry.getValue(), entryBuilder);
|
| + Object key = parseFieldValue(keyField, new JsonPrimitive(entry.getKey()), entryBuilder);
|
| + Object value = parseFieldValue(valueField, entry.getValue(), entryBuilder);
|
| if (value == null) {
|
| - throw new InvalidProtocolBufferException(
|
| - "Map value cannot be null.");
|
| + throw new InvalidProtocolBufferException("Map value cannot be null.");
|
| }
|
| entryBuilder.setField(keyField, key);
|
| entryBuilder.setField(valueField, value);
|
| builder.addRepeatedField(field, entryBuilder.build());
|
| }
|
| }
|
| -
|
| +
|
| /**
|
| * Gets the default value for a field type. Note that we use proto3
|
| * language defaults and ignore any default values set through the
|
| - * proto "default" option.
|
| + * proto "default" option.
|
| */
|
| - private Object getDefaultValue(FieldDescriptor field,
|
| - Message.Builder builder) {
|
| + private Object getDefaultValue(FieldDescriptor field, Message.Builder builder) {
|
| switch (field.getType()) {
|
| case INT32:
|
| case SINT32:
|
| @@ -1377,30 +1471,27 @@ public class JsonFormat {
|
| case GROUP:
|
| return builder.newBuilderForField(field).getDefaultInstanceForType();
|
| default:
|
| - throw new IllegalStateException(
|
| - "Invalid field type: " + field.getType());
|
| + throw new IllegalStateException("Invalid field type: " + field.getType());
|
| }
|
| }
|
| -
|
| - private void mergeRepeatedField(FieldDescriptor field, JsonElement json,
|
| - Message.Builder builder) throws InvalidProtocolBufferException {
|
| +
|
| + private void mergeRepeatedField(
|
| + FieldDescriptor field, JsonElement json, Message.Builder builder)
|
| + throws InvalidProtocolBufferException {
|
| if (!(json instanceof JsonArray)) {
|
| - throw new InvalidProtocolBufferException(
|
| - "Expect an array but found: " + json);
|
| + throw new InvalidProtocolBufferException("Expect an array but found: " + json);
|
| }
|
| JsonArray array = (JsonArray) json;
|
| for (int i = 0; i < array.size(); ++i) {
|
| Object value = parseFieldValue(field, array.get(i), builder);
|
| if (value == null) {
|
| - throw new InvalidProtocolBufferException(
|
| - "Repeated field elements cannot be null");
|
| + throw new InvalidProtocolBufferException("Repeated field elements cannot be null");
|
| }
|
| builder.addRepeatedField(field, value);
|
| }
|
| }
|
| -
|
| - private int parseInt32(JsonElement json)
|
| - throws InvalidProtocolBufferException {
|
| +
|
| + private int parseInt32(JsonElement json) throws InvalidProtocolBufferException {
|
| try {
|
| return Integer.parseInt(json.getAsString());
|
| } catch (Exception e) {
|
| @@ -1416,9 +1507,8 @@ public class JsonFormat {
|
| throw new InvalidProtocolBufferException("Not an int32 value: " + json);
|
| }
|
| }
|
| -
|
| - private long parseInt64(JsonElement json)
|
| - throws InvalidProtocolBufferException {
|
| +
|
| + private long parseInt64(JsonElement json) throws InvalidProtocolBufferException {
|
| try {
|
| return Long.parseLong(json.getAsString());
|
| } catch (Exception e) {
|
| @@ -1434,14 +1524,12 @@ public class JsonFormat {
|
| throw new InvalidProtocolBufferException("Not an int32 value: " + json);
|
| }
|
| }
|
| -
|
| - private int parseUint32(JsonElement json)
|
| - throws InvalidProtocolBufferException {
|
| +
|
| + private int parseUint32(JsonElement json) throws InvalidProtocolBufferException {
|
| try {
|
| long result = Long.parseLong(json.getAsString());
|
| if (result < 0 || result > 0xFFFFFFFFL) {
|
| - throw new InvalidProtocolBufferException(
|
| - "Out of range uint32 value: " + json);
|
| + throw new InvalidProtocolBufferException("Out of range uint32 value: " + json);
|
| }
|
| return (int) result;
|
| } catch (InvalidProtocolBufferException e) {
|
| @@ -1462,35 +1550,28 @@ public class JsonFormat {
|
| } catch (InvalidProtocolBufferException e) {
|
| throw e;
|
| } catch (Exception e) {
|
| - throw new InvalidProtocolBufferException(
|
| - "Not an uint32 value: " + json);
|
| + throw new InvalidProtocolBufferException("Not an uint32 value: " + json);
|
| }
|
| }
|
| -
|
| - private static final BigInteger MAX_UINT64 =
|
| - new BigInteger("FFFFFFFFFFFFFFFF", 16);
|
| -
|
| - private long parseUint64(JsonElement json)
|
| - throws InvalidProtocolBufferException {
|
| +
|
| + private static final BigInteger MAX_UINT64 = new BigInteger("FFFFFFFFFFFFFFFF", 16);
|
| +
|
| + private long parseUint64(JsonElement json) throws InvalidProtocolBufferException {
|
| try {
|
| BigDecimal decimalValue = new BigDecimal(json.getAsString());
|
| BigInteger value = decimalValue.toBigIntegerExact();
|
| - if (value.compareTo(BigInteger.ZERO) < 0
|
| - || value.compareTo(MAX_UINT64) > 0) {
|
| - throw new InvalidProtocolBufferException(
|
| - "Out of range uint64 value: " + json);
|
| + if (value.compareTo(BigInteger.ZERO) < 0 || value.compareTo(MAX_UINT64) > 0) {
|
| + throw new InvalidProtocolBufferException("Out of range uint64 value: " + json);
|
| }
|
| return value.longValue();
|
| } catch (InvalidProtocolBufferException e) {
|
| throw e;
|
| } catch (Exception e) {
|
| - throw new InvalidProtocolBufferException(
|
| - "Not an uint64 value: " + json);
|
| + throw new InvalidProtocolBufferException("Not an uint64 value: " + json);
|
| }
|
| }
|
| -
|
| - private boolean parseBool(JsonElement json)
|
| - throws InvalidProtocolBufferException {
|
| +
|
| + private boolean parseBool(JsonElement json) throws InvalidProtocolBufferException {
|
| if (json.getAsString().equals("true")) {
|
| return true;
|
| }
|
| @@ -1499,11 +1580,10 @@ public class JsonFormat {
|
| }
|
| throw new InvalidProtocolBufferException("Invalid bool value: " + json);
|
| }
|
| -
|
| +
|
| private static final double EPSILON = 1e-6;
|
| -
|
| - private float parseFloat(JsonElement json)
|
| - throws InvalidProtocolBufferException {
|
| +
|
| + private float parseFloat(JsonElement json) throws InvalidProtocolBufferException {
|
| if (json.getAsString().equals("NaN")) {
|
| return Float.NaN;
|
| } else if (json.getAsString().equals("Infinity")) {
|
| @@ -1521,8 +1601,7 @@ public class JsonFormat {
|
| // of tolerance when checking whether the float value is in range.
|
| if (value > Float.MAX_VALUE * (1.0 + EPSILON)
|
| || value < -Float.MAX_VALUE * (1.0 + EPSILON)) {
|
| - throw new InvalidProtocolBufferException(
|
| - "Out of range float value: " + json);
|
| + throw new InvalidProtocolBufferException("Out of range float value: " + json);
|
| }
|
| return (float) value;
|
| } catch (InvalidProtocolBufferException e) {
|
| @@ -1531,19 +1610,17 @@ public class JsonFormat {
|
| throw new InvalidProtocolBufferException("Not a float value: " + json);
|
| }
|
| }
|
| -
|
| - private static final BigDecimal MORE_THAN_ONE = new BigDecimal(
|
| - String.valueOf(1.0 + EPSILON));
|
| +
|
| + private static final BigDecimal MORE_THAN_ONE = new BigDecimal(String.valueOf(1.0 + EPSILON));
|
| // When a float value is printed, the printed value might be a little
|
| // larger or smaller due to precision loss. Here we need to add a bit
|
| // of tolerance when checking whether the float value is in range.
|
| - private static final BigDecimal MAX_DOUBLE = new BigDecimal(
|
| - String.valueOf(Double.MAX_VALUE)).multiply(MORE_THAN_ONE);
|
| - private static final BigDecimal MIN_DOUBLE = new BigDecimal(
|
| - String.valueOf(-Double.MAX_VALUE)).multiply(MORE_THAN_ONE);
|
| -
|
| - private double parseDouble(JsonElement json)
|
| - throws InvalidProtocolBufferException {
|
| + private static final BigDecimal MAX_DOUBLE =
|
| + new BigDecimal(String.valueOf(Double.MAX_VALUE)).multiply(MORE_THAN_ONE);
|
| + private static final BigDecimal MIN_DOUBLE =
|
| + new BigDecimal(String.valueOf(-Double.MAX_VALUE)).multiply(MORE_THAN_ONE);
|
| +
|
| + private double parseDouble(JsonElement json) throws InvalidProtocolBufferException {
|
| if (json.getAsString().equals("NaN")) {
|
| return Double.NaN;
|
| } else if (json.getAsString().equals("Infinity")) {
|
| @@ -1556,36 +1633,27 @@ public class JsonFormat {
|
| // accepts all values. Here we parse the value into a BigDecimal and do
|
| // explicit range check on it.
|
| BigDecimal value = new BigDecimal(json.getAsString());
|
| - if (value.compareTo(MAX_DOUBLE) > 0
|
| - || value.compareTo(MIN_DOUBLE) < 0) {
|
| - throw new InvalidProtocolBufferException(
|
| - "Out of range double value: " + json);
|
| + if (value.compareTo(MAX_DOUBLE) > 0 || value.compareTo(MIN_DOUBLE) < 0) {
|
| + throw new InvalidProtocolBufferException("Out of range double value: " + json);
|
| }
|
| return value.doubleValue();
|
| } catch (InvalidProtocolBufferException e) {
|
| throw e;
|
| } catch (Exception e) {
|
| - throw new InvalidProtocolBufferException(
|
| - "Not an double value: " + json);
|
| + throw new InvalidProtocolBufferException("Not an double value: " + json);
|
| }
|
| }
|
| -
|
| +
|
| private String parseString(JsonElement json) {
|
| return json.getAsString();
|
| }
|
| -
|
| +
|
| private ByteString parseBytes(JsonElement json) throws InvalidProtocolBufferException {
|
| - String encoded = json.getAsString();
|
| - if (encoded.length() % 4 != 0) {
|
| - throw new InvalidProtocolBufferException(
|
| - "Bytes field is not encoded in standard BASE64 with paddings: " + encoded);
|
| - }
|
| - return ByteString.copyFrom(
|
| - BaseEncoding.base64().decode(json.getAsString()));
|
| + return ByteString.copyFrom(BaseEncoding.base64().decode(json.getAsString()));
|
| }
|
| -
|
| - private EnumValueDescriptor parseEnum(EnumDescriptor enumDescriptor,
|
| - JsonElement json) throws InvalidProtocolBufferException {
|
| +
|
| + private EnumValueDescriptor parseEnum(EnumDescriptor enumDescriptor, JsonElement json)
|
| + throws InvalidProtocolBufferException {
|
| String value = json.getAsString();
|
| EnumValueDescriptor result = enumDescriptor.findValueByName(value);
|
| if (result == null) {
|
| @@ -1602,27 +1670,28 @@ public class JsonFormat {
|
| // that's not the exception we want the user to see. Since result == null, we will throw
|
| // an exception later.
|
| }
|
| -
|
| +
|
| if (result == null) {
|
| throw new InvalidProtocolBufferException(
|
| - "Invalid enum value: " + value + " for enum type: "
|
| - + enumDescriptor.getFullName());
|
| + "Invalid enum value: " + value + " for enum type: " + enumDescriptor.getFullName());
|
| }
|
| }
|
| return result;
|
| }
|
| -
|
| - private Object parseFieldValue(FieldDescriptor field, JsonElement json,
|
| - Message.Builder builder) throws InvalidProtocolBufferException {
|
| +
|
| + private Object parseFieldValue(FieldDescriptor field, JsonElement json, Message.Builder builder)
|
| + throws InvalidProtocolBufferException {
|
| if (json instanceof JsonNull) {
|
| if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE
|
| - && field.getMessageType().getFullName().equals(
|
| - Value.getDescriptor().getFullName())) {
|
| + && field.getMessageType().getFullName().equals(Value.getDescriptor().getFullName())) {
|
| // For every other type, "null" means absence, but for the special
|
| // Value message, it means the "null_value" field has been set.
|
| Value value = Value.newBuilder().setNullValueValue(0).build();
|
| - return builder.newBuilderForField(field).mergeFrom(
|
| - value.toByteString()).build();
|
| + return builder.newBuilderForField(field).mergeFrom(value.toByteString()).build();
|
| + } else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM
|
| + && field.getEnumType().getFullName().equals(NullValue.getDescriptor().getFullName())) {
|
| + // If the type of the field is a NullValue, then the value should be explicitly set.
|
| + return field.getEnumType().findValueByNumber(0);
|
| }
|
| return null;
|
| }
|
| @@ -1642,7 +1711,7 @@ public class JsonFormat {
|
|
|
| case FLOAT:
|
| return parseFloat(json);
|
| -
|
| +
|
| case DOUBLE:
|
| return parseDouble(json);
|
|
|
| @@ -1665,14 +1734,18 @@ public class JsonFormat {
|
|
|
| case MESSAGE:
|
| case GROUP:
|
| + if (currentDepth >= recursionLimit) {
|
| + throw new InvalidProtocolBufferException("Hit recursion limit.");
|
| + }
|
| + ++currentDepth;
|
| Message.Builder subBuilder = builder.newBuilderForField(field);
|
| merge(json, subBuilder);
|
| + --currentDepth;
|
| return subBuilder.build();
|
| -
|
| +
|
| default:
|
| - throw new InvalidProtocolBufferException(
|
| - "Invalid field type: " + field.getType());
|
| - }
|
| + throw new InvalidProtocolBufferException("Invalid field type: " + field.getType());
|
| + }
|
| }
|
| }
|
| }
|
|
|