1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 package com.google.protobuf.util;
32 
33 import com.google.common.io.BaseEncoding;
34 import com.google.gson.Gson;
35 import com.google.gson.GsonBuilder;
36 import com.google.gson.JsonArray;
37 import com.google.gson.JsonElement;
38 import com.google.gson.JsonNull;
39 import com.google.gson.JsonObject;
40 import com.google.gson.JsonParser;
41 import com.google.gson.JsonPrimitive;
42 import com.google.gson.stream.JsonReader;
43 import com.google.protobuf.Any;
44 import com.google.protobuf.BoolValue;
45 import com.google.protobuf.ByteString;
46 import com.google.protobuf.BytesValue;
47 import com.google.protobuf.Descriptors.Descriptor;
48 import com.google.protobuf.Descriptors.EnumDescriptor;
49 import com.google.protobuf.Descriptors.EnumValueDescriptor;
50 import com.google.protobuf.Descriptors.FieldDescriptor;
51 import com.google.protobuf.Descriptors.FileDescriptor;
52 import com.google.protobuf.DoubleValue;
53 import com.google.protobuf.Duration;
54 import com.google.protobuf.DynamicMessage;
55 import com.google.protobuf.FieldMask;
56 import com.google.protobuf.FloatValue;
57 import com.google.protobuf.Int32Value;
58 import com.google.protobuf.Int64Value;
59 import com.google.protobuf.InvalidProtocolBufferException;
60 import com.google.protobuf.ListValue;
61 import com.google.protobuf.Message;
62 import com.google.protobuf.MessageOrBuilder;
63 import com.google.protobuf.NullValue;
64 import com.google.protobuf.StringValue;
65 import com.google.protobuf.Struct;
66 import com.google.protobuf.Timestamp;
67 import com.google.protobuf.UInt32Value;
68 import com.google.protobuf.UInt64Value;
69 import com.google.protobuf.Value;
70 
71 import java.io.IOException;
72 import java.io.Reader;
73 import java.io.StringReader;
74 import java.math.BigDecimal;
75 import java.math.BigInteger;
76 import java.text.ParseException;
77 import java.util.Collections;
78 import java.util.HashMap;
79 import java.util.HashSet;
80 import java.util.List;
81 import java.util.Map;
82 import java.util.Set;
83 import java.util.TreeMap;
84 import java.util.logging.Logger;
85 
86 /**
87  * Utility classes to convert protobuf messages to/from JSON format. The JSON
88  * format follows Proto3 JSON specification and only proto3 features are
89  * supported. Proto2 only features (e.g., extensions and unknown fields) will
90  * be discarded in the conversion. That is, when converting proto2 messages
91  * to JSON format, extensions and unknown fields will be treated as if they
92  * do not exist. This applies to proto2 messages embedded in proto3 messages
93  * as well.
94  */
95 public class JsonFormat {
96   private static final Logger logger = Logger.getLogger(JsonFormat.class.getName());
97 
JsonFormat()98   private JsonFormat() {}
99 
100   /**
101    * Creates a {@link Printer} with default configurations.
102    */
printer()103   public static Printer printer() {
104     return new Printer(TypeRegistry.getEmptyTypeRegistry(), false, false, false);
105   }
106 
107   /**
108    * A Printer converts protobuf message to JSON format.
109    */
110   public static class Printer {
111     private final TypeRegistry registry;
112     private final boolean includingDefaultValueFields;
113     private final boolean preservingProtoFieldNames;
114     private final boolean omittingInsignificantWhitespace;
115 
Printer( TypeRegistry registry, boolean includingDefaultValueFields, boolean preservingProtoFieldNames, boolean omittingInsignificantWhitespace)116     private Printer(
117         TypeRegistry registry,
118         boolean includingDefaultValueFields,
119         boolean preservingProtoFieldNames,
120         boolean omittingInsignificantWhitespace) {
121       this.registry = registry;
122       this.includingDefaultValueFields = includingDefaultValueFields;
123       this.preservingProtoFieldNames = preservingProtoFieldNames;
124       this.omittingInsignificantWhitespace = omittingInsignificantWhitespace;
125     }
126 
127     /**
128      * Creates a new {@link Printer} using the given registry. The new Printer
129      * clones all other configurations from the current {@link Printer}.
130      *
131      * @throws IllegalArgumentException if a registry is already set.
132      */
usingTypeRegistry(TypeRegistry registry)133     public Printer usingTypeRegistry(TypeRegistry registry) {
134       if (this.registry != TypeRegistry.getEmptyTypeRegistry()) {
135         throw new IllegalArgumentException("Only one registry is allowed.");
136       }
137       return new Printer(
138           registry,
139           includingDefaultValueFields,
140           preservingProtoFieldNames,
141           omittingInsignificantWhitespace);
142     }
143 
144     /**
145      * Creates a new {@link Printer} that will also print fields set to their
146      * defaults. Empty repeated fields and map fields will be printed as well.
147      * The new Printer clones all other configurations from the current
148      * {@link Printer}.
149      */
includingDefaultValueFields()150     public Printer includingDefaultValueFields() {
151       return new Printer(
152           registry, true, preservingProtoFieldNames, omittingInsignificantWhitespace);
153     }
154 
155     /**
156      * Creates a new {@link Printer} that is configured to use the original proto
157      * field names as defined in the .proto file rather than converting them to
158      * lowerCamelCase. The new Printer clones all other configurations from the
159      * current {@link Printer}.
160      */
preservingProtoFieldNames()161     public Printer preservingProtoFieldNames() {
162       return new Printer(
163           registry, includingDefaultValueFields, true, omittingInsignificantWhitespace);
164     }
165 
166 
167     /**
168      * Create a new  {@link Printer}  that will omit all insignificant whitespace
169      * in the JSON output. This new Printer clones all other configurations from the
170      * current Printer. Insignificant whitespace is defined by the JSON spec as whitespace
171      * that appear between JSON structural elements:
172      * <pre>
173      * ws = *(
174      * %x20 /              ; Space
175      * %x09 /              ; Horizontal tab
176      * %x0A /              ; Line feed or New line
177      * %x0D )              ; Carriage return
178      * </pre>
179      * See <a href="https://tools.ietf.org/html/rfc7159">https://tools.ietf.org/html/rfc7159</a>
180      * current {@link Printer}.
181      */
omittingInsignificantWhitespace()182     public Printer omittingInsignificantWhitespace() {
183       return new Printer(registry, includingDefaultValueFields, preservingProtoFieldNames, true);
184     }
185 
186     /**
187      * Converts a protobuf message to JSON format.
188      *
189      * @throws InvalidProtocolBufferException if the message contains Any types
190      *         that can't be resolved.
191      * @throws IOException if writing to the output fails.
192      */
appendTo(MessageOrBuilder message, Appendable output)193     public void appendTo(MessageOrBuilder message, Appendable output) throws IOException {
194       // TODO(xiaofeng): Investigate the allocation overhead and optimize for
195       // mobile.
196       new PrinterImpl(
197               registry,
198               includingDefaultValueFields,
199               preservingProtoFieldNames,
200               output,
201               omittingInsignificantWhitespace)
202           .print(message);
203     }
204 
205     /**
206      * Converts a protobuf message to JSON format. Throws exceptions if there
207      * are unknown Any types in the message.
208      */
print(MessageOrBuilder message)209     public String print(MessageOrBuilder message) throws InvalidProtocolBufferException {
210       try {
211         StringBuilder builder = new StringBuilder();
212         appendTo(message, builder);
213         return builder.toString();
214       } catch (InvalidProtocolBufferException e) {
215         throw e;
216       } catch (IOException e) {
217         // Unexpected IOException.
218         throw new IllegalStateException(e);
219       }
220     }
221   }
222 
223   /**
224    * Creates a {@link Parser} with default configuration.
225    */
parser()226   public static Parser parser() {
227     return new Parser(TypeRegistry.getEmptyTypeRegistry());
228   }
229 
230   /**
231    * A Parser parses JSON to protobuf message.
232    */
233   public static class Parser {
234     private final TypeRegistry registry;
235 
Parser(TypeRegistry registry)236     private Parser(TypeRegistry registry) {
237       this.registry = registry;
238     }
239 
240     /**
241      * Creates a new {@link Parser} using the given registry. The new Parser
242      * clones all other configurations from this Parser.
243      *
244      * @throws IllegalArgumentException if a registry is already set.
245      */
usingTypeRegistry(TypeRegistry registry)246     public Parser usingTypeRegistry(TypeRegistry registry) {
247       if (this.registry != TypeRegistry.getEmptyTypeRegistry()) {
248         throw new IllegalArgumentException("Only one registry is allowed.");
249       }
250       return new Parser(registry);
251     }
252 
253     /**
254      * Parses from JSON into a protobuf message.
255      *
256      * @throws InvalidProtocolBufferException if the input is not valid JSON
257      *         format or there are unknown fields in the input.
258      */
merge(String json, Message.Builder builder)259     public void merge(String json, Message.Builder builder) throws InvalidProtocolBufferException {
260       // TODO(xiaofeng): Investigate the allocation overhead and optimize for
261       // mobile.
262       new ParserImpl(registry).merge(json, builder);
263     }
264 
265     /**
266      * Parses from JSON into a protobuf message.
267      *
268      * @throws InvalidProtocolBufferException if the input is not valid JSON
269      *         format or there are unknown fields in the input.
270      * @throws IOException if reading from the input throws.
271      */
merge(Reader json, Message.Builder builder)272     public void merge(Reader json, Message.Builder builder) throws IOException {
273       // TODO(xiaofeng): Investigate the allocation overhead and optimize for
274       // mobile.
275       new ParserImpl(registry).merge(json, builder);
276     }
277   }
278 
279   /**
280    * A TypeRegistry is used to resolve Any messages in the JSON conversion.
281    * You must provide a TypeRegistry containing all message types used in
282    * Any message fields, or the JSON conversion will fail because data
283    * in Any message fields is unrecognizable. You don't need to supply a
284    * TypeRegistry if you don't use Any message fields.
285    */
286   public static class TypeRegistry {
287     private static class EmptyTypeRegistryHolder {
288       private static final TypeRegistry EMPTY =
289           new TypeRegistry(Collections.<String, Descriptor>emptyMap());
290     }
291 
getEmptyTypeRegistry()292     public static TypeRegistry getEmptyTypeRegistry() {
293       return EmptyTypeRegistryHolder.EMPTY;
294     }
295 
newBuilder()296     public static Builder newBuilder() {
297       return new Builder();
298     }
299 
300     /**
301      * Find a type by its full name. Returns null if it cannot be found in
302      * this {@link TypeRegistry}.
303      */
find(String name)304     public Descriptor find(String name) {
305       return types.get(name);
306     }
307 
308     private final Map<String, Descriptor> types;
309 
TypeRegistry(Map<String, Descriptor> types)310     private TypeRegistry(Map<String, Descriptor> types) {
311       this.types = types;
312     }
313 
314     /**
315      * A Builder is used to build {@link TypeRegistry}.
316      */
317     public static class Builder {
Builder()318       private Builder() {}
319 
320       /**
321        * Adds a message type and all types defined in the same .proto file as
322        * well as all transitively imported .proto files to this {@link Builder}.
323        */
add(Descriptor messageType)324       public Builder add(Descriptor messageType) {
325         if (types == null) {
326           throw new IllegalStateException("A TypeRegistry.Builer can only be used once.");
327         }
328         addFile(messageType.getFile());
329         return this;
330       }
331 
332       /**
333        * Adds message types and all types defined in the same .proto file as
334        * well as all transitively imported .proto files to this {@link Builder}.
335        */
add(Iterable<Descriptor> messageTypes)336       public Builder add(Iterable<Descriptor> messageTypes) {
337         if (types == null) {
338           throw new IllegalStateException("A TypeRegistry.Builer can only be used once.");
339         }
340         for (Descriptor type : messageTypes) {
341           addFile(type.getFile());
342         }
343         return this;
344       }
345 
346       /**
347        * Builds a {@link TypeRegistry}. This method can only be called once for
348        * one Builder.
349        */
build()350       public TypeRegistry build() {
351         TypeRegistry result = new TypeRegistry(types);
352         // Make sure the built {@link TypeRegistry} is immutable.
353         types = null;
354         return result;
355       }
356 
addFile(FileDescriptor file)357       private void addFile(FileDescriptor file) {
358         // Skip the file if it's already added.
359         if (!files.add(file.getFullName())) {
360           return;
361         }
362         for (FileDescriptor dependency : file.getDependencies()) {
363           addFile(dependency);
364         }
365         for (Descriptor message : file.getMessageTypes()) {
366           addMessage(message);
367         }
368       }
369 
addMessage(Descriptor message)370       private void addMessage(Descriptor message) {
371         for (Descriptor nestedType : message.getNestedTypes()) {
372           addMessage(nestedType);
373         }
374 
375         if (types.containsKey(message.getFullName())) {
376           logger.warning("Type " + message.getFullName() + " is added multiple times.");
377           return;
378         }
379 
380         types.put(message.getFullName(), message);
381       }
382 
383       private final Set<String> files = new HashSet<String>();
384       private Map<String, Descriptor> types = new HashMap<String, Descriptor>();
385     }
386   }
387 
388   /**
389    * An interface for json formatting that can be used in
390    * combination with the omittingInsignificantWhitespace() method
391    */
392   interface TextGenerator {
indent()393     void indent();
394 
outdent()395     void outdent();
396 
print(final CharSequence text)397     void print(final CharSequence text) throws IOException;
398   }
399 
400   /**
401    * Format the json without indentation
402    */
403   private static final class CompactTextGenerator implements TextGenerator {
404     private final Appendable output;
405 
CompactTextGenerator(final Appendable output)406     private CompactTextGenerator(final Appendable output) {
407       this.output = output;
408     }
409 
410     /**
411      * ignored by compact printer
412      */
indent()413     public void indent() {}
414 
415     /**
416      * ignored by compact printer
417      */
outdent()418     public void outdent() {}
419 
420     /**
421      * Print text to the output stream.
422      */
print(final CharSequence text)423     public void print(final CharSequence text) throws IOException {
424       output.append(text);
425     }
426   }
427   /**
428    * A TextGenerator adds indentation when writing formatted text.
429    */
430   private static final class PrettyTextGenerator implements TextGenerator {
431     private final Appendable output;
432     private final StringBuilder indent = new StringBuilder();
433     private boolean atStartOfLine = true;
434 
PrettyTextGenerator(final Appendable output)435     private PrettyTextGenerator(final Appendable output) {
436       this.output = output;
437     }
438 
439     /**
440      * Indent text by two spaces.  After calling Indent(), two spaces will be
441      * inserted at the beginning of each line of text.  Indent() may be called
442      * multiple times to produce deeper indents.
443      */
indent()444     public void indent() {
445       indent.append("  ");
446     }
447 
448     /**
449      * Reduces the current indent level by two spaces, or crashes if the indent
450      * level is zero.
451      */
outdent()452     public void outdent() {
453       final int length = indent.length();
454       if (length < 2) {
455         throw new IllegalArgumentException(" Outdent() without matching Indent().");
456       }
457       indent.delete(length - 2, length);
458     }
459 
460     /**
461      * Print text to the output stream.
462      */
print(final CharSequence text)463     public void print(final CharSequence text) throws IOException {
464       final int size = text.length();
465       int pos = 0;
466 
467       for (int i = 0; i < size; i++) {
468         if (text.charAt(i) == '\n') {
469           write(text.subSequence(pos, i + 1));
470           pos = i + 1;
471           atStartOfLine = true;
472         }
473       }
474       write(text.subSequence(pos, size));
475     }
476 
write(final CharSequence data)477     private void write(final CharSequence data) throws IOException {
478       if (data.length() == 0) {
479         return;
480       }
481       if (atStartOfLine) {
482         atStartOfLine = false;
483         output.append(indent);
484       }
485       output.append(data);
486     }
487   }
488 
489   /**
490    * A Printer converts protobuf messages to JSON format.
491    */
492   private static final class PrinterImpl {
493     private final TypeRegistry registry;
494     private final boolean includingDefaultValueFields;
495     private final boolean preservingProtoFieldNames;
496     private final TextGenerator generator;
497     // We use Gson to help handle string escapes.
498     private final Gson gson;
499     private final CharSequence blankOrSpace;
500     private final CharSequence blankOrNewLine;
501 
502     private static class GsonHolder {
503       private static final Gson DEFAULT_GSON = new GsonBuilder().disableHtmlEscaping().create();
504     }
505 
PrinterImpl( TypeRegistry registry, boolean includingDefaultValueFields, boolean preservingProtoFieldNames, Appendable jsonOutput, boolean omittingInsignificantWhitespace)506     PrinterImpl(
507         TypeRegistry registry,
508         boolean includingDefaultValueFields,
509         boolean preservingProtoFieldNames,
510         Appendable jsonOutput,
511         boolean omittingInsignificantWhitespace) {
512       this.registry = registry;
513       this.includingDefaultValueFields = includingDefaultValueFields;
514       this.preservingProtoFieldNames = preservingProtoFieldNames;
515       this.gson = GsonHolder.DEFAULT_GSON;
516       // json format related properties, determined by printerType
517       if (omittingInsignificantWhitespace) {
518         this.generator = new CompactTextGenerator(jsonOutput);
519         this.blankOrSpace = "";
520         this.blankOrNewLine = "";
521       } else {
522         this.generator = new PrettyTextGenerator(jsonOutput);
523         this.blankOrSpace = " ";
524         this.blankOrNewLine = "\n";
525       }
526     }
527 
print(MessageOrBuilder message)528     void print(MessageOrBuilder message) throws IOException {
529       WellKnownTypePrinter specialPrinter =
530           wellKnownTypePrinters.get(message.getDescriptorForType().getFullName());
531       if (specialPrinter != null) {
532         specialPrinter.print(this, message);
533         return;
534       }
535       print(message, null);
536     }
537 
538     private interface WellKnownTypePrinter {
print(PrinterImpl printer, MessageOrBuilder message)539       void print(PrinterImpl printer, MessageOrBuilder message) throws IOException;
540     }
541 
542     private static final Map<String, WellKnownTypePrinter> wellKnownTypePrinters =
543         buildWellKnownTypePrinters();
544 
buildWellKnownTypePrinters()545     private static Map<String, WellKnownTypePrinter> buildWellKnownTypePrinters() {
546       Map<String, WellKnownTypePrinter> printers = new HashMap<String, WellKnownTypePrinter>();
547       // Special-case Any.
548       printers.put(
549           Any.getDescriptor().getFullName(),
550           new WellKnownTypePrinter() {
551             @Override
552             public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
553               printer.printAny(message);
554             }
555           });
556       // Special-case wrapper types.
557       WellKnownTypePrinter wrappersPrinter =
558           new WellKnownTypePrinter() {
559             @Override
560             public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
561               printer.printWrapper(message);
562             }
563           };
564       printers.put(BoolValue.getDescriptor().getFullName(), wrappersPrinter);
565       printers.put(Int32Value.getDescriptor().getFullName(), wrappersPrinter);
566       printers.put(UInt32Value.getDescriptor().getFullName(), wrappersPrinter);
567       printers.put(Int64Value.getDescriptor().getFullName(), wrappersPrinter);
568       printers.put(UInt64Value.getDescriptor().getFullName(), wrappersPrinter);
569       printers.put(StringValue.getDescriptor().getFullName(), wrappersPrinter);
570       printers.put(BytesValue.getDescriptor().getFullName(), wrappersPrinter);
571       printers.put(FloatValue.getDescriptor().getFullName(), wrappersPrinter);
572       printers.put(DoubleValue.getDescriptor().getFullName(), wrappersPrinter);
573       // Special-case Timestamp.
574       printers.put(
575           Timestamp.getDescriptor().getFullName(),
576           new WellKnownTypePrinter() {
577             @Override
578             public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
579               printer.printTimestamp(message);
580             }
581           });
582       // Special-case Duration.
583       printers.put(
584           Duration.getDescriptor().getFullName(),
585           new WellKnownTypePrinter() {
586             @Override
587             public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
588               printer.printDuration(message);
589             }
590           });
591       // Special-case FieldMask.
592       printers.put(
593           FieldMask.getDescriptor().getFullName(),
594           new WellKnownTypePrinter() {
595             @Override
596             public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
597               printer.printFieldMask(message);
598             }
599           });
600       // Special-case Struct.
601       printers.put(
602           Struct.getDescriptor().getFullName(),
603           new WellKnownTypePrinter() {
604             @Override
605             public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
606               printer.printStruct(message);
607             }
608           });
609       // Special-case Value.
610       printers.put(
611           Value.getDescriptor().getFullName(),
612           new WellKnownTypePrinter() {
613             @Override
614             public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
615               printer.printValue(message);
616             }
617           });
618       // Special-case ListValue.
619       printers.put(
620           ListValue.getDescriptor().getFullName(),
621           new WellKnownTypePrinter() {
622             @Override
623             public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
624               printer.printListValue(message);
625             }
626           });
627       return printers;
628     }
629 
630     /** Prints google.protobuf.Any */
printAny(MessageOrBuilder message)631     private void printAny(MessageOrBuilder message) throws IOException {
632       Descriptor descriptor = message.getDescriptorForType();
633       FieldDescriptor typeUrlField = descriptor.findFieldByName("type_url");
634       FieldDescriptor valueField = descriptor.findFieldByName("value");
635       // Validates type of the message. Note that we can't just cast the message
636       // to com.google.protobuf.Any because it might be a DynamicMessage.
637       if (typeUrlField == null
638           || valueField == null
639           || typeUrlField.getType() != FieldDescriptor.Type.STRING
640           || valueField.getType() != FieldDescriptor.Type.BYTES) {
641         throw new InvalidProtocolBufferException("Invalid Any type.");
642       }
643       String typeUrl = (String) message.getField(typeUrlField);
644       String typeName = getTypeName(typeUrl);
645       Descriptor type = registry.find(typeName);
646       if (type == null) {
647         throw new InvalidProtocolBufferException("Cannot find type for url: " + typeUrl);
648       }
649       ByteString content = (ByteString) message.getField(valueField);
650       Message contentMessage =
651           DynamicMessage.getDefaultInstance(type).getParserForType().parseFrom(content);
652       WellKnownTypePrinter printer = wellKnownTypePrinters.get(typeName);
653       if (printer != null) {
654         // If the type is one of the well-known types, we use a special
655         // formatting.
656         generator.print("{" + blankOrNewLine);
657         generator.indent();
658         generator.print("\"@type\":" + blankOrSpace + gson.toJson(typeUrl) + "," + blankOrNewLine);
659         generator.print("\"value\":" + blankOrSpace);
660         printer.print(this, contentMessage);
661         generator.print(blankOrNewLine);
662         generator.outdent();
663         generator.print("}");
664       } else {
665         // Print the content message instead (with a "@type" field added).
666         print(contentMessage, typeUrl);
667       }
668     }
669 
670     /** Prints wrapper types (e.g., google.protobuf.Int32Value) */
printWrapper(MessageOrBuilder message)671     private void printWrapper(MessageOrBuilder message) throws IOException {
672       Descriptor descriptor = message.getDescriptorForType();
673       FieldDescriptor valueField = descriptor.findFieldByName("value");
674       if (valueField == null) {
675         throw new InvalidProtocolBufferException("Invalid Wrapper type.");
676       }
677       // When formatting wrapper types, we just print its value field instead of
678       // the whole message.
679       printSingleFieldValue(valueField, message.getField(valueField));
680     }
681 
toByteString(MessageOrBuilder message)682     private ByteString toByteString(MessageOrBuilder message) {
683       if (message instanceof Message) {
684         return ((Message) message).toByteString();
685       } else {
686         return ((Message.Builder) message).build().toByteString();
687       }
688     }
689 
690     /** Prints google.protobuf.Timestamp */
printTimestamp(MessageOrBuilder message)691     private void printTimestamp(MessageOrBuilder message) throws IOException {
692       Timestamp value = Timestamp.parseFrom(toByteString(message));
693       generator.print("\"" + Timestamps.toString(value) + "\"");
694     }
695 
696     /** Prints google.protobuf.Duration */
printDuration(MessageOrBuilder message)697     private void printDuration(MessageOrBuilder message) throws IOException {
698       Duration value = Duration.parseFrom(toByteString(message));
699       generator.print("\"" + Durations.toString(value) + "\"");
700     }
701 
702     /** Prints google.protobuf.FieldMask */
printFieldMask(MessageOrBuilder message)703     private void printFieldMask(MessageOrBuilder message) throws IOException {
704       FieldMask value = FieldMask.parseFrom(toByteString(message));
705       generator.print("\"" + FieldMaskUtil.toJsonString(value) + "\"");
706     }
707 
708     /** Prints google.protobuf.Struct */
printStruct(MessageOrBuilder message)709     private void printStruct(MessageOrBuilder message) throws IOException {
710       Descriptor descriptor = message.getDescriptorForType();
711       FieldDescriptor field = descriptor.findFieldByName("fields");
712       if (field == null) {
713         throw new InvalidProtocolBufferException("Invalid Struct type.");
714       }
715       // Struct is formatted as a map object.
716       printMapFieldValue(field, message.getField(field));
717     }
718 
719     /** Prints google.protobuf.Value */
printValue(MessageOrBuilder message)720     private void printValue(MessageOrBuilder message) throws IOException {
721       // For a Value message, only the value of the field is formatted.
722       Map<FieldDescriptor, Object> fields = message.getAllFields();
723       if (fields.isEmpty()) {
724         // No value set.
725         generator.print("null");
726         return;
727       }
728       // A Value message can only have at most one field set (it only contains
729       // an oneof).
730       if (fields.size() != 1) {
731         throw new InvalidProtocolBufferException("Invalid Value type.");
732       }
733       for (Map.Entry<FieldDescriptor, Object> entry : fields.entrySet()) {
734         printSingleFieldValue(entry.getKey(), entry.getValue());
735       }
736     }
737 
738     /** Prints google.protobuf.ListValue */
printListValue(MessageOrBuilder message)739     private void printListValue(MessageOrBuilder message) throws IOException {
740       Descriptor descriptor = message.getDescriptorForType();
741       FieldDescriptor field = descriptor.findFieldByName("values");
742       if (field == null) {
743         throw new InvalidProtocolBufferException("Invalid ListValue type.");
744       }
745       printRepeatedFieldValue(field, message.getField(field));
746     }
747 
748     /** Prints a regular message with an optional type URL. */
print(MessageOrBuilder message, String typeUrl)749     private void print(MessageOrBuilder message, String typeUrl) throws IOException {
750       generator.print("{" + blankOrNewLine);
751       generator.indent();
752 
753       boolean printedField = false;
754       if (typeUrl != null) {
755         generator.print("\"@type\":" + blankOrSpace + gson.toJson(typeUrl));
756         printedField = true;
757       }
758       Map<FieldDescriptor, Object> fieldsToPrint = null;
759       if (includingDefaultValueFields) {
760         fieldsToPrint = new TreeMap<FieldDescriptor, Object>();
761         for (FieldDescriptor field : message.getDescriptorForType().getFields()) {
762           if (field.isOptional()
763               && field.getJavaType() == FieldDescriptor.JavaType.MESSAGE
764               && !message.hasField(field)) {
765             // Always skip empty optional message fields. If not we will recurse indefinitely if
766             // a message has itself as a sub-field.
767             continue;
768           }
769           fieldsToPrint.put(field, message.getField(field));
770         }
771       } else {
772         fieldsToPrint = message.getAllFields();
773       }
774       for (Map.Entry<FieldDescriptor, Object> field : fieldsToPrint.entrySet()) {
775         if (printedField) {
776           // Add line-endings for the previous field.
777           generator.print("," + blankOrNewLine);
778         } else {
779           printedField = true;
780         }
781         printField(field.getKey(), field.getValue());
782       }
783 
784       // Add line-endings for the last field.
785       if (printedField) {
786         generator.print(blankOrNewLine);
787       }
788       generator.outdent();
789       generator.print("}");
790     }
791 
printField(FieldDescriptor field, Object value)792     private void printField(FieldDescriptor field, Object value) throws IOException {
793       if (preservingProtoFieldNames) {
794         generator.print("\"" + field.getName() + "\":" + blankOrSpace);
795       } else {
796         generator.print("\"" + field.getJsonName() + "\":" + blankOrSpace);
797       }
798       if (field.isMapField()) {
799         printMapFieldValue(field, value);
800       } else if (field.isRepeated()) {
801         printRepeatedFieldValue(field, value);
802       } else {
803         printSingleFieldValue(field, value);
804       }
805     }
806 
807     @SuppressWarnings("rawtypes")
printRepeatedFieldValue(FieldDescriptor field, Object value)808     private void printRepeatedFieldValue(FieldDescriptor field, Object value) throws IOException {
809       generator.print("[");
810       boolean printedElement = false;
811       for (Object element : (List) value) {
812         if (printedElement) {
813           generator.print("," + blankOrSpace);
814         } else {
815           printedElement = true;
816         }
817         printSingleFieldValue(field, element);
818       }
819       generator.print("]");
820     }
821 
822     @SuppressWarnings("rawtypes")
printMapFieldValue(FieldDescriptor field, Object value)823     private void printMapFieldValue(FieldDescriptor field, Object value) throws IOException {
824       Descriptor type = field.getMessageType();
825       FieldDescriptor keyField = type.findFieldByName("key");
826       FieldDescriptor valueField = type.findFieldByName("value");
827       if (keyField == null || valueField == null) {
828         throw new InvalidProtocolBufferException("Invalid map field.");
829       }
830       generator.print("{" + blankOrNewLine);
831       generator.indent();
832       boolean printedElement = false;
833       for (Object element : (List) value) {
834         Message entry = (Message) element;
835         Object entryKey = entry.getField(keyField);
836         Object entryValue = entry.getField(valueField);
837         if (printedElement) {
838           generator.print("," + blankOrNewLine);
839         } else {
840           printedElement = true;
841         }
842         // Key fields are always double-quoted.
843         printSingleFieldValue(keyField, entryKey, true);
844         generator.print(":" + blankOrSpace);
845         printSingleFieldValue(valueField, entryValue);
846       }
847       if (printedElement) {
848         generator.print(blankOrNewLine);
849       }
850       generator.outdent();
851       generator.print("}");
852     }
853 
printSingleFieldValue(FieldDescriptor field, Object value)854     private void printSingleFieldValue(FieldDescriptor field, Object value) throws IOException {
855       printSingleFieldValue(field, value, false);
856     }
857 
858     /**
859      * Prints a field's value in JSON format.
860      *
861      * @param alwaysWithQuotes whether to always add double-quotes to primitive
862      *        types.
863      */
printSingleFieldValue( final FieldDescriptor field, final Object value, boolean alwaysWithQuotes)864     private void printSingleFieldValue(
865         final FieldDescriptor field, final Object value, boolean alwaysWithQuotes)
866         throws IOException {
867       switch (field.getType()) {
868         case INT32:
869         case SINT32:
870         case SFIXED32:
871           if (alwaysWithQuotes) {
872             generator.print("\"");
873           }
874           generator.print(((Integer) value).toString());
875           if (alwaysWithQuotes) {
876             generator.print("\"");
877           }
878           break;
879 
880         case INT64:
881         case SINT64:
882         case SFIXED64:
883           generator.print("\"" + ((Long) value).toString() + "\"");
884           break;
885 
886         case BOOL:
887           if (alwaysWithQuotes) {
888             generator.print("\"");
889           }
890           if (((Boolean) value).booleanValue()) {
891             generator.print("true");
892           } else {
893             generator.print("false");
894           }
895           if (alwaysWithQuotes) {
896             generator.print("\"");
897           }
898           break;
899 
900         case FLOAT:
901           Float floatValue = (Float) value;
902           if (floatValue.isNaN()) {
903             generator.print("\"NaN\"");
904           } else if (floatValue.isInfinite()) {
905             if (floatValue < 0) {
906               generator.print("\"-Infinity\"");
907             } else {
908               generator.print("\"Infinity\"");
909             }
910           } else {
911             if (alwaysWithQuotes) {
912               generator.print("\"");
913             }
914             generator.print(floatValue.toString());
915             if (alwaysWithQuotes) {
916               generator.print("\"");
917             }
918           }
919           break;
920 
921         case DOUBLE:
922           Double doubleValue = (Double) value;
923           if (doubleValue.isNaN()) {
924             generator.print("\"NaN\"");
925           } else if (doubleValue.isInfinite()) {
926             if (doubleValue < 0) {
927               generator.print("\"-Infinity\"");
928             } else {
929               generator.print("\"Infinity\"");
930             }
931           } else {
932             if (alwaysWithQuotes) {
933               generator.print("\"");
934             }
935             generator.print(doubleValue.toString());
936             if (alwaysWithQuotes) {
937               generator.print("\"");
938             }
939           }
940           break;
941 
942         case UINT32:
943         case FIXED32:
944           if (alwaysWithQuotes) {
945             generator.print("\"");
946           }
947           generator.print(unsignedToString((Integer) value));
948           if (alwaysWithQuotes) {
949             generator.print("\"");
950           }
951           break;
952 
953         case UINT64:
954         case FIXED64:
955           generator.print("\"" + unsignedToString((Long) value) + "\"");
956           break;
957 
958         case STRING:
959           generator.print(gson.toJson(value));
960           break;
961 
962         case BYTES:
963           generator.print("\"");
964           generator.print(BaseEncoding.base64().encode(((ByteString) value).toByteArray()));
965           generator.print("\"");
966           break;
967 
968         case ENUM:
969           // Special-case google.protobuf.NullValue (it's an Enum).
970           if (field.getEnumType().getFullName().equals("google.protobuf.NullValue")) {
971             // No matter what value it contains, we always print it as "null".
972             if (alwaysWithQuotes) {
973               generator.print("\"");
974             }
975             generator.print("null");
976             if (alwaysWithQuotes) {
977               generator.print("\"");
978             }
979           } else {
980             if (((EnumValueDescriptor) value).getIndex() == -1) {
981               generator.print(String.valueOf(((EnumValueDescriptor) value).getNumber()));
982             } else {
983               generator.print("\"" + ((EnumValueDescriptor) value).getName() + "\"");
984             }
985           }
986           break;
987 
988         case MESSAGE:
989         case GROUP:
990           print((Message) value);
991           break;
992       }
993     }
994   }
995 
996   /** Convert an unsigned 32-bit integer to a string. */
unsignedToString(final int value)997   private static String unsignedToString(final int value) {
998     if (value >= 0) {
999       return Integer.toString(value);
1000     } else {
1001       return Long.toString(value & 0x00000000FFFFFFFFL);
1002     }
1003   }
1004 
1005   /** Convert an unsigned 64-bit integer to a string. */
unsignedToString(final long value)1006   private static String unsignedToString(final long value) {
1007     if (value >= 0) {
1008       return Long.toString(value);
1009     } else {
1010       // Pull off the most-significant bit so that BigInteger doesn't think
1011       // the number is negative, then set it again using setBit().
1012       return BigInteger.valueOf(value & Long.MAX_VALUE).setBit(Long.SIZE - 1).toString();
1013     }
1014   }
1015 
getTypeName(String typeUrl)1016   private static String getTypeName(String typeUrl) throws InvalidProtocolBufferException {
1017     String[] parts = typeUrl.split("/");
1018     if (parts.length == 1) {
1019       throw new InvalidProtocolBufferException("Invalid type url found: " + typeUrl);
1020     }
1021     return parts[parts.length - 1];
1022   }
1023 
1024   private static class ParserImpl {
1025     private final TypeRegistry registry;
1026     private final JsonParser jsonParser;
1027 
ParserImpl(TypeRegistry registry)1028     ParserImpl(TypeRegistry registry) {
1029       this.registry = registry;
1030       this.jsonParser = new JsonParser();
1031     }
1032 
merge(Reader json, Message.Builder builder)1033     void merge(Reader json, Message.Builder builder) throws IOException {
1034       JsonReader reader = new JsonReader(json);
1035       reader.setLenient(false);
1036       merge(jsonParser.parse(reader), builder);
1037     }
1038 
merge(String json, Message.Builder builder)1039     void merge(String json, Message.Builder builder) throws InvalidProtocolBufferException {
1040       try {
1041         JsonReader reader = new JsonReader(new StringReader(json));
1042         reader.setLenient(false);
1043         merge(jsonParser.parse(reader), builder);
1044       } catch (InvalidProtocolBufferException e) {
1045         throw e;
1046       } catch (Exception e) {
1047         // We convert all exceptions from JSON parsing to our own exceptions.
1048         throw new InvalidProtocolBufferException(e.getMessage());
1049       }
1050     }
1051 
1052     private interface WellKnownTypeParser {
merge(ParserImpl parser, JsonElement json, Message.Builder builder)1053       void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
1054           throws InvalidProtocolBufferException;
1055     }
1056 
1057     private static final Map<String, WellKnownTypeParser> wellKnownTypeParsers =
1058         buildWellKnownTypeParsers();
1059 
buildWellKnownTypeParsers()1060     private static Map<String, WellKnownTypeParser> buildWellKnownTypeParsers() {
1061       Map<String, WellKnownTypeParser> parsers = new HashMap<String, WellKnownTypeParser>();
1062       // Special-case Any.
1063       parsers.put(
1064           Any.getDescriptor().getFullName(),
1065           new WellKnownTypeParser() {
1066             @Override
1067             public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
1068                 throws InvalidProtocolBufferException {
1069               parser.mergeAny(json, builder);
1070             }
1071           });
1072       // Special-case wrapper types.
1073       WellKnownTypeParser wrappersPrinter =
1074           new WellKnownTypeParser() {
1075             @Override
1076             public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
1077                 throws InvalidProtocolBufferException {
1078               parser.mergeWrapper(json, builder);
1079             }
1080           };
1081       parsers.put(BoolValue.getDescriptor().getFullName(), wrappersPrinter);
1082       parsers.put(Int32Value.getDescriptor().getFullName(), wrappersPrinter);
1083       parsers.put(UInt32Value.getDescriptor().getFullName(), wrappersPrinter);
1084       parsers.put(Int64Value.getDescriptor().getFullName(), wrappersPrinter);
1085       parsers.put(UInt64Value.getDescriptor().getFullName(), wrappersPrinter);
1086       parsers.put(StringValue.getDescriptor().getFullName(), wrappersPrinter);
1087       parsers.put(BytesValue.getDescriptor().getFullName(), wrappersPrinter);
1088       parsers.put(FloatValue.getDescriptor().getFullName(), wrappersPrinter);
1089       parsers.put(DoubleValue.getDescriptor().getFullName(), wrappersPrinter);
1090       // Special-case Timestamp.
1091       parsers.put(
1092           Timestamp.getDescriptor().getFullName(),
1093           new WellKnownTypeParser() {
1094             @Override
1095             public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
1096                 throws InvalidProtocolBufferException {
1097               parser.mergeTimestamp(json, builder);
1098             }
1099           });
1100       // Special-case Duration.
1101       parsers.put(
1102           Duration.getDescriptor().getFullName(),
1103           new WellKnownTypeParser() {
1104             @Override
1105             public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
1106                 throws InvalidProtocolBufferException {
1107               parser.mergeDuration(json, builder);
1108             }
1109           });
1110       // Special-case FieldMask.
1111       parsers.put(
1112           FieldMask.getDescriptor().getFullName(),
1113           new WellKnownTypeParser() {
1114             @Override
1115             public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
1116                 throws InvalidProtocolBufferException {
1117               parser.mergeFieldMask(json, builder);
1118             }
1119           });
1120       // Special-case Struct.
1121       parsers.put(
1122           Struct.getDescriptor().getFullName(),
1123           new WellKnownTypeParser() {
1124             @Override
1125             public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
1126                 throws InvalidProtocolBufferException {
1127               parser.mergeStruct(json, builder);
1128             }
1129           });
1130       // Special-case ListValue.
1131       parsers.put(
1132           ListValue.getDescriptor().getFullName(),
1133           new WellKnownTypeParser() {
1134             @Override
1135             public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
1136                 throws InvalidProtocolBufferException {
1137               parser.mergeListValue(json, builder);
1138             }
1139           });
1140       // Special-case Value.
1141       parsers.put(
1142           Value.getDescriptor().getFullName(),
1143           new WellKnownTypeParser() {
1144             @Override
1145             public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
1146                 throws InvalidProtocolBufferException {
1147               parser.mergeValue(json, builder);
1148             }
1149           });
1150       return parsers;
1151     }
1152 
merge(JsonElement json, Message.Builder builder)1153     private void merge(JsonElement json, Message.Builder builder)
1154         throws InvalidProtocolBufferException {
1155       WellKnownTypeParser specialParser =
1156           wellKnownTypeParsers.get(builder.getDescriptorForType().getFullName());
1157       if (specialParser != null) {
1158         specialParser.merge(this, json, builder);
1159         return;
1160       }
1161       mergeMessage(json, builder, false);
1162     }
1163 
1164     // Maps from camel-case field names to FieldDescriptor.
1165     private final Map<Descriptor, Map<String, FieldDescriptor>> fieldNameMaps =
1166         new HashMap<Descriptor, Map<String, FieldDescriptor>>();
1167 
getFieldNameMap(Descriptor descriptor)1168     private Map<String, FieldDescriptor> getFieldNameMap(Descriptor descriptor) {
1169       if (!fieldNameMaps.containsKey(descriptor)) {
1170         Map<String, FieldDescriptor> fieldNameMap = new HashMap<String, FieldDescriptor>();
1171         for (FieldDescriptor field : descriptor.getFields()) {
1172           fieldNameMap.put(field.getName(), field);
1173           fieldNameMap.put(field.getJsonName(), field);
1174         }
1175         fieldNameMaps.put(descriptor, fieldNameMap);
1176         return fieldNameMap;
1177       }
1178       return fieldNameMaps.get(descriptor);
1179     }
1180 
mergeMessage(JsonElement json, Message.Builder builder, boolean skipTypeUrl)1181     private void mergeMessage(JsonElement json, Message.Builder builder, boolean skipTypeUrl)
1182         throws InvalidProtocolBufferException {
1183       if (!(json instanceof JsonObject)) {
1184         throw new InvalidProtocolBufferException("Expect message object but got: " + json);
1185       }
1186       JsonObject object = (JsonObject) json;
1187       Map<String, FieldDescriptor> fieldNameMap = getFieldNameMap(builder.getDescriptorForType());
1188       for (Map.Entry<String, JsonElement> entry : object.entrySet()) {
1189         if (skipTypeUrl && entry.getKey().equals("@type")) {
1190           continue;
1191         }
1192         FieldDescriptor field = fieldNameMap.get(entry.getKey());
1193         if (field == null) {
1194           throw new InvalidProtocolBufferException(
1195               "Cannot find field: "
1196                   + entry.getKey()
1197                   + " in message "
1198                   + builder.getDescriptorForType().getFullName());
1199         }
1200         mergeField(field, entry.getValue(), builder);
1201       }
1202     }
1203 
mergeAny(JsonElement json, Message.Builder builder)1204     private void mergeAny(JsonElement json, Message.Builder builder)
1205         throws InvalidProtocolBufferException {
1206       Descriptor descriptor = builder.getDescriptorForType();
1207       FieldDescriptor typeUrlField = descriptor.findFieldByName("type_url");
1208       FieldDescriptor valueField = descriptor.findFieldByName("value");
1209       // Validates type of the message. Note that we can't just cast the message
1210       // to com.google.protobuf.Any because it might be a DynamicMessage.
1211       if (typeUrlField == null
1212           || valueField == null
1213           || typeUrlField.getType() != FieldDescriptor.Type.STRING
1214           || valueField.getType() != FieldDescriptor.Type.BYTES) {
1215         throw new InvalidProtocolBufferException("Invalid Any type.");
1216       }
1217 
1218       if (!(json instanceof JsonObject)) {
1219         throw new InvalidProtocolBufferException("Expect message object but got: " + json);
1220       }
1221       JsonObject object = (JsonObject) json;
1222       JsonElement typeUrlElement = object.get("@type");
1223       if (typeUrlElement == null) {
1224         throw new InvalidProtocolBufferException("Missing type url when parsing: " + json);
1225       }
1226       String typeUrl = typeUrlElement.getAsString();
1227       Descriptor contentType = registry.find(getTypeName(typeUrl));
1228       if (contentType == null) {
1229         throw new InvalidProtocolBufferException("Cannot resolve type: " + typeUrl);
1230       }
1231       builder.setField(typeUrlField, typeUrl);
1232       Message.Builder contentBuilder =
1233           DynamicMessage.getDefaultInstance(contentType).newBuilderForType();
1234       WellKnownTypeParser specialParser = wellKnownTypeParsers.get(contentType.getFullName());
1235       if (specialParser != null) {
1236         JsonElement value = object.get("value");
1237         if (value != null) {
1238           specialParser.merge(this, value, contentBuilder);
1239         }
1240       } else {
1241         mergeMessage(json, contentBuilder, true);
1242       }
1243       builder.setField(valueField, contentBuilder.build().toByteString());
1244     }
1245 
mergeFieldMask(JsonElement json, Message.Builder builder)1246     private void mergeFieldMask(JsonElement json, Message.Builder builder)
1247         throws InvalidProtocolBufferException {
1248       FieldMask value = FieldMaskUtil.fromJsonString(json.getAsString());
1249       builder.mergeFrom(value.toByteString());
1250     }
1251 
mergeTimestamp(JsonElement json, Message.Builder builder)1252     private void mergeTimestamp(JsonElement json, Message.Builder builder)
1253         throws InvalidProtocolBufferException {
1254       try {
1255         Timestamp value = Timestamps.parse(json.getAsString());
1256         builder.mergeFrom(value.toByteString());
1257       } catch (ParseException e) {
1258         throw new InvalidProtocolBufferException("Failed to parse timestamp: " + json);
1259       }
1260     }
1261 
mergeDuration(JsonElement json, Message.Builder builder)1262     private void mergeDuration(JsonElement json, Message.Builder builder)
1263         throws InvalidProtocolBufferException {
1264       try {
1265         Duration value = Durations.parse(json.getAsString());
1266         builder.mergeFrom(value.toByteString());
1267       } catch (ParseException e) {
1268         throw new InvalidProtocolBufferException("Failed to parse duration: " + json);
1269       }
1270     }
1271 
mergeStruct(JsonElement json, Message.Builder builder)1272     private void mergeStruct(JsonElement json, Message.Builder builder)
1273         throws InvalidProtocolBufferException {
1274       Descriptor descriptor = builder.getDescriptorForType();
1275       FieldDescriptor field = descriptor.findFieldByName("fields");
1276       if (field == null) {
1277         throw new InvalidProtocolBufferException("Invalid Struct type.");
1278       }
1279       mergeMapField(field, json, builder);
1280     }
1281 
mergeListValue(JsonElement json, Message.Builder builder)1282     private void mergeListValue(JsonElement json, Message.Builder builder)
1283         throws InvalidProtocolBufferException {
1284       Descriptor descriptor = builder.getDescriptorForType();
1285       FieldDescriptor field = descriptor.findFieldByName("values");
1286       if (field == null) {
1287         throw new InvalidProtocolBufferException("Invalid ListValue type.");
1288       }
1289       mergeRepeatedField(field, json, builder);
1290     }
1291 
mergeValue(JsonElement json, Message.Builder builder)1292     private void mergeValue(JsonElement json, Message.Builder builder)
1293         throws InvalidProtocolBufferException {
1294       Descriptor type = builder.getDescriptorForType();
1295       if (json instanceof JsonPrimitive) {
1296         JsonPrimitive primitive = (JsonPrimitive) json;
1297         if (primitive.isBoolean()) {
1298           builder.setField(type.findFieldByName("bool_value"), primitive.getAsBoolean());
1299         } else if (primitive.isNumber()) {
1300           builder.setField(type.findFieldByName("number_value"), primitive.getAsDouble());
1301         } else {
1302           builder.setField(type.findFieldByName("string_value"), primitive.getAsString());
1303         }
1304       } else if (json instanceof JsonObject) {
1305         FieldDescriptor field = type.findFieldByName("struct_value");
1306         Message.Builder structBuilder = builder.newBuilderForField(field);
1307         merge(json, structBuilder);
1308         builder.setField(field, structBuilder.build());
1309       } else if (json instanceof JsonArray) {
1310         FieldDescriptor field = type.findFieldByName("list_value");
1311         Message.Builder listBuilder = builder.newBuilderForField(field);
1312         merge(json, listBuilder);
1313         builder.setField(field, listBuilder.build());
1314       } else {
1315         throw new IllegalStateException("Unexpected json data: " + json);
1316       }
1317     }
1318 
mergeWrapper(JsonElement json, Message.Builder builder)1319     private void mergeWrapper(JsonElement json, Message.Builder builder)
1320         throws InvalidProtocolBufferException {
1321       Descriptor type = builder.getDescriptorForType();
1322       FieldDescriptor field = type.findFieldByName("value");
1323       if (field == null) {
1324         throw new InvalidProtocolBufferException("Invalid wrapper type: " + type.getFullName());
1325       }
1326       builder.setField(field, parseFieldValue(field, json, builder));
1327     }
1328 
mergeField(FieldDescriptor field, JsonElement json, Message.Builder builder)1329     private void mergeField(FieldDescriptor field, JsonElement json, Message.Builder builder)
1330         throws InvalidProtocolBufferException {
1331       if (field.isRepeated()) {
1332         if (builder.getRepeatedFieldCount(field) > 0) {
1333           throw new InvalidProtocolBufferException(
1334               "Field " + field.getFullName() + " has already been set.");
1335         }
1336       } else {
1337         if (builder.hasField(field)) {
1338           throw new InvalidProtocolBufferException(
1339               "Field " + field.getFullName() + " has already been set.");
1340         }
1341         if (field.getContainingOneof() != null
1342             && builder.getOneofFieldDescriptor(field.getContainingOneof()) != null) {
1343           FieldDescriptor other = builder.getOneofFieldDescriptor(field.getContainingOneof());
1344           throw new InvalidProtocolBufferException(
1345               "Cannot set field "
1346                   + field.getFullName()
1347                   + " because another field "
1348                   + other.getFullName()
1349                   + " belonging to the same oneof has already been set ");
1350         }
1351       }
1352       if (field.isRepeated() && json instanceof JsonNull) {
1353         // We allow "null" as value for all field types and treat it as if the
1354         // field is not present.
1355         return;
1356       }
1357       if (field.isMapField()) {
1358         mergeMapField(field, json, builder);
1359       } else if (field.isRepeated()) {
1360         mergeRepeatedField(field, json, builder);
1361       } else {
1362         Object value = parseFieldValue(field, json, builder);
1363         if (value != null) {
1364           builder.setField(field, value);
1365         }
1366       }
1367     }
1368 
mergeMapField(FieldDescriptor field, JsonElement json, Message.Builder builder)1369     private void mergeMapField(FieldDescriptor field, JsonElement json, Message.Builder builder)
1370         throws InvalidProtocolBufferException {
1371       if (!(json instanceof JsonObject)) {
1372         throw new InvalidProtocolBufferException("Expect a map object but found: " + json);
1373       }
1374       Descriptor type = field.getMessageType();
1375       FieldDescriptor keyField = type.findFieldByName("key");
1376       FieldDescriptor valueField = type.findFieldByName("value");
1377       if (keyField == null || valueField == null) {
1378         throw new InvalidProtocolBufferException("Invalid map field: " + field.getFullName());
1379       }
1380       JsonObject object = (JsonObject) json;
1381       for (Map.Entry<String, JsonElement> entry : object.entrySet()) {
1382         Message.Builder entryBuilder = builder.newBuilderForField(field);
1383         Object key = parseFieldValue(keyField, new JsonPrimitive(entry.getKey()), entryBuilder);
1384         Object value = parseFieldValue(valueField, entry.getValue(), entryBuilder);
1385         if (value == null) {
1386           throw new InvalidProtocolBufferException("Map value cannot be null.");
1387         }
1388         entryBuilder.setField(keyField, key);
1389         entryBuilder.setField(valueField, value);
1390         builder.addRepeatedField(field, entryBuilder.build());
1391       }
1392     }
1393 
1394     /**
1395      * Gets the default value for a field type. Note that we use proto3
1396      * language defaults and ignore any default values set through the
1397      * proto "default" option.
1398      */
getDefaultValue(FieldDescriptor field, Message.Builder builder)1399     private Object getDefaultValue(FieldDescriptor field, Message.Builder builder) {
1400       switch (field.getType()) {
1401         case INT32:
1402         case SINT32:
1403         case SFIXED32:
1404         case UINT32:
1405         case FIXED32:
1406           return 0;
1407         case INT64:
1408         case SINT64:
1409         case SFIXED64:
1410         case UINT64:
1411         case FIXED64:
1412           return 0L;
1413         case FLOAT:
1414           return 0.0f;
1415         case DOUBLE:
1416           return 0.0;
1417         case BOOL:
1418           return false;
1419         case STRING:
1420           return "";
1421         case BYTES:
1422           return ByteString.EMPTY;
1423         case ENUM:
1424           return field.getEnumType().getValues().get(0);
1425         case MESSAGE:
1426         case GROUP:
1427           return builder.newBuilderForField(field).getDefaultInstanceForType();
1428         default:
1429           throw new IllegalStateException("Invalid field type: " + field.getType());
1430       }
1431     }
1432 
mergeRepeatedField( FieldDescriptor field, JsonElement json, Message.Builder builder)1433     private void mergeRepeatedField(
1434         FieldDescriptor field, JsonElement json, Message.Builder builder)
1435         throws InvalidProtocolBufferException {
1436       if (!(json instanceof JsonArray)) {
1437         throw new InvalidProtocolBufferException("Expect an array but found: " + json);
1438       }
1439       JsonArray array = (JsonArray) json;
1440       for (int i = 0; i < array.size(); ++i) {
1441         Object value = parseFieldValue(field, array.get(i), builder);
1442         if (value == null) {
1443           throw new InvalidProtocolBufferException("Repeated field elements cannot be null");
1444         }
1445         builder.addRepeatedField(field, value);
1446       }
1447     }
1448 
parseInt32(JsonElement json)1449     private int parseInt32(JsonElement json) throws InvalidProtocolBufferException {
1450       try {
1451         return Integer.parseInt(json.getAsString());
1452       } catch (Exception e) {
1453         // Fall through.
1454       }
1455       // JSON doesn't distinguish between integer values and floating point values so "1" and
1456       // "1.000" are treated as equal in JSON. For this reason we accept floating point values for
1457       // integer fields as well as long as it actually is an integer (i.e., round(value) == value).
1458       try {
1459         BigDecimal value = new BigDecimal(json.getAsString());
1460         return value.intValueExact();
1461       } catch (Exception e) {
1462         throw new InvalidProtocolBufferException("Not an int32 value: " + json);
1463       }
1464     }
1465 
parseInt64(JsonElement json)1466     private long parseInt64(JsonElement json) throws InvalidProtocolBufferException {
1467       try {
1468         return Long.parseLong(json.getAsString());
1469       } catch (Exception e) {
1470         // Fall through.
1471       }
1472       // JSON doesn't distinguish between integer values and floating point values so "1" and
1473       // "1.000" are treated as equal in JSON. For this reason we accept floating point values for
1474       // integer fields as well as long as it actually is an integer (i.e., round(value) == value).
1475       try {
1476         BigDecimal value = new BigDecimal(json.getAsString());
1477         return value.longValueExact();
1478       } catch (Exception e) {
1479         throw new InvalidProtocolBufferException("Not an int32 value: " + json);
1480       }
1481     }
1482 
parseUint32(JsonElement json)1483     private int parseUint32(JsonElement json) throws InvalidProtocolBufferException {
1484       try {
1485         long result = Long.parseLong(json.getAsString());
1486         if (result < 0 || result > 0xFFFFFFFFL) {
1487           throw new InvalidProtocolBufferException("Out of range uint32 value: " + json);
1488         }
1489         return (int) result;
1490       } catch (InvalidProtocolBufferException e) {
1491         throw e;
1492       } catch (Exception e) {
1493         // Fall through.
1494       }
1495       // JSON doesn't distinguish between integer values and floating point values so "1" and
1496       // "1.000" are treated as equal in JSON. For this reason we accept floating point values for
1497       // integer fields as well as long as it actually is an integer (i.e., round(value) == value).
1498       try {
1499         BigDecimal decimalValue = new BigDecimal(json.getAsString());
1500         BigInteger value = decimalValue.toBigIntegerExact();
1501         if (value.signum() < 0 || value.compareTo(new BigInteger("FFFFFFFF", 16)) > 0) {
1502           throw new InvalidProtocolBufferException("Out of range uint32 value: " + json);
1503         }
1504         return value.intValue();
1505       } catch (InvalidProtocolBufferException e) {
1506         throw e;
1507       } catch (Exception e) {
1508         throw new InvalidProtocolBufferException("Not an uint32 value: " + json);
1509       }
1510     }
1511 
1512     private static final BigInteger MAX_UINT64 = new BigInteger("FFFFFFFFFFFFFFFF", 16);
1513 
parseUint64(JsonElement json)1514     private long parseUint64(JsonElement json) throws InvalidProtocolBufferException {
1515       try {
1516         BigDecimal decimalValue = new BigDecimal(json.getAsString());
1517         BigInteger value = decimalValue.toBigIntegerExact();
1518         if (value.compareTo(BigInteger.ZERO) < 0 || value.compareTo(MAX_UINT64) > 0) {
1519           throw new InvalidProtocolBufferException("Out of range uint64 value: " + json);
1520         }
1521         return value.longValue();
1522       } catch (InvalidProtocolBufferException e) {
1523         throw e;
1524       } catch (Exception e) {
1525         throw new InvalidProtocolBufferException("Not an uint64 value: " + json);
1526       }
1527     }
1528 
parseBool(JsonElement json)1529     private boolean parseBool(JsonElement json) throws InvalidProtocolBufferException {
1530       if (json.getAsString().equals("true")) {
1531         return true;
1532       }
1533       if (json.getAsString().equals("false")) {
1534         return false;
1535       }
1536       throw new InvalidProtocolBufferException("Invalid bool value: " + json);
1537     }
1538 
1539     private static final double EPSILON = 1e-6;
1540 
parseFloat(JsonElement json)1541     private float parseFloat(JsonElement json) throws InvalidProtocolBufferException {
1542       if (json.getAsString().equals("NaN")) {
1543         return Float.NaN;
1544       } else if (json.getAsString().equals("Infinity")) {
1545         return Float.POSITIVE_INFINITY;
1546       } else if (json.getAsString().equals("-Infinity")) {
1547         return Float.NEGATIVE_INFINITY;
1548       }
1549       try {
1550         // We don't use Float.parseFloat() here because that function simply
1551         // accepts all double values. Here we parse the value into a Double
1552         // and do explicit range check on it.
1553         double value = Double.parseDouble(json.getAsString());
1554         // When a float value is printed, the printed value might be a little
1555         // larger or smaller due to precision loss. Here we need to add a bit
1556         // of tolerance when checking whether the float value is in range.
1557         if (value > Float.MAX_VALUE * (1.0 + EPSILON)
1558             || value < -Float.MAX_VALUE * (1.0 + EPSILON)) {
1559           throw new InvalidProtocolBufferException("Out of range float value: " + json);
1560         }
1561         return (float) value;
1562       } catch (InvalidProtocolBufferException e) {
1563         throw e;
1564       } catch (Exception e) {
1565         throw new InvalidProtocolBufferException("Not a float value: " + json);
1566       }
1567     }
1568 
1569     private static final BigDecimal MORE_THAN_ONE = new BigDecimal(String.valueOf(1.0 + EPSILON));
1570     // When a float value is printed, the printed value might be a little
1571     // larger or smaller due to precision loss. Here we need to add a bit
1572     // of tolerance when checking whether the float value is in range.
1573     private static final BigDecimal MAX_DOUBLE =
1574         new BigDecimal(String.valueOf(Double.MAX_VALUE)).multiply(MORE_THAN_ONE);
1575     private static final BigDecimal MIN_DOUBLE =
1576         new BigDecimal(String.valueOf(-Double.MAX_VALUE)).multiply(MORE_THAN_ONE);
1577 
parseDouble(JsonElement json)1578     private double parseDouble(JsonElement json) throws InvalidProtocolBufferException {
1579       if (json.getAsString().equals("NaN")) {
1580         return Double.NaN;
1581       } else if (json.getAsString().equals("Infinity")) {
1582         return Double.POSITIVE_INFINITY;
1583       } else if (json.getAsString().equals("-Infinity")) {
1584         return Double.NEGATIVE_INFINITY;
1585       }
1586       try {
1587         // We don't use Double.parseDouble() here because that function simply
1588         // accepts all values. Here we parse the value into a BigDecimal and do
1589         // explicit range check on it.
1590         BigDecimal value = new BigDecimal(json.getAsString());
1591         if (value.compareTo(MAX_DOUBLE) > 0 || value.compareTo(MIN_DOUBLE) < 0) {
1592           throw new InvalidProtocolBufferException("Out of range double value: " + json);
1593         }
1594         return value.doubleValue();
1595       } catch (InvalidProtocolBufferException e) {
1596         throw e;
1597       } catch (Exception e) {
1598         throw new InvalidProtocolBufferException("Not an double value: " + json);
1599       }
1600     }
1601 
parseString(JsonElement json)1602     private String parseString(JsonElement json) {
1603       return json.getAsString();
1604     }
1605 
parseBytes(JsonElement json)1606     private ByteString parseBytes(JsonElement json) throws InvalidProtocolBufferException {
1607       String encoded = json.getAsString();
1608       if (encoded.length() % 4 != 0) {
1609         throw new InvalidProtocolBufferException(
1610             "Bytes field is not encoded in standard BASE64 with paddings: " + encoded);
1611       }
1612       return ByteString.copyFrom(BaseEncoding.base64().decode(json.getAsString()));
1613     }
1614 
parseEnum(EnumDescriptor enumDescriptor, JsonElement json)1615     private EnumValueDescriptor parseEnum(EnumDescriptor enumDescriptor, JsonElement json)
1616         throws InvalidProtocolBufferException {
1617       String value = json.getAsString();
1618       EnumValueDescriptor result = enumDescriptor.findValueByName(value);
1619       if (result == null) {
1620         // Try to interpret the value as a number.
1621         try {
1622           int numericValue = parseInt32(json);
1623           if (enumDescriptor.getFile().getSyntax() == FileDescriptor.Syntax.PROTO3) {
1624             result = enumDescriptor.findValueByNumberCreatingIfUnknown(numericValue);
1625           } else {
1626             result = enumDescriptor.findValueByNumber(numericValue);
1627           }
1628         } catch (InvalidProtocolBufferException e) {
1629           // Fall through. This exception is about invalid int32 value we get from parseInt32() but
1630           // that's not the exception we want the user to see. Since result == null, we will throw
1631           // an exception later.
1632         }
1633 
1634         if (result == null) {
1635           throw new InvalidProtocolBufferException(
1636               "Invalid enum value: " + value + " for enum type: " + enumDescriptor.getFullName());
1637         }
1638       }
1639       return result;
1640     }
1641 
parseFieldValue(FieldDescriptor field, JsonElement json, Message.Builder builder)1642     private Object parseFieldValue(FieldDescriptor field, JsonElement json, Message.Builder builder)
1643         throws InvalidProtocolBufferException {
1644       if (json instanceof JsonNull) {
1645         if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE
1646             && field.getMessageType().getFullName().equals(Value.getDescriptor().getFullName())) {
1647           // For every other type, "null" means absence, but for the special
1648           // Value message, it means the "null_value" field has been set.
1649           Value value = Value.newBuilder().setNullValueValue(0).build();
1650           return builder.newBuilderForField(field).mergeFrom(value.toByteString()).build();
1651         } else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM
1652             && field.getEnumType().getFullName().equals(NullValue.getDescriptor().getFullName())) {
1653           // If the type of the field is a NullValue, then the value should be explicitly set.
1654           return field.getEnumType().findValueByNumber(0);
1655         }
1656         return null;
1657       }
1658       switch (field.getType()) {
1659         case INT32:
1660         case SINT32:
1661         case SFIXED32:
1662           return parseInt32(json);
1663 
1664         case INT64:
1665         case SINT64:
1666         case SFIXED64:
1667           return parseInt64(json);
1668 
1669         case BOOL:
1670           return parseBool(json);
1671 
1672         case FLOAT:
1673           return parseFloat(json);
1674 
1675         case DOUBLE:
1676           return parseDouble(json);
1677 
1678         case UINT32:
1679         case FIXED32:
1680           return parseUint32(json);
1681 
1682         case UINT64:
1683         case FIXED64:
1684           return parseUint64(json);
1685 
1686         case STRING:
1687           return parseString(json);
1688 
1689         case BYTES:
1690           return parseBytes(json);
1691 
1692         case ENUM:
1693           return parseEnum(field.getEnumType(), json);
1694 
1695         case MESSAGE:
1696         case GROUP:
1697           Message.Builder subBuilder = builder.newBuilderForField(field);
1698           merge(json, subBuilder);
1699           return subBuilder.build();
1700 
1701         default:
1702           throw new InvalidProtocolBufferException("Invalid field type: " + field.getType());
1703       }
1704     }
1705   }
1706 }
1707