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