1 #region License 2 // Copyright (c) 2007 James Newton-King 3 // 4 // Permission is hereby granted, free of charge, to any person 5 // obtaining a copy of this software and associated documentation 6 // files (the "Software"), to deal in the Software without 7 // restriction, including without limitation the rights to use, 8 // copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the 10 // Software is furnished to do so, subject to the following 11 // conditions: 12 // 13 // The above copyright notice and this permission notice shall be 14 // included in all copies or substantial portions of the Software. 15 // 16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 20 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 // OTHER DEALINGS IN THE SOFTWARE. 24 #endregion 25 26 using System; 27 using System.Collections; 28 using System.Collections.Generic; 29 using System.Collections.ObjectModel; 30 #if !(NET35 || NET20 || WINDOWS_PHONE) 31 using System.Dynamic; 32 #endif 33 using System.Globalization; 34 using System.Reflection; 35 using System.Runtime.Serialization; 36 using Newtonsoft.Json.Linq; 37 using Newtonsoft.Json.Utilities; 38 #if NET20 39 using Newtonsoft.Json.Utilities.LinqBridge; 40 #else 41 using System.Linq; 42 #endif 43 44 namespace Newtonsoft.Json.Serialization 45 { 46 internal class JsonSerializerInternalReader : JsonSerializerInternalBase 47 { 48 private JsonSerializerProxy _internalSerializer; 49 #if !SILVERLIGHT && !PocketPC && !NETFX_CORE 50 private JsonFormatterConverter _formatterConverter; 51 #endif 52 JsonSerializerInternalReader(JsonSerializer serializer)53 public JsonSerializerInternalReader(JsonSerializer serializer) 54 : base(serializer) 55 { 56 } 57 Populate(JsonReader reader, object target)58 public void Populate(JsonReader reader, object target) 59 { 60 ValidationUtils.ArgumentNotNull(target, "target"); 61 62 Type objectType = target.GetType(); 63 64 JsonContract contract = Serializer.ContractResolver.ResolveContract(objectType); 65 66 if (reader.TokenType == JsonToken.None) 67 reader.Read(); 68 69 if (reader.TokenType == JsonToken.StartArray) 70 { 71 if (contract.ContractType == JsonContractType.Array) 72 PopulateList(CollectionUtils.CreateCollectionWrapper(target), reader, null, (JsonArrayContract) contract); 73 else 74 throw CreateSerializationException(reader, "Cannot populate JSON array onto type '{0}'.".FormatWith(CultureInfo.InvariantCulture, objectType)); 75 } 76 else if (reader.TokenType == JsonToken.StartObject) 77 { 78 CheckedRead(reader); 79 80 string id = null; 81 if (reader.TokenType == JsonToken.PropertyName && string.Equals(reader.Value.ToString(), JsonTypeReflector.IdPropertyName, StringComparison.Ordinal)) 82 { 83 CheckedRead(reader); 84 id = (reader.Value != null) ? reader.Value.ToString() : null; 85 CheckedRead(reader); 86 } 87 88 if (contract.ContractType == JsonContractType.Dictionary) 89 PopulateDictionary(CollectionUtils.CreateDictionaryWrapper(target), reader, (JsonDictionaryContract) contract, id); 90 else if (contract.ContractType == JsonContractType.Object) 91 PopulateObject(target, reader, (JsonObjectContract) contract, id); 92 else 93 throw CreateSerializationException(reader, "Cannot populate JSON object onto type '{0}'.".FormatWith(CultureInfo.InvariantCulture, objectType)); 94 } 95 else 96 { 97 throw CreateSerializationException(reader, "Unexpected initial token '{0}' when populating object. Expected JSON object or array.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); 98 } 99 } 100 GetContractSafe(Type type)101 private JsonContract GetContractSafe(Type type) 102 { 103 if (type == null) 104 return null; 105 106 return Serializer.ContractResolver.ResolveContract(type); 107 } 108 Deserialize(JsonReader reader, Type objectType)109 public object Deserialize(JsonReader reader, Type objectType) 110 { 111 if (reader == null) 112 throw new ArgumentNullException("reader"); 113 114 JsonContract contract = GetContractSafe(objectType); 115 116 JsonConverter converter = GetConverter(contract, null); 117 118 if (reader.TokenType == JsonToken.None && !ReadForType(reader, contract, converter != null, false)) 119 { 120 if (!contract.IsNullable) 121 throw new JsonSerializationException("No JSON content found and type '{0}' is not nullable.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); 122 123 return null; 124 } 125 126 return CreateValueNonProperty(reader, objectType, contract, converter); 127 } 128 GetInternalSerializer()129 private JsonSerializerProxy GetInternalSerializer() 130 { 131 if (_internalSerializer == null) 132 _internalSerializer = new JsonSerializerProxy(this); 133 134 return _internalSerializer; 135 } 136 137 #if !SILVERLIGHT && !PocketPC && !NETFX_CORE GetFormatterConverter()138 private JsonFormatterConverter GetFormatterConverter() 139 { 140 if (_formatterConverter == null) 141 _formatterConverter = new JsonFormatterConverter(GetInternalSerializer()); 142 143 return _formatterConverter; 144 } 145 #endif 146 CreateJToken(JsonReader reader, JsonContract contract)147 private JToken CreateJToken(JsonReader reader, JsonContract contract) 148 { 149 ValidationUtils.ArgumentNotNull(reader, "reader"); 150 151 if (contract != null && contract.UnderlyingType == typeof (JRaw)) 152 { 153 return JRaw.Create(reader); 154 } 155 else 156 { 157 JToken token; 158 using (JTokenWriter writer = new JTokenWriter()) 159 { 160 writer.WriteToken(reader); 161 token = writer.Token; 162 } 163 164 return token; 165 } 166 } 167 CreateJObject(JsonReader reader)168 private JToken CreateJObject(JsonReader reader) 169 { 170 ValidationUtils.ArgumentNotNull(reader, "reader"); 171 172 // this is needed because we've already read inside the object, looking for special properties 173 JToken token; 174 using (JTokenWriter writer = new JTokenWriter()) 175 { 176 writer.WriteStartObject(); 177 178 if (reader.TokenType == JsonToken.PropertyName) 179 writer.WriteToken(reader, reader.Depth - 1); 180 else 181 writer.WriteEndObject(); 182 183 token = writer.Token; 184 } 185 186 return token; 187 } 188 CreateValueProperty(JsonReader reader, JsonProperty property, JsonConverter propertyConverter, object target, bool gottenCurrentValue, object currentValue)189 private object CreateValueProperty(JsonReader reader, JsonProperty property, JsonConverter propertyConverter, object target, bool gottenCurrentValue, object currentValue) 190 { 191 JsonContract contract; 192 JsonConverter converter; 193 194 if (property.PropertyContract == null) 195 property.PropertyContract = GetContractSafe(property.PropertyType); 196 197 if (currentValue == null) 198 { 199 contract = property.PropertyContract; 200 converter = propertyConverter; 201 } 202 else 203 { 204 contract = GetContractSafe(currentValue.GetType()); 205 206 if (contract != property.PropertyContract) 207 converter = GetConverter(contract, property.MemberConverter); 208 else 209 converter = propertyConverter; 210 } 211 212 Type objectType = property.PropertyType; 213 214 if (converter != null && converter.CanRead) 215 { 216 if (!gottenCurrentValue && target != null && property.Readable) 217 currentValue = property.ValueProvider.GetValue(target); 218 219 return converter.ReadJson(reader, objectType, currentValue, GetInternalSerializer()); 220 } 221 222 return CreateValueInternal(reader, objectType, contract, property, currentValue); 223 } 224 CreateValueNonProperty(JsonReader reader, Type objectType, JsonContract contract, JsonConverter converter)225 private object CreateValueNonProperty(JsonReader reader, Type objectType, JsonContract contract, JsonConverter converter) 226 { 227 if (converter != null && converter.CanRead) 228 return converter.ReadJson(reader, objectType, null, GetInternalSerializer()); 229 230 return CreateValueInternal(reader, objectType, contract, null, null); 231 } 232 CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, object existingValue)233 private object CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, object existingValue) 234 { 235 if (contract != null && contract.ContractType == JsonContractType.Linq) 236 return CreateJToken(reader, contract); 237 238 do 239 { 240 switch (reader.TokenType) 241 { 242 // populate a typed object or generic dictionary/array 243 // depending upon whether an objectType was supplied 244 case JsonToken.StartObject: 245 return CreateObject(reader, objectType, contract, member, existingValue); 246 case JsonToken.StartArray: 247 return CreateList(reader, objectType, contract, member, existingValue, null); 248 case JsonToken.Integer: 249 case JsonToken.Float: 250 case JsonToken.Boolean: 251 case JsonToken.Date: 252 case JsonToken.Bytes: 253 return EnsureType(reader, reader.Value, CultureInfo.InvariantCulture, contract, objectType); 254 case JsonToken.String: 255 // convert empty string to null automatically for nullable types 256 if (string.IsNullOrEmpty((string)reader.Value) && objectType != typeof(string) && objectType != typeof(object) && contract.IsNullable) 257 return null; 258 259 // string that needs to be returned as a byte array should be base 64 decoded 260 if (objectType == typeof (byte[])) 261 return Convert.FromBase64String((string) reader.Value); 262 263 return EnsureType(reader, reader.Value, CultureInfo.InvariantCulture, contract, objectType); 264 case JsonToken.StartConstructor: 265 case JsonToken.EndConstructor: 266 string constructorName = reader.Value.ToString(); 267 268 return constructorName; 269 case JsonToken.Null: 270 case JsonToken.Undefined: 271 #if !NETFX_CORE 272 if (objectType == typeof (DBNull)) 273 return DBNull.Value; 274 #endif 275 276 return EnsureType(reader, reader.Value, CultureInfo.InvariantCulture, contract, objectType); 277 case JsonToken.Raw: 278 return new JRaw((string) reader.Value); 279 case JsonToken.Comment: 280 // ignore 281 break; 282 default: 283 throw CreateSerializationException(reader, "Unexpected token while deserializing object: " + reader.TokenType); 284 } 285 } while (reader.Read()); 286 287 throw CreateSerializationException(reader, "Unexpected end when deserializing object."); 288 } 289 CreateSerializationException(JsonReader reader, string message)290 private JsonSerializationException CreateSerializationException(JsonReader reader, string message) 291 { 292 return CreateSerializationException(reader, message, null); 293 } 294 CreateSerializationException(JsonReader reader, string message, Exception ex)295 private JsonSerializationException CreateSerializationException(JsonReader reader, string message, Exception ex) 296 { 297 return CreateSerializationException(reader as IJsonLineInfo, message, ex); 298 } 299 CreateSerializationException(IJsonLineInfo lineInfo, string message, Exception ex)300 private JsonSerializationException CreateSerializationException(IJsonLineInfo lineInfo, string message, Exception ex) 301 { 302 message = JsonReader.FormatExceptionMessage(lineInfo, message); 303 304 return new JsonSerializationException(message, ex); 305 } 306 GetConverter(JsonContract contract, JsonConverter memberConverter)307 private JsonConverter GetConverter(JsonContract contract, JsonConverter memberConverter) 308 { 309 JsonConverter converter = null; 310 if (memberConverter != null) 311 { 312 // member attribute converter 313 converter = memberConverter; 314 } 315 else if (contract != null) 316 { 317 JsonConverter matchingConverter; 318 if (contract.Converter != null) 319 // class attribute converter 320 converter = contract.Converter; 321 else if ((matchingConverter = Serializer.GetMatchingConverter(contract.UnderlyingType)) != null) 322 // passed in converters 323 converter = matchingConverter; 324 else if (contract.InternalConverter != null) 325 // internally specified converter 326 converter = contract.InternalConverter; 327 } 328 return converter; 329 } 330 CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, object existingValue)331 private object CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, object existingValue) 332 { 333 CheckedRead(reader); 334 335 string id = null; 336 337 if (reader.TokenType == JsonToken.PropertyName) 338 { 339 string propertyName = reader.Value.ToString(); 340 341 if (propertyName.Length > 0 && propertyName[0] == '$') 342 { 343 // read 'special' properties 344 // $type, $id, $ref, etc 345 bool specialProperty; 346 347 do 348 { 349 propertyName = reader.Value.ToString(); 350 351 if (string.Equals(propertyName, JsonTypeReflector.RefPropertyName, StringComparison.Ordinal)) 352 { 353 CheckedRead(reader); 354 if (reader.TokenType != JsonToken.String && reader.TokenType != JsonToken.Null) 355 throw CreateSerializationException(reader, "JSON reference {0} property must have a string or null value.".FormatWith(CultureInfo.InvariantCulture, JsonTypeReflector.RefPropertyName)); 356 357 string reference = (reader.Value != null) ? reader.Value.ToString() : null; 358 359 CheckedRead(reader); 360 361 if (reference != null) 362 { 363 if (reader.TokenType == JsonToken.PropertyName) 364 throw CreateSerializationException(reader, "Additional content found in JSON reference object. A JSON reference object should only have a {0} property.".FormatWith(CultureInfo.InvariantCulture, JsonTypeReflector.RefPropertyName)); 365 366 return Serializer.ReferenceResolver.ResolveReference(this, reference); 367 } 368 else 369 { 370 specialProperty = true; 371 } 372 } 373 else if (string.Equals(propertyName, JsonTypeReflector.TypePropertyName, StringComparison.Ordinal)) 374 { 375 CheckedRead(reader); 376 string qualifiedTypeName = reader.Value.ToString(); 377 378 if ((((member != null) ? member.TypeNameHandling : null) ?? Serializer.TypeNameHandling) != TypeNameHandling.None) 379 { 380 string typeName; 381 string assemblyName; 382 ReflectionUtils.SplitFullyQualifiedTypeName(qualifiedTypeName, out typeName, out assemblyName); 383 384 Type specifiedType; 385 try 386 { 387 specifiedType = Serializer.Binder.BindToType(assemblyName, typeName); 388 } 389 catch (Exception ex) 390 { 391 throw CreateSerializationException(reader, "Error resolving type specified in JSON '{0}'.".FormatWith(CultureInfo.InvariantCulture, qualifiedTypeName), ex); 392 } 393 394 if (specifiedType == null) 395 throw CreateSerializationException(reader, "Type specified in JSON '{0}' was not resolved.".FormatWith(CultureInfo.InvariantCulture, qualifiedTypeName)); 396 397 if (objectType != null && !objectType.IsAssignableFrom(specifiedType)) 398 throw CreateSerializationException(reader, "Type specified in JSON '{0}' is not compatible with '{1}'.".FormatWith(CultureInfo.InvariantCulture, specifiedType.AssemblyQualifiedName, objectType.AssemblyQualifiedName)); 399 400 objectType = specifiedType; 401 contract = GetContractSafe(specifiedType); 402 } 403 404 CheckedRead(reader); 405 406 specialProperty = true; 407 } 408 else if (string.Equals(propertyName, JsonTypeReflector.IdPropertyName, StringComparison.Ordinal)) 409 { 410 CheckedRead(reader); 411 412 id = (reader.Value != null) ? reader.Value.ToString() : null; 413 414 CheckedRead(reader); 415 specialProperty = true; 416 } 417 else if (string.Equals(propertyName, JsonTypeReflector.ArrayValuesPropertyName, StringComparison.Ordinal)) 418 { 419 CheckedRead(reader); 420 object list = CreateList(reader, objectType, contract, member, existingValue, id); 421 CheckedRead(reader); 422 return list; 423 } 424 else 425 { 426 specialProperty = false; 427 } 428 } while (specialProperty 429 && reader.TokenType == JsonToken.PropertyName); 430 } 431 } 432 433 if (!HasDefinedType(objectType)) 434 return CreateJObject(reader); 435 436 if (contract == null) 437 throw CreateSerializationException(reader, "Could not resolve type '{0}' to a JsonContract.".FormatWith(CultureInfo.InvariantCulture, objectType)); 438 439 switch (contract.ContractType) 440 { 441 case JsonContractType.Object: 442 JsonObjectContract objectContract = (JsonObjectContract) contract; 443 if (existingValue == null) 444 return CreateAndPopulateObject(reader, objectContract, id); 445 446 return PopulateObject(existingValue, reader, objectContract, id); 447 case JsonContractType.Primitive: 448 JsonPrimitiveContract primitiveContract = (JsonPrimitiveContract) contract; 449 // if the content is inside $value then read past it 450 if (reader.TokenType == JsonToken.PropertyName && string.Equals(reader.Value.ToString(), JsonTypeReflector.ValuePropertyName, StringComparison.Ordinal)) 451 { 452 CheckedRead(reader); 453 object value = CreateValueInternal(reader, objectType, primitiveContract, member, existingValue); 454 455 CheckedRead(reader); 456 return value; 457 } 458 break; 459 case JsonContractType.Dictionary: 460 JsonDictionaryContract dictionaryContract = (JsonDictionaryContract) contract; 461 if (existingValue == null) 462 return CreateAndPopulateDictionary(reader, dictionaryContract, id); 463 464 return PopulateDictionary(dictionaryContract.CreateWrapper(existingValue), reader, dictionaryContract, id); 465 #if !(NET35 || NET20 || WINDOWS_PHONE) 466 case JsonContractType.Dynamic: 467 JsonDynamicContract dynamicContract = (JsonDynamicContract) contract; 468 return CreateDynamic(reader, dynamicContract, id); 469 #endif 470 #if !SILVERLIGHT && !PocketPC && !NETFX_CORE 471 case JsonContractType.Serializable: 472 JsonISerializableContract serializableContract = (JsonISerializableContract) contract; 473 return CreateISerializable(reader, serializableContract, id); 474 #endif 475 } 476 477 throw CreateSerializationException(reader, @"Cannot deserialize JSON object (i.e. {{""name"":""value""}}) into type '{0}'. 478 The deserialized type should be a normal .NET type (i.e. not a primitive type like integer, not a collection type like an array or List<T>) or a dictionary type (i.e. Dictionary<TKey, TValue>). 479 To force JSON objects to deserialize add the JsonObjectAttribute to the type.".FormatWith(CultureInfo.InvariantCulture, objectType)); 480 } 481 EnsureArrayContract(JsonReader reader, Type objectType, JsonContract contract)482 private JsonArrayContract EnsureArrayContract(JsonReader reader, Type objectType, JsonContract contract) 483 { 484 if (contract == null) 485 throw CreateSerializationException(reader, "Could not resolve type '{0}' to a JsonContract.".FormatWith(CultureInfo.InvariantCulture, objectType)); 486 487 JsonArrayContract arrayContract = contract as JsonArrayContract; 488 if (arrayContract == null) 489 throw CreateSerializationException(reader, @"Cannot deserialize JSON array (i.e. [1,2,3]) into type '{0}'. 490 The deserialized type must be an array or implement a collection interface like IEnumerable, ICollection or IList. 491 To force JSON arrays to deserialize add the JsonArrayAttribute to the type.".FormatWith(CultureInfo.InvariantCulture, objectType)); 492 493 return arrayContract; 494 } 495 CheckedRead(JsonReader reader)496 private void CheckedRead(JsonReader reader) 497 { 498 if (!reader.Read()) 499 throw CreateSerializationException(reader, "Unexpected end when deserializing object."); 500 } 501 CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, object existingValue, string reference)502 private object CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, object existingValue, string reference) 503 { 504 object value; 505 if (HasDefinedType(objectType)) 506 { 507 JsonArrayContract arrayContract = EnsureArrayContract(reader, objectType, contract); 508 509 if (existingValue == null) 510 value = CreateAndPopulateList(reader, reference, arrayContract); 511 else 512 value = PopulateList(arrayContract.CreateWrapper(existingValue), reader, reference, arrayContract); 513 } 514 else 515 { 516 value = CreateJToken(reader, contract); 517 } 518 return value; 519 } 520 HasDefinedType(Type type)521 private bool HasDefinedType(Type type) 522 { 523 return (type != null && type != typeof (object) && !typeof (JToken).IsSubclassOf(type) 524 #if !(NET35 || NET20 || WINDOWS_PHONE) 525 && type != typeof (IDynamicMetaObjectProvider) 526 #endif 527 ); 528 } 529 EnsureType(JsonReader reader, object value, CultureInfo culture, JsonContract contract, Type targetType)530 private object EnsureType(JsonReader reader, object value, CultureInfo culture, JsonContract contract, Type targetType) 531 { 532 if (targetType == null) 533 return value; 534 535 Type valueType = ReflectionUtils.GetObjectType(value); 536 537 // type of value and type of target don't match 538 // attempt to convert value's type to target's type 539 if (valueType != targetType) 540 { 541 try 542 { 543 if (value == null && contract.IsNullable) 544 return null; 545 546 if (contract.IsConvertable) 547 { 548 if (contract.NonNullableUnderlyingType.IsEnum()) 549 { 550 if (value is string) 551 return Enum.Parse(contract.NonNullableUnderlyingType, value.ToString(), true); 552 else if (ConvertUtils.IsInteger(value)) 553 return Enum.ToObject(contract.NonNullableUnderlyingType, value); 554 } 555 556 return Convert.ChangeType(value, contract.NonNullableUnderlyingType, culture); 557 } 558 559 return ConvertUtils.ConvertOrCast(value, culture, contract.NonNullableUnderlyingType); 560 } 561 catch (Exception ex) 562 { 563 throw CreateSerializationException(reader, "Error converting value {0} to type '{1}'.".FormatWith(CultureInfo.InvariantCulture, FormatValueForPrint(value), targetType), ex); 564 } 565 } 566 567 return value; 568 } 569 FormatValueForPrint(object value)570 private string FormatValueForPrint(object value) 571 { 572 if (value == null) 573 return "{null}"; 574 575 if (value is string) 576 return @"""" + value + @""""; 577 578 return value.ToString(); 579 } 580 SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonReader reader, object target)581 private void SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonReader reader, object target) 582 { 583 if (property.Ignored) 584 { 585 reader.Skip(); 586 return; 587 } 588 589 object currentValue = null; 590 bool useExistingValue = false; 591 bool gottenCurrentValue = false; 592 593 ObjectCreationHandling objectCreationHandling = 594 property.ObjectCreationHandling.GetValueOrDefault(Serializer.ObjectCreationHandling); 595 596 if ((objectCreationHandling == ObjectCreationHandling.Auto || objectCreationHandling == ObjectCreationHandling.Reuse) 597 && (reader.TokenType == JsonToken.StartArray || reader.TokenType == JsonToken.StartObject) 598 && property.Readable) 599 { 600 currentValue = property.ValueProvider.GetValue(target); 601 gottenCurrentValue = true; 602 603 useExistingValue = (currentValue != null 604 && !property.PropertyType.IsArray 605 && !ReflectionUtils.InheritsGenericDefinition(property.PropertyType, typeof (ReadOnlyCollection<>)) 606 && !property.PropertyType.IsValueType()); 607 } 608 609 if (!property.Writable && !useExistingValue) 610 { 611 reader.Skip(); 612 return; 613 } 614 615 // test tokentype here because null might not be convertable to some types, e.g. ignoring null when applied to DateTime 616 if (property.NullValueHandling.GetValueOrDefault(Serializer.NullValueHandling) == NullValueHandling.Ignore && reader.TokenType == JsonToken.Null) 617 { 618 reader.Skip(); 619 return; 620 } 621 622 // test tokentype here because default value might not be convertable to actual type, e.g. default of "" for DateTime 623 if (HasFlag(property.DefaultValueHandling.GetValueOrDefault(Serializer.DefaultValueHandling), DefaultValueHandling.Ignore) 624 && JsonReader.IsPrimitiveToken(reader.TokenType) 625 && MiscellaneousUtils.ValueEquals(reader.Value, property.DefaultValue)) 626 { 627 reader.Skip(); 628 return; 629 } 630 631 object existingValue = (useExistingValue) ? currentValue : null; 632 object value = CreateValueProperty(reader, property, propertyConverter, target, gottenCurrentValue, existingValue); 633 634 // always set the value if useExistingValue is false, 635 // otherwise also set it if CreateValue returns a new value compared to the currentValue 636 // this could happen because of a JsonConverter against the type 637 if ((!useExistingValue || value != currentValue) 638 && ShouldSetPropertyValue(property, value)) 639 { 640 property.ValueProvider.SetValue(target, value); 641 642 if (property.SetIsSpecified != null) 643 property.SetIsSpecified(target, true); 644 } 645 } 646 HasFlag(DefaultValueHandling value, DefaultValueHandling flag)647 private bool HasFlag(DefaultValueHandling value, DefaultValueHandling flag) 648 { 649 return ((value & flag) == flag); 650 } 651 ShouldSetPropertyValue(JsonProperty property, object value)652 private bool ShouldSetPropertyValue(JsonProperty property, object value) 653 { 654 if (property.NullValueHandling.GetValueOrDefault(Serializer.NullValueHandling) == NullValueHandling.Ignore && value == null) 655 return false; 656 657 if (HasFlag(property.DefaultValueHandling.GetValueOrDefault(Serializer.DefaultValueHandling), DefaultValueHandling.Ignore) 658 && MiscellaneousUtils.ValueEquals(value, property.DefaultValue)) 659 return false; 660 661 if (!property.Writable) 662 return false; 663 664 return true; 665 } 666 CreateAndPopulateDictionary(JsonReader reader, JsonDictionaryContract contract, string id)667 private object CreateAndPopulateDictionary(JsonReader reader, JsonDictionaryContract contract, string id) 668 { 669 object dictionary; 670 671 if (contract.DefaultCreator != null && 672 (!contract.DefaultCreatorNonPublic || Serializer.ConstructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor)) 673 dictionary = contract.DefaultCreator(); 674 else 675 throw CreateSerializationException(reader, "Unable to find a default constructor to use for type {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); 676 677 IWrappedDictionary dictionaryWrapper = contract.CreateWrapper(dictionary); 678 679 PopulateDictionary(dictionaryWrapper, reader, contract, id); 680 681 return dictionaryWrapper.UnderlyingDictionary; 682 } 683 PopulateDictionary(IWrappedDictionary dictionary, JsonReader reader, JsonDictionaryContract contract, string id)684 private object PopulateDictionary(IWrappedDictionary dictionary, JsonReader reader, JsonDictionaryContract contract, string id) 685 { 686 if (id != null) 687 Serializer.ReferenceResolver.AddReference(this, id, dictionary.UnderlyingDictionary); 688 689 contract.InvokeOnDeserializing(dictionary.UnderlyingDictionary, Serializer.Context); 690 691 int initialDepth = reader.Depth; 692 693 do 694 { 695 switch (reader.TokenType) 696 { 697 case JsonToken.PropertyName: 698 object keyValue = reader.Value; 699 try 700 { 701 if (contract.DictionaryKeyContract == null) 702 contract.DictionaryKeyContract = GetContractSafe(contract.DictionaryKeyType); 703 704 try 705 { 706 keyValue = EnsureType(reader, keyValue, CultureInfo.InvariantCulture, contract.DictionaryKeyContract, contract.DictionaryKeyType); 707 } 708 catch (Exception ex) 709 { 710 throw CreateSerializationException(reader, "Could not convert string '{0}' to dictionary key type '{1}'. Create a TypeConverter to convert from the string to the key type object.".FormatWith(CultureInfo.InvariantCulture, reader.Value, contract.DictionaryKeyType), ex); 711 } 712 713 if (contract.DictionaryValueContract == null) 714 contract.DictionaryValueContract = GetContractSafe(contract.DictionaryValueType); 715 716 JsonConverter dictionaryValueConverter = GetConverter(contract.DictionaryValueContract, null); 717 718 if (!ReadForType(reader, contract.DictionaryValueContract, dictionaryValueConverter != null, false)) 719 throw CreateSerializationException(reader, "Unexpected end when deserializing object."); 720 721 dictionary[keyValue] = CreateValueNonProperty(reader, contract.DictionaryValueType, contract.DictionaryValueContract, dictionaryValueConverter); 722 } 723 catch (Exception ex) 724 { 725 if (IsErrorHandled(dictionary, contract, keyValue, reader.Path, ex)) 726 HandleError(reader, initialDepth); 727 else 728 throw; 729 } 730 break; 731 case JsonToken.Comment: 732 break; 733 case JsonToken.EndObject: 734 contract.InvokeOnDeserialized(dictionary.UnderlyingDictionary, Serializer.Context); 735 736 return dictionary.UnderlyingDictionary; 737 default: 738 throw CreateSerializationException(reader, "Unexpected token when deserializing object: " + reader.TokenType); 739 } 740 } while (reader.Read()); 741 742 throw CreateSerializationException(reader, "Unexpected end when deserializing object."); 743 } 744 CreateAndPopulateList(JsonReader reader, string reference, JsonArrayContract contract)745 private object CreateAndPopulateList(JsonReader reader, string reference, JsonArrayContract contract) 746 { 747 return CollectionUtils.CreateAndPopulateList(contract.CreatedType, (l, isTemporaryListReference) => 748 { 749 if (reference != null && isTemporaryListReference) 750 throw CreateSerializationException(reader, "Cannot preserve reference to array or readonly list: {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); 751 752 #if !PocketPC 753 if (contract.OnSerializing != null && isTemporaryListReference) 754 throw CreateSerializationException(reader, "Cannot call OnSerializing on an array or readonly list: {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); 755 #endif 756 if (contract.OnError != null && isTemporaryListReference) 757 throw CreateSerializationException(reader, "Cannot call OnError on an array or readonly list: {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); 758 759 PopulateList(contract.CreateWrapper(l), reader, reference, contract); 760 }); 761 } 762 PopulateList(IWrappedCollection wrappedList, JsonReader reader, string reference, JsonArrayContract contract)763 private object PopulateList(IWrappedCollection wrappedList, JsonReader reader, string reference, JsonArrayContract contract) 764 { 765 object list = wrappedList.UnderlyingCollection; 766 767 // can't populate an existing array 768 if (wrappedList.IsFixedSize) 769 { 770 reader.Skip(); 771 return wrappedList.UnderlyingCollection; 772 } 773 774 if (reference != null) 775 Serializer.ReferenceResolver.AddReference(this, reference, list); 776 777 contract.InvokeOnDeserializing(list, Serializer.Context); 778 779 int initialDepth = reader.Depth; 780 int index = 0; 781 782 JsonContract collectionItemContract = GetContractSafe(contract.CollectionItemType); 783 JsonConverter collectionItemConverter = GetConverter(collectionItemContract, null); 784 785 while (true) 786 { 787 try 788 { 789 if (ReadForType(reader, collectionItemContract, collectionItemConverter != null, true)) 790 { 791 switch (reader.TokenType) 792 { 793 case JsonToken.EndArray: 794 contract.InvokeOnDeserialized(list, Serializer.Context); 795 796 return wrappedList.UnderlyingCollection; 797 case JsonToken.Comment: 798 break; 799 default: 800 object value = CreateValueNonProperty(reader, contract.CollectionItemType, collectionItemContract, collectionItemConverter); 801 802 wrappedList.Add(value); 803 break; 804 } 805 } 806 else 807 { 808 break; 809 } 810 } 811 catch (Exception ex) 812 { 813 if (IsErrorHandled(list, contract, index, reader.Path, ex)) 814 HandleError(reader, initialDepth); 815 else 816 throw; 817 } 818 finally 819 { 820 index++; 821 } 822 } 823 824 throw CreateSerializationException(reader, "Unexpected end when deserializing array."); 825 } 826 827 #if !SILVERLIGHT && !PocketPC && !NETFX_CORE CreateISerializable(JsonReader reader, JsonISerializableContract contract, string id)828 private object CreateISerializable(JsonReader reader, JsonISerializableContract contract, string id) 829 { 830 Type objectType = contract.UnderlyingType; 831 832 if (!JsonTypeReflector.FullyTrusted) 833 { 834 throw new JsonSerializationException(@"Type '{0}' implements ISerializable but cannot be deserialized using the ISerializable interface because the current application is not fully trusted and ISerializable can expose secure data. 835 To fix this error either change the environment to be fully trusted, change the application to not deserialize the type, add to JsonObjectAttribute to the type or change the JsonSerializer setting ContractResolver to use a new DefaultContractResolver with IgnoreSerializableInterface set to true.".FormatWith(CultureInfo.InvariantCulture, objectType)); 836 } 837 838 SerializationInfo serializationInfo = new SerializationInfo(contract.UnderlyingType, GetFormatterConverter()); 839 840 bool exit = false; 841 do 842 { 843 switch (reader.TokenType) 844 { 845 case JsonToken.PropertyName: 846 string memberName = reader.Value.ToString(); 847 if (!reader.Read()) 848 throw CreateSerializationException(reader, "Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName)); 849 850 serializationInfo.AddValue(memberName, JToken.ReadFrom(reader)); 851 break; 852 case JsonToken.Comment: 853 break; 854 case JsonToken.EndObject: 855 exit = true; 856 break; 857 default: 858 throw CreateSerializationException(reader, "Unexpected token when deserializing object: " + reader.TokenType); 859 } 860 } while (!exit && reader.Read()); 861 862 if (contract.ISerializableCreator == null) 863 throw CreateSerializationException(reader, "ISerializable type '{0}' does not have a valid constructor. To correctly implement ISerializable a constructor that takes SerializationInfo and StreamingContext parameters should be present.".FormatWith(CultureInfo.InvariantCulture, objectType)); 864 865 object createdObject = contract.ISerializableCreator(serializationInfo, Serializer.Context); 866 867 if (id != null) 868 Serializer.ReferenceResolver.AddReference(this, id, createdObject); 869 870 // these are together because OnDeserializing takes an object but for an ISerializable the object is full created in the constructor 871 contract.InvokeOnDeserializing(createdObject, Serializer.Context); 872 contract.InvokeOnDeserialized(createdObject, Serializer.Context); 873 874 return createdObject; 875 } 876 #endif 877 878 #if !(NET35 || NET20 || WINDOWS_PHONE) CreateDynamic(JsonReader reader, JsonDynamicContract contract, string id)879 private object CreateDynamic(JsonReader reader, JsonDynamicContract contract, string id) 880 { 881 IDynamicMetaObjectProvider newObject = null; 882 883 if (contract.UnderlyingType.IsInterface() || contract.UnderlyingType.IsAbstract()) 884 throw CreateSerializationException(reader, "Could not create an instance of type {0}. Type is an interface or abstract class and cannot be instantated.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); 885 886 if (contract.DefaultCreator != null && 887 (!contract.DefaultCreatorNonPublic || Serializer.ConstructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor)) 888 newObject = (IDynamicMetaObjectProvider) contract.DefaultCreator(); 889 else 890 throw CreateSerializationException(reader, "Unable to find a default constructor to use for type {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); 891 892 if (id != null) 893 Serializer.ReferenceResolver.AddReference(this, id, newObject); 894 895 contract.InvokeOnDeserializing(newObject, Serializer.Context); 896 897 int initialDepth = reader.Depth; 898 899 bool exit = false; 900 do 901 { 902 switch (reader.TokenType) 903 { 904 case JsonToken.PropertyName: 905 string memberName = reader.Value.ToString(); 906 907 try 908 { 909 if (!reader.Read()) 910 throw CreateSerializationException(reader, "Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName)); 911 912 // first attempt to find a settable property, otherwise fall back to a dynamic set without type 913 JsonProperty property = contract.Properties.GetClosestMatchProperty(memberName); 914 915 if (property != null && property.Writable && !property.Ignored) 916 { 917 if (property.PropertyContract == null) 918 property.PropertyContract = GetContractSafe(property.PropertyType); 919 920 JsonConverter propertyConverter = GetConverter(property.PropertyContract, property.MemberConverter); 921 922 SetPropertyValue(property, propertyConverter, reader, newObject); 923 } 924 else 925 { 926 Type t = (JsonReader.IsPrimitiveToken(reader.TokenType)) ? reader.ValueType : typeof (IDynamicMetaObjectProvider); 927 928 JsonContract dynamicMemberContract = GetContractSafe(t); 929 JsonConverter dynamicMemberConverter = GetConverter(dynamicMemberContract, null); 930 931 object value = CreateValueNonProperty(reader, t, dynamicMemberContract, dynamicMemberConverter); 932 933 newObject.TrySetMember(memberName, value); 934 } 935 } 936 catch (Exception ex) 937 { 938 if (IsErrorHandled(newObject, contract, memberName, reader.Path, ex)) 939 HandleError(reader, initialDepth); 940 else 941 throw; 942 } 943 break; 944 case JsonToken.EndObject: 945 exit = true; 946 break; 947 default: 948 throw CreateSerializationException(reader, "Unexpected token when deserializing object: " + reader.TokenType); 949 } 950 } while (!exit && reader.Read()); 951 952 contract.InvokeOnDeserialized(newObject, Serializer.Context); 953 954 return newObject; 955 } 956 #endif 957 CreateAndPopulateObject(JsonReader reader, JsonObjectContract contract, string id)958 private object CreateAndPopulateObject(JsonReader reader, JsonObjectContract contract, string id) 959 { 960 object newObject = null; 961 962 if (contract.UnderlyingType.IsInterface() || contract.UnderlyingType.IsAbstract()) 963 throw CreateSerializationException(reader, "Could not create an instance of type {0}. Type is an interface or abstract class and cannot be instantated.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); 964 965 if (contract.OverrideConstructor != null) 966 { 967 if (contract.OverrideConstructor.GetParameters().Length > 0) 968 return CreateObjectFromNonDefaultConstructor(reader, contract, contract.OverrideConstructor, id); 969 970 newObject = contract.OverrideConstructor.Invoke(null); 971 } 972 else if (contract.DefaultCreator != null && 973 (!contract.DefaultCreatorNonPublic || Serializer.ConstructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor || contract.ParametrizedConstructor == null)) 974 { 975 // use the default constructor if it is... 976 // public 977 // non-public and the user has change constructor handling settings 978 // non-public and there is no other constructor 979 newObject = contract.DefaultCreator(); 980 } 981 else if (contract.ParametrizedConstructor != null) 982 { 983 return CreateObjectFromNonDefaultConstructor(reader, contract, contract.ParametrizedConstructor, id); 984 } 985 986 if (newObject == null) 987 throw CreateSerializationException(reader, "Unable to find a constructor to use for type {0}. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); 988 989 PopulateObject(newObject, reader, contract, id); 990 return newObject; 991 } 992 CreateObjectFromNonDefaultConstructor(JsonReader reader, JsonObjectContract contract, ConstructorInfo constructorInfo, string id)993 private object CreateObjectFromNonDefaultConstructor(JsonReader reader, JsonObjectContract contract, ConstructorInfo constructorInfo, string id) 994 { 995 ValidationUtils.ArgumentNotNull(constructorInfo, "constructorInfo"); 996 997 Type objectType = contract.UnderlyingType; 998 999 IDictionary<JsonProperty, object> propertyValues = ResolvePropertyAndConstructorValues(contract, reader, objectType); 1000 1001 IDictionary<ParameterInfo, object> constructorParameters = constructorInfo.GetParameters().ToDictionary(p => p, p => (object) null); 1002 IDictionary<JsonProperty, object> remainingPropertyValues = new Dictionary<JsonProperty, object>(); 1003 1004 foreach (KeyValuePair<JsonProperty, object> propertyValue in propertyValues) 1005 { 1006 ParameterInfo matchingConstructorParameter = constructorParameters.ForgivingCaseSensitiveFind(kv => kv.Key.Name, propertyValue.Key.UnderlyingName).Key; 1007 if (matchingConstructorParameter != null) 1008 constructorParameters[matchingConstructorParameter] = propertyValue.Value; 1009 else 1010 remainingPropertyValues.Add(propertyValue); 1011 } 1012 1013 object createdObject = constructorInfo.Invoke(constructorParameters.Values.ToArray()); 1014 1015 if (id != null) 1016 Serializer.ReferenceResolver.AddReference(this, id, createdObject); 1017 1018 contract.InvokeOnDeserializing(createdObject, Serializer.Context); 1019 1020 // go through unused values and set the newly created object's properties 1021 foreach (KeyValuePair<JsonProperty, object> remainingPropertyValue in remainingPropertyValues) 1022 { 1023 JsonProperty property = remainingPropertyValue.Key; 1024 object value = remainingPropertyValue.Value; 1025 1026 if (ShouldSetPropertyValue(remainingPropertyValue.Key, remainingPropertyValue.Value)) 1027 { 1028 property.ValueProvider.SetValue(createdObject, value); 1029 } 1030 else if (!property.Writable && value != null) 1031 { 1032 // handle readonly collection/dictionary properties 1033 JsonContract propertyContract = Serializer.ContractResolver.ResolveContract(property.PropertyType); 1034 1035 if (propertyContract.ContractType == JsonContractType.Array) 1036 { 1037 JsonArrayContract propertyArrayContract = propertyContract as JsonArrayContract; 1038 1039 object createdObjectCollection = property.ValueProvider.GetValue(createdObject); 1040 if (createdObjectCollection != null) 1041 { 1042 IWrappedCollection createdObjectCollectionWrapper = propertyArrayContract.CreateWrapper(createdObjectCollection); 1043 IWrappedCollection newValues = propertyArrayContract.CreateWrapper(value); 1044 1045 foreach (object newValue in newValues) 1046 { 1047 createdObjectCollectionWrapper.Add(newValue); 1048 } 1049 } 1050 } 1051 else if (propertyContract.ContractType == JsonContractType.Dictionary) 1052 { 1053 JsonDictionaryContract jsonDictionaryContract = propertyContract as JsonDictionaryContract; 1054 1055 object createdObjectDictionary = property.ValueProvider.GetValue(createdObject); 1056 if (createdObjectDictionary != null) 1057 { 1058 IWrappedDictionary createdObjectDictionaryWrapper = jsonDictionaryContract.CreateWrapper(createdObjectDictionary); 1059 IWrappedDictionary newValues = jsonDictionaryContract.CreateWrapper(value); 1060 1061 foreach (DictionaryEntry newValue in newValues) 1062 { 1063 createdObjectDictionaryWrapper.Add(newValue.Key, newValue.Value); 1064 } 1065 } 1066 } 1067 } 1068 } 1069 1070 contract.InvokeOnDeserialized(createdObject, Serializer.Context); 1071 return createdObject; 1072 } 1073 ResolvePropertyAndConstructorValues(JsonObjectContract contract, JsonReader reader, Type objectType)1074 private IDictionary<JsonProperty, object> ResolvePropertyAndConstructorValues(JsonObjectContract contract, JsonReader reader, Type objectType) 1075 { 1076 IDictionary<JsonProperty, object> propertyValues = new Dictionary<JsonProperty, object>(); 1077 bool exit = false; 1078 do 1079 { 1080 switch (reader.TokenType) 1081 { 1082 case JsonToken.PropertyName: 1083 string memberName = reader.Value.ToString(); 1084 1085 // attempt exact case match first 1086 // then try match ignoring case 1087 JsonProperty property = contract.ConstructorParameters.GetClosestMatchProperty(memberName) ?? 1088 contract.Properties.GetClosestMatchProperty(memberName); 1089 1090 if (property != null) 1091 { 1092 if (property.PropertyContract == null) 1093 property.PropertyContract = GetContractSafe(property.PropertyType); 1094 1095 JsonConverter propertyConverter = GetConverter(property.PropertyContract, property.MemberConverter); 1096 1097 if (!ReadForType(reader, property.PropertyContract, propertyConverter != null, false)) 1098 throw CreateSerializationException(reader, "Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName)); 1099 1100 if (!property.Ignored) 1101 propertyValues[property] = CreateValueProperty(reader, property, propertyConverter, null, true, null); 1102 else 1103 reader.Skip(); 1104 } 1105 else 1106 { 1107 if (!reader.Read()) 1108 throw CreateSerializationException(reader, "Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName)); 1109 1110 if (Serializer.MissingMemberHandling == MissingMemberHandling.Error) 1111 throw CreateSerializationException(reader, "Could not find member '{0}' on object of type '{1}'".FormatWith(CultureInfo.InvariantCulture, memberName, objectType.Name)); 1112 1113 reader.Skip(); 1114 } 1115 break; 1116 case JsonToken.Comment: 1117 break; 1118 case JsonToken.EndObject: 1119 exit = true; 1120 break; 1121 default: 1122 throw CreateSerializationException(reader, "Unexpected token when deserializing object: " + reader.TokenType); 1123 } 1124 } while (!exit && reader.Read()); 1125 1126 return propertyValues; 1127 } 1128 ReadForType(JsonReader reader, JsonContract contract, bool hasConverter, bool inArray)1129 private bool ReadForType(JsonReader reader, JsonContract contract, bool hasConverter, bool inArray) 1130 { 1131 // don't read properties with converters as a specific value 1132 // the value might be a string which will then get converted which will error if read as date for example 1133 if (hasConverter) 1134 return reader.Read(); 1135 1136 ReadType t = (contract != null) ? contract.InternalReadType : ReadType.Read; 1137 1138 switch (t) 1139 { 1140 case ReadType.Read: 1141 do 1142 { 1143 if (!reader.Read()) 1144 return false; 1145 } while (reader.TokenType == JsonToken.Comment); 1146 1147 return true; 1148 case ReadType.ReadAsInt32: 1149 reader.ReadAsInt32(); 1150 break; 1151 case ReadType.ReadAsDecimal: 1152 reader.ReadAsDecimal(); 1153 break; 1154 case ReadType.ReadAsBytes: 1155 reader.ReadAsBytes(); 1156 break; 1157 case ReadType.ReadAsString: 1158 reader.ReadAsString(); 1159 break; 1160 case ReadType.ReadAsDateTime: 1161 reader.ReadAsDateTime(); 1162 break; 1163 #if !NET20 1164 case ReadType.ReadAsDateTimeOffset: 1165 reader.ReadAsDateTimeOffset(); 1166 break; 1167 #endif 1168 default: 1169 throw new ArgumentOutOfRangeException(); 1170 } 1171 1172 return (reader.TokenType != JsonToken.None); 1173 } 1174 PopulateObject(object newObject, JsonReader reader, JsonObjectContract contract, string id)1175 private object PopulateObject(object newObject, JsonReader reader, JsonObjectContract contract, string id) 1176 { 1177 contract.InvokeOnDeserializing(newObject, Serializer.Context); 1178 1179 Dictionary<JsonProperty, PropertyPresence> propertiesPresence = 1180 contract.Properties.ToDictionary(m => m, m => PropertyPresence.None); 1181 1182 if (id != null) 1183 Serializer.ReferenceResolver.AddReference(this, id, newObject); 1184 1185 int initialDepth = reader.Depth; 1186 1187 do 1188 { 1189 switch (reader.TokenType) 1190 { 1191 case JsonToken.PropertyName: 1192 { 1193 string memberName = reader.Value.ToString(); 1194 1195 try 1196 { 1197 // attempt exact case match first 1198 // then try match ignoring case 1199 JsonProperty property = contract.Properties.GetClosestMatchProperty(memberName); 1200 1201 if (property == null) 1202 { 1203 if (Serializer.MissingMemberHandling == MissingMemberHandling.Error) 1204 throw CreateSerializationException(reader, "Could not find member '{0}' on object of type '{1}'".FormatWith(CultureInfo.InvariantCulture, memberName, contract.UnderlyingType.Name)); 1205 1206 reader.Skip(); 1207 continue; 1208 } 1209 1210 if (property.PropertyContract == null) 1211 property.PropertyContract = GetContractSafe(property.PropertyType); 1212 1213 JsonConverter propertyConverter = GetConverter(property.PropertyContract, property.MemberConverter); 1214 1215 if (!ReadForType(reader, property.PropertyContract, propertyConverter != null, false)) 1216 throw CreateSerializationException(reader, "Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName)); 1217 1218 SetPropertyPresence(reader, property, propertiesPresence); 1219 1220 SetPropertyValue(property, propertyConverter, reader, newObject); 1221 } 1222 catch (Exception ex) 1223 { 1224 if (IsErrorHandled(newObject, contract, memberName, reader.Path, ex)) 1225 HandleError(reader, initialDepth); 1226 else 1227 throw; 1228 } 1229 } 1230 break; 1231 case JsonToken.EndObject: 1232 { 1233 foreach (KeyValuePair<JsonProperty, PropertyPresence> propertyPresence in propertiesPresence) 1234 { 1235 JsonProperty property = propertyPresence.Key; 1236 PropertyPresence presence = propertyPresence.Value; 1237 1238 if (presence == PropertyPresence.None || presence == PropertyPresence.Null) 1239 { 1240 try 1241 { 1242 switch (presence) 1243 { 1244 case PropertyPresence.None: 1245 if (property.Required == Required.AllowNull || property.Required == Required.Always) 1246 throw CreateSerializationException(reader, "Required property '{0}' not found in JSON.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName)); 1247 1248 if (property.PropertyContract == null) 1249 property.PropertyContract = GetContractSafe(property.PropertyType); 1250 1251 if (HasFlag(property.DefaultValueHandling.GetValueOrDefault(Serializer.DefaultValueHandling), DefaultValueHandling.Populate) 1252 && property.Writable) 1253 property.ValueProvider.SetValue(newObject, EnsureType(reader, property.DefaultValue, CultureInfo.InvariantCulture, property.PropertyContract, property.PropertyType)); 1254 break; 1255 case PropertyPresence.Null: 1256 if (property.Required == Required.Always) 1257 throw CreateSerializationException(reader, "Required property '{0}' expects a value but got null.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName)); 1258 break; 1259 } 1260 } 1261 catch (Exception ex) 1262 { 1263 if (IsErrorHandled(newObject, contract, property.PropertyName, reader.Path, ex)) 1264 HandleError(reader, initialDepth); 1265 else 1266 throw; 1267 } 1268 } 1269 } 1270 1271 contract.InvokeOnDeserialized(newObject, Serializer.Context); 1272 return newObject; 1273 } 1274 case JsonToken.Comment: 1275 // ignore 1276 break; 1277 default: 1278 throw CreateSerializationException(reader, "Unexpected token when deserializing object: " + reader.TokenType); 1279 } 1280 } while (reader.Read()); 1281 1282 throw CreateSerializationException(reader, "Unexpected end when deserializing object."); 1283 } 1284 SetPropertyPresence(JsonReader reader, JsonProperty property, Dictionary<JsonProperty, PropertyPresence> requiredProperties)1285 private void SetPropertyPresence(JsonReader reader, JsonProperty property, Dictionary<JsonProperty, PropertyPresence> requiredProperties) 1286 { 1287 if (property != null) 1288 { 1289 requiredProperties[property] = (reader.TokenType == JsonToken.Null || reader.TokenType == JsonToken.Undefined) 1290 ? PropertyPresence.Null 1291 : PropertyPresence.Value; 1292 } 1293 } 1294 HandleError(JsonReader reader, int initialDepth)1295 private void HandleError(JsonReader reader, int initialDepth) 1296 { 1297 ClearErrorContext(); 1298 1299 reader.Skip(); 1300 1301 while (reader.Depth > (initialDepth + 1)) 1302 { 1303 reader.Read(); 1304 } 1305 } 1306 1307 internal enum PropertyPresence 1308 { 1309 None, 1310 Null, 1311 Value 1312 } 1313 } 1314 }