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