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