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