OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 } |
OLD | NEW |