/// <summary> /// Deserialize a JSON value into an instance. /// </summary> /// <param name="value">The JSON value.</param> /// <param name="instance">The instance to deserialize into.</param> /// <param name="ignoreCustomSerialization"> /// A value to indicate whether or not custom serialization should be /// ignored if the instance implements /// ICustomMobileServiceTableSerialization. This flag is used by /// implementations of ICustomMobileServiceTableSerialization that want /// to invoke the default serialization behavior. /// </param> public static void Deserialize(JToken value, object instance, bool ignoreCustomSerialization) { if (value == null) { throw new ArgumentNullException("value"); } else if (instance == null) { throw new ArgumentNullException("instance"); } // If the instance implements // ICustomMobileServiceTableSerialization, allow it to handle its // own deserialization. if (!ignoreCustomSerialization) { ICustomMobileServiceTableSerialization custom = instance as ICustomMobileServiceTableSerialization; if (custom != null) { custom.Deserialize(value); return; } } // Get the Mobile Services specific type info SerializableType type = SerializableType.Get(instance.GetType()); // Get the object to deserialize JObject obj = value.AsObject(); if (obj == null) { throw new ArgumentException( string.Format( CultureInfo.InvariantCulture, Resources.MobileServiceTableSerializer_Deserialize_NeedObject, value.Type), "value"); } // Create a set of required members that we can remove from as we // process their values from the object. If there's anything left // in the set after processing all of the values, then we weren't // given all of our required properties. HashSet <SerializableMember> requiredMembers = new HashSet <SerializableMember>(type.Members.Values.Where(m => m.IsRequired)); // Walk through all of the members defined in the object and // deserialize them into the instance one at a time foreach (KeyValuePair <string, JToken> assignment in obj.GetPropertyValues().OrderBy(a => type.GetMemberOrder(a.Key))) { // Look up the instance member corresponding to the JSON member SerializableMember member = null; if (type.Members.TryGetValue(assignment.Key, out member)) { // Remove the member from the required list (does nothing // if it wasn't present) requiredMembers.Remove(member); // Convert the property value into a CLR value using either // the converter or a standard simple JSON mapping. This // will throw for JSON arrays or objects. Also note that // we'll still run the value returned from the converter // through the ChangeType call below to make writing // converters a little easier (but it should be a no-op // for most folks anyway). object propertyValue = null; if (member.Converter != null) { propertyValue = member.Converter.ConvertFromJson(assignment.Value); } else if (!assignment.Value.TryConvert(out propertyValue)) { throw new ArgumentException( string.Format( CultureInfo.InvariantCulture, Resources.MobileServiceTableSerializer_Deserialize_CannotDeserializeValue, assignment.Value.ToString(), type.Type.FullName, member.MemberName), "value"); } // Change the type of the value to the desired property // type (mostly to handle things like casting issues) and // set the value on the instance. object convertedValue = TypeExtensions.ChangeType(propertyValue, member.Type); member.SetValue(instance, convertedValue); } } // Ensure we were provided all of the required properties. if (requiredMembers.Count > 0) { throw new SerializationException( string.Format( CultureInfo.InvariantCulture, Resources.MobileServiceTableSerializer_Deserialize_MissingRequired, type.Type.FullName, string.Join(", ", requiredMembers.Select(m => m.Name)))); } }