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 }