Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(219)

Side by Side Diff: third_party/protobuf/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java

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

Powered by Google App Engine
This is Rietveld 408576698