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