| OLD | NEW |
| 1 #region Copyright notice and license | 1 #region Copyright notice and license |
| 2 // Protocol Buffers - Google's data interchange format | 2 // Protocol Buffers - Google's data interchange format |
| 3 // Copyright 2015 Google Inc. All rights reserved. | 3 // Copyright 2015 Google Inc. All rights reserved. |
| 4 // https://developers.google.com/protocol-buffers/ | 4 // https://developers.google.com/protocol-buffers/ |
| 5 // | 5 // |
| 6 // Redistribution and use in source and binary forms, with or without | 6 // Redistribution and use in source and binary forms, with or without |
| 7 // modification, are permitted provided that the following conditions are | 7 // modification, are permitted provided that the following conditions are |
| 8 // met: | 8 // met: |
| 9 // | 9 // |
| 10 // * Redistributions of source code must retain the above copyright | 10 // * Redistributions of source code must retain the above copyright |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 161 } | 161 } |
| 162 // Well-known types with no special handling continue in the nor
mal way. | 162 // Well-known types with no special handling continue in the nor
mal way. |
| 163 } | 163 } |
| 164 var token = tokenizer.Next(); | 164 var token = tokenizer.Next(); |
| 165 if (token.Type != JsonToken.TokenType.StartObject) | 165 if (token.Type != JsonToken.TokenType.StartObject) |
| 166 { | 166 { |
| 167 throw new InvalidProtocolBufferException("Expected an object"); | 167 throw new InvalidProtocolBufferException("Expected an object"); |
| 168 } | 168 } |
| 169 var descriptor = message.Descriptor; | 169 var descriptor = message.Descriptor; |
| 170 var jsonFieldMap = descriptor.Fields.ByJsonName(); | 170 var jsonFieldMap = descriptor.Fields.ByJsonName(); |
| 171 // All the oneof fields we've already accounted for - we can only se
e each of them once. |
| 172 // The set is created lazily to avoid the overhead of creating a set
for every message |
| 173 // we parsed, when oneofs are relatively rare. |
| 174 HashSet<OneofDescriptor> seenOneofs = null; |
| 171 while (true) | 175 while (true) |
| 172 { | 176 { |
| 173 token = tokenizer.Next(); | 177 token = tokenizer.Next(); |
| 174 if (token.Type == JsonToken.TokenType.EndObject) | 178 if (token.Type == JsonToken.TokenType.EndObject) |
| 175 { | 179 { |
| 176 return; | 180 return; |
| 177 } | 181 } |
| 178 if (token.Type != JsonToken.TokenType.Name) | 182 if (token.Type != JsonToken.TokenType.Name) |
| 179 { | 183 { |
| 180 throw new InvalidOperationException("Unexpected token type "
+ token.Type); | 184 throw new InvalidOperationException("Unexpected token type "
+ token.Type); |
| 181 } | 185 } |
| 182 string name = token.StringValue; | 186 string name = token.StringValue; |
| 183 FieldDescriptor field; | 187 FieldDescriptor field; |
| 184 if (jsonFieldMap.TryGetValue(name, out field)) | 188 if (jsonFieldMap.TryGetValue(name, out field)) |
| 185 { | 189 { |
| 190 if (field.ContainingOneof != null) |
| 191 { |
| 192 if (seenOneofs == null) |
| 193 { |
| 194 seenOneofs = new HashSet<OneofDescriptor>(); |
| 195 } |
| 196 if (!seenOneofs.Add(field.ContainingOneof)) |
| 197 { |
| 198 throw new InvalidProtocolBufferException($"Multiple
values specified for oneof {field.ContainingOneof.Name}"); |
| 199 } |
| 200 } |
| 186 MergeField(message, field, tokenizer); | 201 MergeField(message, field, tokenizer); |
| 187 } | 202 } |
| 188 else | 203 else |
| 189 { | 204 { |
| 190 // TODO: Is this what we want to do? If not, we'll need to s
kip the value, | 205 // TODO: Is this what we want to do? If not, we'll need to s
kip the value, |
| 191 // which may be an object or array. (We might want to put co
de in the tokenizer | 206 // which may be an object or array. (We might want to put co
de in the tokenizer |
| 192 // to do that.) | 207 // to do that.) |
| 193 throw new InvalidProtocolBufferException("Unknown field: " +
name); | 208 throw new InvalidProtocolBufferException("Unknown field: " +
name); |
| 194 } | 209 } |
| 195 } | 210 } |
| 196 } | 211 } |
| 197 | 212 |
| 198 private void MergeField(IMessage message, FieldDescriptor field, JsonTok
enizer tokenizer) | 213 private void MergeField(IMessage message, FieldDescriptor field, JsonTok
enizer tokenizer) |
| 199 { | 214 { |
| 200 var token = tokenizer.Next(); | 215 var token = tokenizer.Next(); |
| 201 if (token.Type == JsonToken.TokenType.Null) | 216 if (token.Type == JsonToken.TokenType.Null) |
| 202 { | 217 { |
| 218 // Clear the field if we see a null token, unless it's for a sin
gular field of type |
| 219 // google.protobuf.Value. |
| 203 // Note: different from Java API, which just ignores it. | 220 // Note: different from Java API, which just ignores it. |
| 204 // TODO: Bring it more in line? Discuss... | 221 // TODO: Bring it more in line? Discuss... |
| 205 field.Accessor.Clear(message); | 222 if (field.IsMap || field.IsRepeated || !IsGoogleProtobufValueFie
ld(field)) |
| 206 return; | 223 { |
| 224 field.Accessor.Clear(message); |
| 225 return; |
| 226 } |
| 207 } | 227 } |
| 208 tokenizer.PushBack(token); | 228 tokenizer.PushBack(token); |
| 209 | 229 |
| 210 if (field.IsMap) | 230 if (field.IsMap) |
| 211 { | 231 { |
| 212 MergeMapField(message, field, tokenizer); | 232 MergeMapField(message, field, tokenizer); |
| 213 } | 233 } |
| 214 else if (field.IsRepeated) | 234 else if (field.IsRepeated) |
| 215 { | 235 { |
| 216 MergeRepeatedField(message, field, tokenizer); | 236 MergeRepeatedField(message, field, tokenizer); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 232 | 252 |
| 233 IList list = (IList) field.Accessor.GetValue(message); | 253 IList list = (IList) field.Accessor.GetValue(message); |
| 234 while (true) | 254 while (true) |
| 235 { | 255 { |
| 236 token = tokenizer.Next(); | 256 token = tokenizer.Next(); |
| 237 if (token.Type == JsonToken.TokenType.EndArray) | 257 if (token.Type == JsonToken.TokenType.EndArray) |
| 238 { | 258 { |
| 239 return; | 259 return; |
| 240 } | 260 } |
| 241 tokenizer.PushBack(token); | 261 tokenizer.PushBack(token); |
| 262 if (token.Type == JsonToken.TokenType.Null) |
| 263 { |
| 264 throw new InvalidProtocolBufferException("Repeated field ele
ments cannot be null"); |
| 265 } |
| 242 list.Add(ParseSingleValue(field, tokenizer)); | 266 list.Add(ParseSingleValue(field, tokenizer)); |
| 243 } | 267 } |
| 244 } | 268 } |
| 245 | 269 |
| 246 private void MergeMapField(IMessage message, FieldDescriptor field, Json
Tokenizer tokenizer) | 270 private void MergeMapField(IMessage message, FieldDescriptor field, Json
Tokenizer tokenizer) |
| 247 { | 271 { |
| 248 // Map fields are always objects, even if the values are well-known
types: ParseSingleValue handles those. | 272 // Map fields are always objects, even if the values are well-known
types: ParseSingleValue handles those. |
| 249 var token = tokenizer.Next(); | 273 var token = tokenizer.Next(); |
| 250 if (token.Type != JsonToken.TokenType.StartObject) | 274 if (token.Type != JsonToken.TokenType.StartObject) |
| 251 { | 275 { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 263 | 287 |
| 264 while (true) | 288 while (true) |
| 265 { | 289 { |
| 266 token = tokenizer.Next(); | 290 token = tokenizer.Next(); |
| 267 if (token.Type == JsonToken.TokenType.EndObject) | 291 if (token.Type == JsonToken.TokenType.EndObject) |
| 268 { | 292 { |
| 269 return; | 293 return; |
| 270 } | 294 } |
| 271 object key = ParseMapKey(keyField, token.StringValue); | 295 object key = ParseMapKey(keyField, token.StringValue); |
| 272 object value = ParseSingleValue(valueField, tokenizer); | 296 object value = ParseSingleValue(valueField, tokenizer); |
| 273 // TODO: Null handling | 297 if (value == null) |
| 298 { |
| 299 throw new InvalidProtocolBufferException("Map values must no
t be null"); |
| 300 } |
| 274 dictionary[key] = value; | 301 dictionary[key] = value; |
| 275 } | 302 } |
| 276 } | 303 } |
| 277 | 304 |
| 305 private static bool IsGoogleProtobufValueField(FieldDescriptor field) |
| 306 { |
| 307 return field.FieldType == FieldType.Message && |
| 308 field.MessageType.FullName == Value.Descriptor.FullName; |
| 309 } |
| 310 |
| 278 private object ParseSingleValue(FieldDescriptor field, JsonTokenizer tok
enizer) | 311 private object ParseSingleValue(FieldDescriptor field, JsonTokenizer tok
enizer) |
| 279 { | 312 { |
| 280 var token = tokenizer.Next(); | 313 var token = tokenizer.Next(); |
| 281 if (token.Type == JsonToken.TokenType.Null) | 314 if (token.Type == JsonToken.TokenType.Null) |
| 282 { | 315 { |
| 283 if (field.FieldType == FieldType.Message && field.MessageType.Fu
llName == Value.Descriptor.FullName) | 316 // TODO: In order to support dynamic messages, we should really
build this up |
| 317 // dynamically. |
| 318 if (IsGoogleProtobufValueField(field)) |
| 284 { | 319 { |
| 285 return new Value { NullValue = NullValue.NULL_VALUE }; | 320 return Value.ForNull(); |
| 286 } | 321 } |
| 287 return null; | 322 return null; |
| 288 } | 323 } |
| 289 | 324 |
| 290 var fieldType = field.FieldType; | 325 var fieldType = field.FieldType; |
| 291 if (fieldType == FieldType.Message) | 326 if (fieldType == FieldType.Message) |
| 292 { | 327 { |
| 293 // Parse wrapper types as their constituent types. | 328 // Parse wrapper types as their constituent types. |
| 294 // TODO: What does this mean for null? | 329 // TODO: What does this mean for null? |
| 295 if (field.MessageType.IsWrapperType) | 330 if (field.MessageType.IsWrapperType) |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 332 | 367 |
| 333 /// <summary> | 368 /// <summary> |
| 334 /// Parses <paramref name="json"/> into a new message. | 369 /// Parses <paramref name="json"/> into a new message. |
| 335 /// </summary> | 370 /// </summary> |
| 336 /// <typeparam name="T">The type of message to create.</typeparam> | 371 /// <typeparam name="T">The type of message to create.</typeparam> |
| 337 /// <param name="json">The JSON to parse.</param> | 372 /// <param name="json">The JSON to parse.</param> |
| 338 /// <exception cref="InvalidJsonException">The JSON does not comply with
RFC 7159</exception> | 373 /// <exception cref="InvalidJsonException">The JSON does not comply with
RFC 7159</exception> |
| 339 /// <exception cref="InvalidProtocolBufferException">The JSON does not r
epresent a Protocol Buffers message correctly</exception> | 374 /// <exception cref="InvalidProtocolBufferException">The JSON does not r
epresent a Protocol Buffers message correctly</exception> |
| 340 public T Parse<T>(string json) where T : IMessage, new() | 375 public T Parse<T>(string json) where T : IMessage, new() |
| 341 { | 376 { |
| 342 Preconditions.CheckNotNull(json, nameof(json)); | 377 ProtoPreconditions.CheckNotNull(json, nameof(json)); |
| 343 return Parse<T>(new StringReader(json)); | 378 return Parse<T>(new StringReader(json)); |
| 344 } | 379 } |
| 345 | 380 |
| 346 /// <summary> | 381 /// <summary> |
| 347 /// Parses JSON read from <paramref name="jsonReader"/> into a new messa
ge. | 382 /// Parses JSON read from <paramref name="jsonReader"/> into a new messa
ge. |
| 348 /// </summary> | 383 /// </summary> |
| 349 /// <typeparam name="T">The type of message to create.</typeparam> | 384 /// <typeparam name="T">The type of message to create.</typeparam> |
| 350 /// <param name="jsonReader">Reader providing the JSON to parse.</param> | 385 /// <param name="jsonReader">Reader providing the JSON to parse.</param> |
| 351 /// <exception cref="InvalidJsonException">The JSON does not comply with
RFC 7159</exception> | 386 /// <exception cref="InvalidJsonException">The JSON does not comply with
RFC 7159</exception> |
| 352 /// <exception cref="InvalidProtocolBufferException">The JSON does not r
epresent a Protocol Buffers message correctly</exception> | 387 /// <exception cref="InvalidProtocolBufferException">The JSON does not r
epresent a Protocol Buffers message correctly</exception> |
| 353 public T Parse<T>(TextReader jsonReader) where T : IMessage, new() | 388 public T Parse<T>(TextReader jsonReader) where T : IMessage, new() |
| 354 { | 389 { |
| 355 Preconditions.CheckNotNull(jsonReader, nameof(jsonReader)); | 390 ProtoPreconditions.CheckNotNull(jsonReader, nameof(jsonReader)); |
| 356 T message = new T(); | 391 T message = new T(); |
| 357 Merge(message, jsonReader); | 392 Merge(message, jsonReader); |
| 358 return message; | 393 return message; |
| 359 } | 394 } |
| 360 | 395 |
| 361 /// <summary> | 396 /// <summary> |
| 362 /// Parses <paramref name="json"/> into a new message. | 397 /// Parses <paramref name="json"/> into a new message. |
| 363 /// </summary> | 398 /// </summary> |
| 364 /// <param name="json">The JSON to parse.</param> | 399 /// <param name="json">The JSON to parse.</param> |
| 365 /// <param name="descriptor">Descriptor of message type to parse.</param
> | 400 /// <param name="descriptor">Descriptor of message type to parse.</param
> |
| 366 /// <exception cref="InvalidJsonException">The JSON does not comply with
RFC 7159</exception> | 401 /// <exception cref="InvalidJsonException">The JSON does not comply with
RFC 7159</exception> |
| 367 /// <exception cref="InvalidProtocolBufferException">The JSON does not r
epresent a Protocol Buffers message correctly</exception> | 402 /// <exception cref="InvalidProtocolBufferException">The JSON does not r
epresent a Protocol Buffers message correctly</exception> |
| 368 public IMessage Parse(string json, MessageDescriptor descriptor) | 403 public IMessage Parse(string json, MessageDescriptor descriptor) |
| 369 { | 404 { |
| 370 Preconditions.CheckNotNull(json, nameof(json)); | 405 ProtoPreconditions.CheckNotNull(json, nameof(json)); |
| 371 Preconditions.CheckNotNull(descriptor, nameof(descriptor)); | 406 ProtoPreconditions.CheckNotNull(descriptor, nameof(descriptor)); |
| 372 return Parse(new StringReader(json), descriptor); | 407 return Parse(new StringReader(json), descriptor); |
| 373 } | 408 } |
| 374 | 409 |
| 375 /// <summary> | 410 /// <summary> |
| 376 /// Parses JSON read from <paramref name="jsonReader"/> into a new messa
ge. | 411 /// Parses JSON read from <paramref name="jsonReader"/> into a new messa
ge. |
| 377 /// </summary> | 412 /// </summary> |
| 378 /// <param name="jsonReader">Reader providing the JSON to parse.</param> | 413 /// <param name="jsonReader">Reader providing the JSON to parse.</param> |
| 379 /// <param name="descriptor">Descriptor of message type to parse.</param
> | 414 /// <param name="descriptor">Descriptor of message type to parse.</param
> |
| 380 /// <exception cref="InvalidJsonException">The JSON does not comply with
RFC 7159</exception> | 415 /// <exception cref="InvalidJsonException">The JSON does not comply with
RFC 7159</exception> |
| 381 /// <exception cref="InvalidProtocolBufferException">The JSON does not r
epresent a Protocol Buffers message correctly</exception> | 416 /// <exception cref="InvalidProtocolBufferException">The JSON does not r
epresent a Protocol Buffers message correctly</exception> |
| 382 public IMessage Parse(TextReader jsonReader, MessageDescriptor descripto
r) | 417 public IMessage Parse(TextReader jsonReader, MessageDescriptor descripto
r) |
| 383 { | 418 { |
| 384 Preconditions.CheckNotNull(jsonReader, nameof(jsonReader)); | 419 ProtoPreconditions.CheckNotNull(jsonReader, nameof(jsonReader)); |
| 385 Preconditions.CheckNotNull(descriptor, nameof(descriptor)); | 420 ProtoPreconditions.CheckNotNull(descriptor, nameof(descriptor)); |
| 386 IMessage message = descriptor.Parser.CreateTemplate(); | 421 IMessage message = descriptor.Parser.CreateTemplate(); |
| 387 Merge(message, jsonReader); | 422 Merge(message, jsonReader); |
| 388 return message; | 423 return message; |
| 389 } | 424 } |
| 390 | 425 |
| 391 private void MergeStructValue(IMessage message, JsonTokenizer tokenizer) | 426 private void MergeStructValue(IMessage message, JsonTokenizer tokenizer) |
| 392 { | 427 { |
| 393 var firstToken = tokenizer.Next(); | 428 var firstToken = tokenizer.Next(); |
| 394 var fields = message.Descriptor.Fields; | 429 var fields = message.Descriptor.Fields; |
| 395 switch (firstToken.Type) | 430 switch (firstToken.Type) |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 457 int typeUrlObjectDepth = tokenizer.ObjectDepth; | 492 int typeUrlObjectDepth = tokenizer.ObjectDepth; |
| 458 | 493 |
| 459 // The check for the property depth protects us from nested Any valu
es which occur before the type URL | 494 // The check for the property depth protects us from nested Any valu
es which occur before the type URL |
| 460 // for *this* Any. | 495 // for *this* Any. |
| 461 while (token.Type != JsonToken.TokenType.Name || | 496 while (token.Type != JsonToken.TokenType.Name || |
| 462 token.StringValue != JsonFormatter.AnyTypeUrlField || | 497 token.StringValue != JsonFormatter.AnyTypeUrlField || |
| 463 tokenizer.ObjectDepth != typeUrlObjectDepth) | 498 tokenizer.ObjectDepth != typeUrlObjectDepth) |
| 464 { | 499 { |
| 465 tokens.Add(token); | 500 tokens.Add(token); |
| 466 token = tokenizer.Next(); | 501 token = tokenizer.Next(); |
| 502 |
| 503 if (tokenizer.ObjectDepth < typeUrlObjectDepth) |
| 504 { |
| 505 throw new InvalidProtocolBufferException("Any message with n
o @type"); |
| 506 } |
| 467 } | 507 } |
| 468 | 508 |
| 469 // Don't add the @type property or its value to the recorded token l
ist | 509 // Don't add the @type property or its value to the recorded token l
ist |
| 470 token = tokenizer.Next(); | 510 token = tokenizer.Next(); |
| 471 if (token.Type != JsonToken.TokenType.StringValue) | 511 if (token.Type != JsonToken.TokenType.StringValue) |
| 472 { | 512 { |
| 473 throw new InvalidProtocolBufferException("Expected string value
for Any.@type"); | 513 throw new InvalidProtocolBufferException("Expected string value
for Any.@type"); |
| 474 } | 514 } |
| 475 string typeUrl = token.StringValue; | 515 string typeUrl = token.StringValue; |
| 476 string typeName = JsonFormatter.GetTypeName(typeUrl); | 516 string typeName = Any.GetTypeName(typeUrl); |
| 477 | 517 |
| 478 MessageDescriptor descriptor = settings.TypeRegistry.Find(typeName); | 518 MessageDescriptor descriptor = settings.TypeRegistry.Find(typeName); |
| 479 if (descriptor == null) | 519 if (descriptor == null) |
| 480 { | 520 { |
| 481 throw new InvalidOperationException($"Type registry has no descr
iptor for type name '{typeName}'"); | 521 throw new InvalidOperationException($"Type registry has no descr
iptor for type name '{typeName}'"); |
| 482 } | 522 } |
| 483 | 523 |
| 484 // Now replay the token stream we've already read and anything that
remains of the object, just parsing it | 524 // Now replay the token stream we've already read and anything that
remains of the object, just parsing it |
| 485 // as normal. Our original tokenizer should end up at the end of the
object. | 525 // as normal. Our original tokenizer should end up at the end of the
object. |
| 486 var replay = JsonTokenizer.FromReplayedTokens(tokens, tokenizer); | 526 var replay = JsonTokenizer.FromReplayedTokens(tokens, tokenizer); |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 533 if (keyText == "false") | 573 if (keyText == "false") |
| 534 { | 574 { |
| 535 return false; | 575 return false; |
| 536 } | 576 } |
| 537 throw new InvalidProtocolBufferException("Invalid string for
bool map key: " + keyText); | 577 throw new InvalidProtocolBufferException("Invalid string for
bool map key: " + keyText); |
| 538 case FieldType.String: | 578 case FieldType.String: |
| 539 return keyText; | 579 return keyText; |
| 540 case FieldType.Int32: | 580 case FieldType.Int32: |
| 541 case FieldType.SInt32: | 581 case FieldType.SInt32: |
| 542 case FieldType.SFixed32: | 582 case FieldType.SFixed32: |
| 543 return ParseNumericString(keyText, int.Parse, false); | 583 return ParseNumericString(keyText, int.Parse); |
| 544 case FieldType.UInt32: | 584 case FieldType.UInt32: |
| 545 case FieldType.Fixed32: | 585 case FieldType.Fixed32: |
| 546 return ParseNumericString(keyText, uint.Parse, false); | 586 return ParseNumericString(keyText, uint.Parse); |
| 547 case FieldType.Int64: | 587 case FieldType.Int64: |
| 548 case FieldType.SInt64: | 588 case FieldType.SInt64: |
| 549 case FieldType.SFixed64: | 589 case FieldType.SFixed64: |
| 550 return ParseNumericString(keyText, long.Parse, false); | 590 return ParseNumericString(keyText, long.Parse); |
| 551 case FieldType.UInt64: | 591 case FieldType.UInt64: |
| 552 case FieldType.Fixed64: | 592 case FieldType.Fixed64: |
| 553 return ParseNumericString(keyText, ulong.Parse, false); | 593 return ParseNumericString(keyText, ulong.Parse); |
| 554 default: | 594 default: |
| 555 throw new InvalidProtocolBufferException("Invalid field type
for map: " + field.FieldType); | 595 throw new InvalidProtocolBufferException("Invalid field type
for map: " + field.FieldType); |
| 556 } | 596 } |
| 557 } | 597 } |
| 558 | 598 |
| 559 private static object ParseSingleNumberValue(FieldDescriptor field, Json
Token token) | 599 private static object ParseSingleNumberValue(FieldDescriptor field, Json
Token token) |
| 560 { | 600 { |
| 561 double value = token.NumberValue; | 601 double value = token.NumberValue; |
| 562 checked | 602 checked |
| 563 { | 603 { |
| 564 // TODO: Validate that it's actually an integer, possibly in ter
ms of the textual representation? | |
| 565 try | 604 try |
| 566 { | 605 { |
| 567 switch (field.FieldType) | 606 switch (field.FieldType) |
| 568 { | 607 { |
| 569 case FieldType.Int32: | 608 case FieldType.Int32: |
| 570 case FieldType.SInt32: | 609 case FieldType.SInt32: |
| 571 case FieldType.SFixed32: | 610 case FieldType.SFixed32: |
| 611 CheckInteger(value); |
| 572 return (int) value; | 612 return (int) value; |
| 573 case FieldType.UInt32: | 613 case FieldType.UInt32: |
| 574 case FieldType.Fixed32: | 614 case FieldType.Fixed32: |
| 615 CheckInteger(value); |
| 575 return (uint) value; | 616 return (uint) value; |
| 576 case FieldType.Int64: | 617 case FieldType.Int64: |
| 577 case FieldType.SInt64: | 618 case FieldType.SInt64: |
| 578 case FieldType.SFixed64: | 619 case FieldType.SFixed64: |
| 620 CheckInteger(value); |
| 579 return (long) value; | 621 return (long) value; |
| 580 case FieldType.UInt64: | 622 case FieldType.UInt64: |
| 581 case FieldType.Fixed64: | 623 case FieldType.Fixed64: |
| 624 CheckInteger(value); |
| 582 return (ulong) value; | 625 return (ulong) value; |
| 583 case FieldType.Double: | 626 case FieldType.Double: |
| 584 return value; | 627 return value; |
| 585 case FieldType.Float: | 628 case FieldType.Float: |
| 586 if (double.IsNaN(value)) | 629 if (double.IsNaN(value)) |
| 587 { | 630 { |
| 588 return float.NaN; | 631 return float.NaN; |
| 589 } | 632 } |
| 590 if (value > float.MaxValue || value < float.MinValue
) | 633 if (value > float.MaxValue || value < float.MinValue
) |
| 591 { | 634 { |
| 592 if (double.IsPositiveInfinity(value)) | 635 if (double.IsPositiveInfinity(value)) |
| 593 { | 636 { |
| 594 return float.PositiveInfinity; | 637 return float.PositiveInfinity; |
| 595 } | 638 } |
| 596 if (double.IsNegativeInfinity(value)) | 639 if (double.IsNegativeInfinity(value)) |
| 597 { | 640 { |
| 598 return float.NegativeInfinity; | 641 return float.NegativeInfinity; |
| 599 } | 642 } |
| 600 throw new InvalidProtocolBufferException("Value
out of range: " + value); | 643 throw new InvalidProtocolBufferException($"Value
out of range: {value}"); |
| 601 } | 644 } |
| 602 return (float) value; | 645 return (float) value; |
| 646 case FieldType.Enum: |
| 647 CheckInteger(value); |
| 648 // Just return it as an int, and let the CLR convert
it. |
| 649 // Note that we deliberately don't check that it's a
known value. |
| 650 return (int) value; |
| 603 default: | 651 default: |
| 604 throw new InvalidProtocolBufferException("Unsupporte
d conversion from JSON number for field type " + field.FieldType); | 652 throw new InvalidProtocolBufferException($"Unsupport
ed conversion from JSON number for field type {field.FieldType}"); |
| 605 } | 653 } |
| 606 } | 654 } |
| 607 catch (OverflowException) | 655 catch (OverflowException) |
| 608 { | 656 { |
| 609 throw new InvalidProtocolBufferException("Value out of range
: " + value); | 657 throw new InvalidProtocolBufferException($"Value out of rang
e: {value}"); |
| 610 } | 658 } |
| 611 } | 659 } |
| 612 } | 660 } |
| 613 | 661 |
| 662 private static void CheckInteger(double value) |
| 663 { |
| 664 if (double.IsInfinity(value) || double.IsNaN(value)) |
| 665 { |
| 666 throw new InvalidProtocolBufferException($"Value not an integer:
{value}"); |
| 667 } |
| 668 if (value != Math.Floor(value)) |
| 669 { |
| 670 throw new InvalidProtocolBufferException($"Value not an integer:
{value}"); |
| 671 } |
| 672 } |
| 673 |
| 614 private static object ParseSingleStringValue(FieldDescriptor field, stri
ng text) | 674 private static object ParseSingleStringValue(FieldDescriptor field, stri
ng text) |
| 615 { | 675 { |
| 616 switch (field.FieldType) | 676 switch (field.FieldType) |
| 617 { | 677 { |
| 618 case FieldType.String: | 678 case FieldType.String: |
| 619 return text; | 679 return text; |
| 620 case FieldType.Bytes: | 680 case FieldType.Bytes: |
| 621 return ByteString.FromBase64(text); | 681 try |
| 682 { |
| 683 return ByteString.FromBase64(text); |
| 684 } |
| 685 catch (FormatException e) |
| 686 { |
| 687 throw InvalidProtocolBufferException.InvalidBase64(e); |
| 688 } |
| 622 case FieldType.Int32: | 689 case FieldType.Int32: |
| 623 case FieldType.SInt32: | 690 case FieldType.SInt32: |
| 624 case FieldType.SFixed32: | 691 case FieldType.SFixed32: |
| 625 return ParseNumericString(text, int.Parse, false); | 692 return ParseNumericString(text, int.Parse); |
| 626 case FieldType.UInt32: | 693 case FieldType.UInt32: |
| 627 case FieldType.Fixed32: | 694 case FieldType.Fixed32: |
| 628 return ParseNumericString(text, uint.Parse, false); | 695 return ParseNumericString(text, uint.Parse); |
| 629 case FieldType.Int64: | 696 case FieldType.Int64: |
| 630 case FieldType.SInt64: | 697 case FieldType.SInt64: |
| 631 case FieldType.SFixed64: | 698 case FieldType.SFixed64: |
| 632 return ParseNumericString(text, long.Parse, false); | 699 return ParseNumericString(text, long.Parse); |
| 633 case FieldType.UInt64: | 700 case FieldType.UInt64: |
| 634 case FieldType.Fixed64: | 701 case FieldType.Fixed64: |
| 635 return ParseNumericString(text, ulong.Parse, false); | 702 return ParseNumericString(text, ulong.Parse); |
| 636 case FieldType.Double: | 703 case FieldType.Double: |
| 637 double d = ParseNumericString(text, double.Parse, true); | 704 double d = ParseNumericString(text, double.Parse); |
| 638 // double.Parse can return +/- infinity on Mono for non-infi
nite values which are out of range for double. | 705 ValidateInfinityAndNan(text, double.IsPositiveInfinity(d), d
ouble.IsNegativeInfinity(d), double.IsNaN(d)); |
| 639 if (double.IsInfinity(d) && !text.Contains("Infinity")) | |
| 640 { | |
| 641 throw new InvalidProtocolBufferException("Invalid numeri
c value: " + text); | |
| 642 } | |
| 643 return d; | 706 return d; |
| 644 case FieldType.Float: | 707 case FieldType.Float: |
| 645 float f = ParseNumericString(text, float.Parse, true); | 708 float f = ParseNumericString(text, float.Parse); |
| 646 // float.Parse can return +/- infinity on Mono for non-infin
ite values which are out of range for float. | 709 ValidateInfinityAndNan(text, float.IsPositiveInfinity(f), fl
oat.IsNegativeInfinity(f), float.IsNaN(f)); |
| 647 if (float.IsInfinity(f) && !text.Contains("Infinity")) | |
| 648 { | |
| 649 throw new InvalidProtocolBufferException("Invalid numeri
c value: " + text); | |
| 650 } | |
| 651 return f; | 710 return f; |
| 652 case FieldType.Enum: | 711 case FieldType.Enum: |
| 653 var enumValue = field.EnumType.FindValueByName(text); | 712 var enumValue = field.EnumType.FindValueByName(text); |
| 654 if (enumValue == null) | 713 if (enumValue == null) |
| 655 { | 714 { |
| 656 throw new InvalidProtocolBufferException("Invalid enum v
alue: " + text + " for enum type: " + field.EnumType.FullName); | 715 throw new InvalidProtocolBufferException($"Invalid enum
value: {text} for enum type: {field.EnumType.FullName}"); |
| 657 } | 716 } |
| 658 // Just return it as an int, and let the CLR convert it. | 717 // Just return it as an int, and let the CLR convert it. |
| 659 return enumValue.Number; | 718 return enumValue.Number; |
| 660 default: | 719 default: |
| 661 throw new InvalidProtocolBufferException("Unsupported conver
sion from JSON string for field type " + field.FieldType); | 720 throw new InvalidProtocolBufferException($"Unsupported conve
rsion from JSON string for field type {field.FieldType}"); |
| 662 } | 721 } |
| 663 } | 722 } |
| 664 | 723 |
| 665 /// <summary> | 724 /// <summary> |
| 666 /// Creates a new instance of the message type for the given field. | 725 /// Creates a new instance of the message type for the given field. |
| 667 /// </summary> | 726 /// </summary> |
| 668 private static IMessage NewMessageForField(FieldDescriptor field) | 727 private static IMessage NewMessageForField(FieldDescriptor field) |
| 669 { | 728 { |
| 670 return field.MessageType.Parser.CreateTemplate(); | 729 return field.MessageType.Parser.CreateTemplate(); |
| 671 } | 730 } |
| 672 | 731 |
| 673 private static T ParseNumericString<T>(string text, Func<string, NumberS
tyles, IFormatProvider, T> parser, bool floatingPoint) | 732 private static T ParseNumericString<T>(string text, Func<string, NumberS
tyles, IFormatProvider, T> parser) |
| 674 { | 733 { |
| 675 // TODO: Prohibit leading zeroes (but allow 0!) | |
| 676 // TODO: Validate handling of "Infinity" etc. (Should be case sensit
ive, no leading whitespace etc) | |
| 677 // Can't prohibit this with NumberStyles. | 734 // Can't prohibit this with NumberStyles. |
| 678 if (text.StartsWith("+")) | 735 if (text.StartsWith("+")) |
| 679 { | 736 { |
| 680 throw new InvalidProtocolBufferException("Invalid numeric value:
" + text); | 737 throw new InvalidProtocolBufferException($"Invalid numeric value
: {text}"); |
| 681 } | 738 } |
| 682 if (text.StartsWith("0") && text.Length > 1) | 739 if (text.StartsWith("0") && text.Length > 1) |
| 683 { | 740 { |
| 684 if (text[1] >= '0' && text[1] <= '9') | 741 if (text[1] >= '0' && text[1] <= '9') |
| 685 { | 742 { |
| 686 throw new InvalidProtocolBufferException("Invalid numeric va
lue: " + text); | 743 throw new InvalidProtocolBufferException($"Invalid numeric v
alue: {text}"); |
| 687 } | 744 } |
| 688 } | 745 } |
| 689 else if (text.StartsWith("-0") && text.Length > 2) | 746 else if (text.StartsWith("-0") && text.Length > 2) |
| 690 { | 747 { |
| 691 if (text[2] >= '0' && text[2] <= '9') | 748 if (text[2] >= '0' && text[2] <= '9') |
| 692 { | 749 { |
| 693 throw new InvalidProtocolBufferException("Invalid numeric va
lue: " + text); | 750 throw new InvalidProtocolBufferException($"Invalid numeric v
alue: {text}"); |
| 694 } | 751 } |
| 695 } | 752 } |
| 696 try | 753 try |
| 697 { | 754 { |
| 698 var styles = floatingPoint | 755 return parser(text, NumberStyles.AllowLeadingSign | NumberStyles
.AllowDecimalPoint | NumberStyles.AllowExponent, CultureInfo.InvariantCulture); |
| 699 ? NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalP
oint | NumberStyles.AllowExponent | |
| 700 : NumberStyles.AllowLeadingSign; | |
| 701 return parser(text, styles, CultureInfo.InvariantCulture); | |
| 702 } | 756 } |
| 703 catch (FormatException) | 757 catch (FormatException) |
| 704 { | 758 { |
| 705 throw new InvalidProtocolBufferException("Invalid numeric value
for type: " + text); | 759 throw new InvalidProtocolBufferException($"Invalid numeric value
for type: {text}"); |
| 706 } | 760 } |
| 707 catch (OverflowException) | 761 catch (OverflowException) |
| 708 { | 762 { |
| 709 throw new InvalidProtocolBufferException("Value out of range: "
+ text); | 763 throw new InvalidProtocolBufferException($"Value out of range: {
text}"); |
| 710 } | 764 } |
| 711 } | 765 } |
| 712 | 766 |
| 767 /// <summary> |
| 768 /// Checks that any infinite/NaN values originated from the correct text
. |
| 769 /// This corrects the lenient whitespace handling of double.Parse/float.
Parse, as well as the |
| 770 /// way that Mono parses out-of-range values as infinity. |
| 771 /// </summary> |
| 772 private static void ValidateInfinityAndNan(string text, bool isPositiveI
nfinity, bool isNegativeInfinity, bool isNaN) |
| 773 { |
| 774 if ((isPositiveInfinity && text != "Infinity") || |
| 775 (isNegativeInfinity && text != "-Infinity") || |
| 776 (isNaN && text != "NaN")) |
| 777 { |
| 778 throw new InvalidProtocolBufferException($"Invalid numeric value
: {text}"); |
| 779 } |
| 780 } |
| 781 |
| 713 private static void MergeTimestamp(IMessage message, JsonToken token) | 782 private static void MergeTimestamp(IMessage message, JsonToken token) |
| 714 { | 783 { |
| 715 if (token.Type != JsonToken.TokenType.StringValue) | 784 if (token.Type != JsonToken.TokenType.StringValue) |
| 716 { | 785 { |
| 717 throw new InvalidProtocolBufferException("Expected string value
for Timestamp"); | 786 throw new InvalidProtocolBufferException("Expected string value
for Timestamp"); |
| 718 } | 787 } |
| 719 var match = TimestampRegex.Match(token.StringValue); | 788 var match = TimestampRegex.Match(token.StringValue); |
| 720 if (!match.Success) | 789 if (!match.Success) |
| 721 { | 790 { |
| 722 throw new InvalidProtocolBufferException("Invalid Timestamp valu
e: " + token.StringValue); | 791 throw new InvalidProtocolBufferException($"Invalid Timestamp val
ue: {token.StringValue}"); |
| 723 } | 792 } |
| 724 var dateTime = match.Groups["datetime"].Value; | 793 var dateTime = match.Groups["datetime"].Value; |
| 725 var subseconds = match.Groups["subseconds"].Value; | 794 var subseconds = match.Groups["subseconds"].Value; |
| 726 var offset = match.Groups["offset"].Value; | 795 var offset = match.Groups["offset"].Value; |
| 727 | 796 |
| 728 try | 797 try |
| 729 { | 798 { |
| 730 DateTime parsed = DateTime.ParseExact( | 799 DateTime parsed = DateTime.ParseExact( |
| 731 dateTime, | 800 dateTime, |
| 732 "yyyy-MM-dd'T'HH:mm:ss", | 801 "yyyy-MM-dd'T'HH:mm:ss", |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 802 // Prohibit leading insignficant zeroes | 871 // Prohibit leading insignficant zeroes |
| 803 if (secondsText[0] == '0' && secondsText.Length > 1) | 872 if (secondsText[0] == '0' && secondsText.Length > 1) |
| 804 { | 873 { |
| 805 throw new InvalidProtocolBufferException("Invalid Duration value
: " + token.StringValue); | 874 throw new InvalidProtocolBufferException("Invalid Duration value
: " + token.StringValue); |
| 806 } | 875 } |
| 807 var subseconds = match.Groups["subseconds"].Value; | 876 var subseconds = match.Groups["subseconds"].Value; |
| 808 var multiplier = sign == "-" ? -1 : 1; | 877 var multiplier = sign == "-" ? -1 : 1; |
| 809 | 878 |
| 810 try | 879 try |
| 811 { | 880 { |
| 812 long seconds = long.Parse(secondsText, CultureInfo.InvariantCult
ure); | 881 long seconds = long.Parse(secondsText, CultureInfo.InvariantCult
ure) * multiplier; |
| 813 int nanos = 0; | 882 int nanos = 0; |
| 814 if (subseconds != "") | 883 if (subseconds != "") |
| 815 { | 884 { |
| 816 // This should always work, as we've got 1-9 digits. | 885 // This should always work, as we've got 1-9 digits. |
| 817 int parsedFraction = int.Parse(subseconds.Substring(1)); | 886 int parsedFraction = int.Parse(subseconds.Substring(1)); |
| 818 nanos = parsedFraction * SubsecondScalingFactors[subseconds.
Length]; | 887 nanos = parsedFraction * SubsecondScalingFactors[subseconds.
Length] * multiplier; |
| 819 } | 888 } |
| 820 if (seconds >= Duration.MaxSeconds) | 889 if (!Duration.IsNormalized(seconds, nanos)) |
| 821 { | 890 { |
| 822 // Allow precisely 315576000000 seconds, but prohibit even 1
ns more. | 891 throw new InvalidProtocolBufferException($"Invalid Duration
value: {token.StringValue}"); |
| 823 if (seconds > Duration.MaxSeconds || nanos > 0) | |
| 824 { | |
| 825 throw new InvalidProtocolBufferException("Invalid Durati
on value: " + token.StringValue); | |
| 826 } | |
| 827 } | 892 } |
| 828 message.Descriptor.Fields[Duration.SecondsFieldNumber].Accessor.
SetValue(message, seconds * multiplier); | 893 message.Descriptor.Fields[Duration.SecondsFieldNumber].Accessor.
SetValue(message, seconds); |
| 829 message.Descriptor.Fields[Duration.NanosFieldNumber].Accessor.Se
tValue(message, nanos * multiplier); | 894 message.Descriptor.Fields[Duration.NanosFieldNumber].Accessor.Se
tValue(message, nanos); |
| 830 } | 895 } |
| 831 catch (FormatException) | 896 catch (FormatException) |
| 832 { | 897 { |
| 833 throw new InvalidProtocolBufferException("Invalid Duration value
: " + token.StringValue); | 898 throw new InvalidProtocolBufferException($"Invalid Duration valu
e: {token.StringValue}"); |
| 834 } | 899 } |
| 835 } | 900 } |
| 836 | 901 |
| 837 private static void MergeFieldMask(IMessage message, JsonToken token) | 902 private static void MergeFieldMask(IMessage message, JsonToken token) |
| 838 { | 903 { |
| 839 if (token.Type != JsonToken.TokenType.StringValue) | 904 if (token.Type != JsonToken.TokenType.StringValue) |
| 840 { | 905 { |
| 841 throw new InvalidProtocolBufferException("Expected string value
for FieldMask"); | 906 throw new InvalidProtocolBufferException("Expected string value
for FieldMask"); |
| 842 } | 907 } |
| 843 // TODO: Do we *want* to remove empty entries? Probably okay to trea
t "" as "no paths", but "foo,,bar"? | 908 // TODO: Do we *want* to remove empty entries? Probably okay to trea
t "" as "no paths", but "foo,,bar"? |
| 844 string[] jsonPaths = token.StringValue.Split(FieldMaskPathSeparators
, StringSplitOptions.RemoveEmptyEntries); | 909 string[] jsonPaths = token.StringValue.Split(FieldMaskPathSeparators
, StringSplitOptions.RemoveEmptyEntries); |
| 845 IList messagePaths = (IList) message.Descriptor.Fields[FieldMask.Pat
hsFieldNumber].Accessor.GetValue(message); | 910 IList messagePaths = (IList) message.Descriptor.Fields[FieldMask.Pat
hsFieldNumber].Accessor.GetValue(message); |
| 846 foreach (var path in jsonPaths) | 911 foreach (var path in jsonPaths) |
| 847 { | 912 { |
| 848 messagePaths.Add(ToSnakeCase(path)); | 913 messagePaths.Add(ToSnakeCase(path)); |
| 849 } | 914 } |
| 850 } | 915 } |
| 851 | 916 |
| 852 // Ported from src/google/protobuf/util/internal/utility.cc | 917 // Ported from src/google/protobuf/util/internal/utility.cc |
| 853 private static string ToSnakeCase(string text) | 918 private static string ToSnakeCase(string text) |
| 854 { | 919 { |
| 855 var builder = new StringBuilder(text.Length * 2); | 920 var builder = new StringBuilder(text.Length * 2); |
| 921 // Note: this is probably unnecessary now, but currently retained to
be as close as possible to the |
| 922 // C++, whilst still throwing an exception on underscores. |
| 856 bool wasNotUnderscore = false; // Initialize to false for case 1 (b
elow) | 923 bool wasNotUnderscore = false; // Initialize to false for case 1 (b
elow) |
| 857 bool wasNotCap = false; | 924 bool wasNotCap = false; |
| 858 | 925 |
| 859 for (int i = 0; i < text.Length; i++) | 926 for (int i = 0; i < text.Length; i++) |
| 860 { | 927 { |
| 861 char c = text[i]; | 928 char c = text[i]; |
| 862 if (c >= 'A' && c <= 'Z') // ascii_isupper | 929 if (c >= 'A' && c <= 'Z') // ascii_isupper |
| 863 { | 930 { |
| 864 // Consider when the current character B is capitalized: | 931 // Consider when the current character B is capitalized: |
| 865 // 1) At beginning of input: "B..." => "b..." | 932 // 1) At beginning of input: "B..." => "b..." |
| (...skipping 13 matching lines...) Expand all Loading... |
| 879 builder.Append('_'); | 946 builder.Append('_'); |
| 880 } | 947 } |
| 881 // ascii_tolower, but we already know that c *is* an upper c
ase ASCII character... | 948 // ascii_tolower, but we already know that c *is* an upper c
ase ASCII character... |
| 882 builder.Append((char) (c + 'a' - 'A')); | 949 builder.Append((char) (c + 'a' - 'A')); |
| 883 wasNotUnderscore = true; | 950 wasNotUnderscore = true; |
| 884 wasNotCap = false; | 951 wasNotCap = false; |
| 885 } | 952 } |
| 886 else | 953 else |
| 887 { | 954 { |
| 888 builder.Append(c); | 955 builder.Append(c); |
| 889 wasNotUnderscore = c != '_'; | 956 if (c == '_') |
| 957 { |
| 958 throw new InvalidProtocolBufferException($"Invalid field
mask: {text}"); |
| 959 } |
| 960 wasNotUnderscore = true; |
| 890 wasNotCap = true; | 961 wasNotCap = true; |
| 891 } | 962 } |
| 892 } | 963 } |
| 893 return builder.ToString(); | 964 return builder.ToString(); |
| 894 } | 965 } |
| 895 #endregion | 966 #endregion |
| 896 | 967 |
| 897 /// <summary> | 968 /// <summary> |
| 898 /// Settings controlling JSON parsing. | 969 /// Settings controlling JSON parsing. |
| 899 /// </summary> | 970 /// </summary> |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 933 } | 1004 } |
| 934 | 1005 |
| 935 /// <summary> | 1006 /// <summary> |
| 936 /// Creates a new <see cref="Settings"/> object with the specified r
ecursion limit and type registry. | 1007 /// Creates a new <see cref="Settings"/> object with the specified r
ecursion limit and type registry. |
| 937 /// </summary> | 1008 /// </summary> |
| 938 /// <param name="recursionLimit">The maximum depth of messages to pa
rse</param> | 1009 /// <param name="recursionLimit">The maximum depth of messages to pa
rse</param> |
| 939 /// <param name="typeRegistry">The type registry used to parse <see
cref="Any"/> messages</param> | 1010 /// <param name="typeRegistry">The type registry used to parse <see
cref="Any"/> messages</param> |
| 940 public Settings(int recursionLimit, TypeRegistry typeRegistry) | 1011 public Settings(int recursionLimit, TypeRegistry typeRegistry) |
| 941 { | 1012 { |
| 942 RecursionLimit = recursionLimit; | 1013 RecursionLimit = recursionLimit; |
| 943 TypeRegistry = Preconditions.CheckNotNull(typeRegistry, nameof(t
ypeRegistry)); | 1014 TypeRegistry = ProtoPreconditions.CheckNotNull(typeRegistry, nam
eof(typeRegistry)); |
| 944 } | 1015 } |
| 945 } | 1016 } |
| 946 } | 1017 } |
| 947 } | 1018 } |
| OLD | NEW |