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