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