public void CoerceType_CoerceToBaseType_NoCoercionNeeded()
		{
			dynamic input = new ExpandoObject();
			input.One = 1;
			input.Two = 2;
			input.Three = 3;
			input.Four = 4;

			var settings = new DataReaderSettings();
			var actual = new TypeCoercionUtility(settings, settings.AllowNullValueTypes).CoerceType(typeof(IDictionary<string, object>), input);

			Assert.Equal(input, actual);
		}
示例#2
0
        /// <summary>
        /// Gets the serialized name for the member.
        /// </summary>
        /// <param name="member"></param>
        /// <returns></returns>
        public override IEnumerable <DataName> GetName(MemberInfo member)
        {
            string    localName, ns;
            Attribute typeAttr;

            if (member is Type)
            {
                typeAttr = TypeCoercionUtility.GetAttribute(member, DataContractResolverStrategy.DataContractType);
                if (typeAttr == null)
                {
                    yield break;
                }

                localName = (string)DataContractResolverStrategy.DataContractNameGetter(typeAttr);
                ns        = (string)DataContractResolverStrategy.DataContractNamespaceGetter(typeAttr);

                if (!String.IsNullOrEmpty(localName))
                {
                    yield return(new DataName(localName, null, ns));
                }
                yield break;
            }

            typeAttr = TypeCoercionUtility.GetAttribute(member.DeclaringType, DataContractResolverStrategy.DataContractType);
            if (typeAttr == null)
            {
                yield break;
            }

            ns = (string)DataContractResolverStrategy.DataContractNamespaceGetter(typeAttr);

            Attribute memberAttr = TypeCoercionUtility.GetAttribute(member, DataContractResolverStrategy.DataMemberType);

            if (memberAttr == null)
            {
                yield break;
            }

            localName = (string)DataContractResolverStrategy.DataMemberNameGetter(memberAttr);

            if (!String.IsNullOrEmpty(localName))
            {
                // members inherit DataContract namespaces
                yield return(new DataName(localName, null, ns));
            }
        }
示例#3
0
        public void CoerceType_ArrayToList_CoercesViaListCtor()
        {
            var input = new string[]
                {
                    "One",
                    "Two",
                    "Three",
                    "Four"
                };

            var expected = new List<string>
                {
                    "One",
                    "Two",
                    "Three",
                    "Four"
                };

            var settings = new DataReaderSettings();
            var actual = new TypeCoercionUtility(settings, settings.AllowNullValueTypes, null).CoerceType(expected.GetType(), input);

            Assert.Equal(expected, actual);
        }
示例#4
0
        /// <summary>
        /// Ctor
        /// </summary>
        /// <param name="settings"></param>
        public ModelAnalyzer(DataReaderSettings settings)
        {
            if (settings == null)
            {
                throw new ArgumentNullException("settings");
            }
            this.Settings = settings;

            var filters = new List <IDataFilter <ModelTokenType> >();

            if (settings.Filters != null)
            {
                foreach (var filter in settings.Filters)
                {
                    if (filter != null)
                    {
                        filters.Add(filter);
                    }
                }
            }
            this.Filters = filters;

            this.Coercion = new TypeCoercionUtility(settings, settings.AllowNullValueTypes);
        }
        /// <summary>
        /// Gets a delegate which determines if the property or field should not be serialized based upon its value.
        /// </summary>
        /// <param name="member"></param>
        /// <returns>if has a value equivalent to the DefaultValueAttribute or has a property named XXXSpecified which determines visibility</returns>
        /// <remarks>
        /// This is useful when default values need not be serialized.
        /// Under these situations XmlSerializer ignores properties based upon value:
        /// - DefaultValue: http://msdn.microsoft.com/en-us/library/system.componentmodel.defaultvalueattribute.aspx
        /// - Specified Properies: http://msdn.microsoft.com/en-us/library/bb402199.aspx
        /// - ShouldSerialize Methods: http://msdn.microsoft.com/en-us/library/53b8022e.aspx
        /// </remarks>
        public override ValueIgnoredDelegate GetValueIgnoredCallback(MemberInfo member)
        {
            Type objType = member.ReflectedType ?? member.DeclaringType;

            // look up specified property to see if exists
            GetterDelegate specifiedPropertyGetter = null;
            PropertyInfo   specProp = objType.GetProperty(member.Name + SpecifiedSuffix, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);

            // ensure is correct return type
            if (specProp != null && specProp.PropertyType == typeof(bool))
            {
                specifiedPropertyGetter = DynamicMethodGenerator.GetPropertyGetter(specProp);
            }

            // look up specified property to see if exists
            ProxyDelegate shouldSerializeProxy = null;
            MethodInfo    shouldSerialize      = objType.GetMethod(ShouldSerializePrefix + member.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);

            // ensure is correct return type
            if (shouldSerialize != null && shouldSerialize.ReturnType == typeof(bool) && shouldSerialize.GetParameters().Length == 0)
            {
                shouldSerializeProxy = DynamicMethodGenerator.GetMethodProxy(shouldSerialize);
            }

            // to be most efficient must create a different delegate for each of 8 combinations so not performing extra work

            DefaultValueAttribute defaultAttr = TypeCoercionUtility.GetAttribute <DefaultValueAttribute>(member);

            if (defaultAttr == null)
            {
                if (specifiedPropertyGetter == null)
                {
                    if (shouldSerializeProxy == null)
                    {
                        // situation 1: only need to check if equal to null
                        return(delegate(object target, object value)
                        {
                            return (value == null);
                        });
                    }

                    // situation 2: create a delegate which simply calls the should serialize method
                    return(delegate(object target, object value)
                    {
                        return
                        (value == null) ||
                        Object.Equals(false, shouldSerializeProxy(target));
                    });
                }

                if (shouldSerializeProxy == null)
                {
                    // situation 3: create a delegate which simply calls the specified property
                    return(delegate(object target, object value)
                    {
                        return
                        (value == null) ||
                        Object.Equals(false, specifiedPropertyGetter(target));
                    });
                }

                // situation 4: create a delegate which calls both the specified property and the should serialize method
                return(delegate(object target, object value)
                {
                    return
                    (value == null) ||
                    Object.Equals(false, shouldSerializeProxy(target)) ||
                    Object.Equals(false, specifiedPropertyGetter(target));
                });
            }

            // extract default value since cannot change (is constant in attribute)
            object defaultValue = defaultAttr.Value;

            if (specifiedPropertyGetter == null)
            {
                if (shouldSerializeProxy == null)
                {
                    // situation 5: create a specific delegate which only has to compare the default value to the current value
                    return(delegate(object target, object value)
                    {
                        return
                        (value == null) ||
                        Object.Equals(defaultValue, value);
                    });
                }

                // situation 6: create a specific delegate which both compares to default value and calls should serialize method
                return(delegate(object target, object value)
                {
                    return
                    (value == null) ||
                    Object.Equals(defaultValue, value) ||
                    Object.Equals(false, shouldSerializeProxy(target));
                });
            }

            if (shouldSerializeProxy == null)
            {
                // situation 7: create a specific delegate which both compares to default value and checks specified property
                return(delegate(object target, object value)
                {
                    return
                    (value == null) ||
                    Object.Equals(defaultValue, value) ||
                    Object.Equals(false, specifiedPropertyGetter(target));
                });
            }

            // situation 8: create a combined delegate which checks all states
            return(delegate(object target, object value)
            {
                return
                (value == null) ||
                Object.Equals(defaultValue, value) ||
                Object.Equals(false, shouldSerializeProxy(target)) ||
                Object.Equals(false, specifiedPropertyGetter(target));
            });
        }
            internal object CoerceType(Type targetType, object value)
            {
                bool isNullable = TypeCoercionUtility.IsNullable(targetType);

                if (value == null)
                {
                    if (!allowNullValueTypes &&
                        targetType.IsValueType &&
                        !isNullable)
                    {
                        throw new JsonTypeCoercionException(String.Format(TypeCoercionUtility.ErrorNullValueType, targetType.FullName));
                    }
                    return(value);
                }

                if (isNullable)
                {
                    // nullable types have a real underlying struct
                    Type[] genericArgs = targetType.GetGenericArguments();
                    if (genericArgs.Length == 1)
                    {
                        targetType = genericArgs[0];
                    }
                }

                Type actualType = value.GetType();

                if (targetType.IsAssignableFrom(actualType))
                {
                    return(value);
                }

                if (targetType.IsEnum)
                {
                    if (value is String)
                    {
                        if (!Enum.IsDefined(targetType, value))
                        {
                            // if isn't a defined value perhaps it is the JsonName
                            foreach (FieldInfo field in targetType.GetFields())
                            {
                                string jsonName = JsonNameAttribute.GetJsonName(field);
                                if (((string)value).Equals(jsonName))
                                {
                                    value = field.Name;
                                    break;
                                }
                            }
                        }

                        return(Enum.Parse(targetType, (string)value));
                    }
                    else
                    {
                        value = this.CoerceType(Enum.GetUnderlyingType(targetType), value);
                        return(Enum.ToObject(targetType, value));
                    }
                }

                if (value is IDictionary)
                {
                    Dictionary <string, MemberInfo> memberMap;
                    return(this.CoerceType(targetType, (IDictionary)value, out memberMap));
                }

                if (typeof(IEnumerable).IsAssignableFrom(targetType) &&
                    typeof(IEnumerable).IsAssignableFrom(actualType))
                {
                    return(this.CoerceList(targetType, actualType, (IEnumerable)value));
                }

                if (value is String)
                {
                    if (targetType == typeof(DateTime))
                    {
                        DateTime date;
                        if (DateTime.TryParse(
                                (string)value,
                                DateTimeFormatInfo.InvariantInfo,
                                DateTimeStyles.RoundtripKind | DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.NoCurrentDateDefault,
                                out date))
                        {
                            return(date);
                        }
                    }
                    else if (targetType == typeof(Guid))
                    {
                        // try-catch is pointless since will throw upon generic conversion
                        return(new Guid((string)value));
                    }
                    else if (targetType == typeof(Char))
                    {
                        if (((string)value).Length == 1)
                        {
                            return(((string)value)[0]);
                        }
                    }
                    else if (targetType == typeof(Uri))
                    {
                        Uri uri;
                        if (Uri.TryCreate((string)value, UriKind.RelativeOrAbsolute, out uri))
                        {
                            return(uri);
                        }
                    }
                    else if (targetType == typeof(Version))
                    {
                        // try-catch is pointless since will throw upon generic conversion
                        return(new Version((string)value));
                    }
                }
                else if (targetType == typeof(TimeSpan))
                {
                    return(new TimeSpan((long)this.CoerceType(typeof(Int64), value)));
                }

                TypeConverter converter = TypeDescriptor.GetConverter(targetType);

                if (converter.CanConvertFrom(actualType))
                {
                    return(converter.ConvertFrom(value));
                }

                converter = TypeDescriptor.GetConverter(actualType);
                if (converter.CanConvertTo(targetType))
                {
                    return(converter.ConvertTo(value, targetType));
                }

                try
                {
                    // fall back to basics
                    return(Convert.ChangeType(value, targetType));
                }
                catch (Exception ex)
                {
                    throw new JsonTypeCoercionException(
                              String.Format("Error converting {0} to {1}", value.GetType().FullName, targetType.FullName), ex);
                }
            }
		public void CoerceType_DictionaryToExpando_CoercesViaDictionaryCopy()
		{
			var input = new Hashtable
				{
					{ "One", 1 },
					{ "Two", 2 },
					{ "Three", 3 },
					{ "Four", 4 }
				};

			dynamic expected = new ExpandoObject();
			expected.One = 1;
			expected.Two = 2;
			expected.Three = 3;
			expected.Four = 4;

			var settings = new DataReaderSettings();
			var actual = new TypeCoercionUtility(settings, settings.AllowNullValueTypes).CoerceType(expected.GetType(), input);

			Assert.Equal(expected, actual);
		}
		public void CoerceType_ExpandoToGenericDictionary_CoercesViaGenericDictionaryCopy()
		{
			dynamic input = new ExpandoObject();
			input.One = 1;
			input.Two = 2;
			input.Three = 3;
			input.Four = 4;

			var expected = new Dictionary<string, object>
				{
					{ "One", 1 },
					{ "Two", 2 },
					{ "Three", 3 },
					{ "Four", 4 }
				};

			var settings = new DataReaderSettings();
			var actual = new TypeCoercionUtility(settings, settings.AllowNullValueTypes).CoerceType(typeof(Dictionary<string, object>), input);

			Assert.Equal(expected, actual);
		}
示例#9
0
        private object ConsumeArray(IStream <Token <ModelTokenType> > tokens, Type arrayType)
        {
            Token <ModelTokenType> token = tokens.Pop();

            // verify correct starting state
            if (token.TokenType != ModelTokenType.ArrayBegin)
            {
                throw new TokenException <ModelTokenType>(
                          token,
                          String.Format(ModelAnalyzer.ErrorExpectedArray, token.TokenType));
            }

            Type itemType = TypeCoercionUtility.GetElementType(arrayType);

            // if itemType was specified by caller, then isn't just a hint
            bool isItemTypeHint = (itemType == null);

            IList array = new List <object>();

            while (!tokens.IsCompleted)
            {
                token = tokens.Peek();

                // consume the next item
                object item;
                switch (token.TokenType)
                {
                case ModelTokenType.ArrayBegin:
                {
                    // array item
                    item = this.ConsumeArray(tokens, isItemTypeHint ? null : itemType);
                    break;
                }

                case ModelTokenType.ArrayEnd:
                {
                    // end of the array loop
                    tokens.Pop();
                    return(this.Coercion.CoerceCollection(arrayType, itemType, array));
                }

                case ModelTokenType.ObjectBegin:
                {
                    // object item
                    item = this.ConsumeObject(tokens, isItemTypeHint ? null : itemType);
                    break;
                }

                case ModelTokenType.Primitive:
                {
                    // primitive item
                    if (!this.TryReadFilters(tokens, out item))
                    {
                        // TODO: evaluate how to ensure that filters didn't take anything
                        token = tokens.Pop();
                        item  = (token != null) ? token.Value : null;
                    }

                    if (!isItemTypeHint)
                    {
                        item = this.Coercion.CoerceType(itemType, item);
                    }
                    break;
                }

                default:
                {
                    // these are invalid here
                    tokens.Pop();
                    throw new TokenException <ModelTokenType>(
                              token,
                              String.Format(ModelAnalyzer.ErrorExpectedArrayItem, token.TokenType));
                }
                }

                // establish common type
                itemType = TypeCoercionUtility.FindCommonType(itemType, item);

                // add item to the array
                array.Add(item);
            }

            // end of input
            throw new TokenException <ModelTokenType>(
                      ModelGrammar.TokenNone,
                      ModelAnalyzer.ErrorUnterminatedArray);
        }
示例#10
0
        private object ConsumeObject(IStream <Token <ModelTokenType> > tokens, Type targetType)
        {
            Token <ModelTokenType> token = tokens.Pop();

            // verify correct starting state
            if (token.TokenType != ModelTokenType.ObjectBegin)
            {
                throw new TokenException <ModelTokenType>(
                          token,
                          String.Format(ModelAnalyzer.ErrorExpectedObject, token.TokenType));
            }

            IDictionary <string, MemberMap> maps = this.Settings.Resolver.LoadMaps(targetType);

            Type   itemType    = TypeCoercionUtility.GetDictionaryItemType(targetType);
            object objectValue = this.Coercion.InstantiateObjectDefaultCtor(targetType);

            while (!tokens.IsCompleted)
            {
                token = tokens.Peek();

                // consume the property key
                string propertyName;
                switch (token.TokenType)
                {
                case ModelTokenType.Property:
                {
                    tokens.Pop();
                    propertyName = token.Name.LocalName;
                    break;
                }

                case ModelTokenType.ObjectEnd:
                {
                    // end of the object loop
                    tokens.Pop();
                    return(this.Coercion.CoerceType(targetType, objectValue));
                }

                default:
                {
                    // these are invalid here
                    tokens.Pop();
                    throw new TokenException <ModelTokenType>(
                              token,
                              String.Format(ModelAnalyzer.ErrorExpectedPropertyName, token.TokenType));
                }
                }

                MemberMap propertyMap;
                Type      propertyType;
                if (itemType != null)
                {
                    // properties all of the same type
                    propertyMap  = null;
                    propertyType = itemType;
                }
                else if ((maps != null) &&
                         maps.TryGetValue(propertyName, out propertyMap))
                {
                    propertyType = (propertyMap != null) ? propertyMap.Type : null;
                }
                else
                {
                    propertyMap  = null;
                    propertyType = null;
                }

                // consume the property value
                object propertyValue = this.ConsumeValue(tokens, propertyType);

                // set member to the result
                this.Coercion.SetMemberValue(objectValue, targetType, propertyMap, propertyName, propertyValue);
            }

            // end of input
            throw new TokenException <ModelTokenType>(
                      ModelGrammar.TokenNone,
                      ModelAnalyzer.ErrorUnterminatedObject);
        }
示例#11
0
            private object ReadObject(Type objectType)
            {
                if (this.Source[this.index] != JsonReader.OperatorObjectStart)
                {
                    throw new JsonDeserializationException(JsonReader.ErrorExpectedObject, this.index);
                }

                Type genericDictionaryType = null;
                Dictionary <string, MemberInfo> memberMap = null;
                Object result;

                if (objectType != null)
                {
                    result = this.Settings.Coercion.InstantiateObject(objectType, out memberMap);

                    if (memberMap == null)
                    {
                        // this allows specific IDictionary<string, T> to deserialize T
                        Type genericDictionary = objectType.GetInterface(JsonReader.TypeGenericIDictionary);
                        if (genericDictionary != null)
                        {
                            Type[] genericArgs = genericDictionary.GetGenericArguments();
                            if (genericArgs.Length == 2)
                            {
                                if (genericArgs[0] != typeof(String))
                                {
                                    throw new JsonDeserializationException(
                                              String.Format(JsonReader.ErrorGenericIDictionaryKeys, objectType),
                                              this.index);
                                }

                                if (genericArgs[1] != typeof(Object))
                                {
                                    genericDictionaryType = genericArgs[1];
                                }
                            }
                        }
                    }
                }
                else
                {
                    result = new Dictionary <String, Object>();
                }

                JsonToken token;

                do
                {
                    Type       memberType;
                    MemberInfo memberInfo;

                    // consume opening brace or delim
                    this.index++;
                    if (this.index >= this.SourceLength)
                    {
                        throw new JsonDeserializationException(JsonReader.ErrorUnterminatedObject, this.index);
                    }

                    // get next token
                    token = this.Tokenize(this.Settings.AllowUnquotedObjectKeys);
                    if (token == JsonToken.ObjectEnd)
                    {
                        break;
                    }

                    if (token != JsonToken.String && token != JsonToken.UnquotedName)
                    {
                        throw new JsonDeserializationException(JsonReader.ErrorExpectedPropertyName, this.index);
                    }

                    // parse object member value
                    string memberName = (token == JsonToken.String) ?
                                        (String)this.ReadString(null) :
                                        this.ReadUnquotedKey();

                    if (genericDictionaryType == null && memberMap != null)
                    {
                        // determine the type of the property/field
                        memberType = TypeCoercionUtility.GetMemberInfo(memberMap, memberName, out memberInfo);
                    }
                    else
                    {
                        memberType = genericDictionaryType;
                        memberInfo = null;
                    }

                    // get next token
                    token = this.Tokenize();
                    if (token != JsonToken.NameDelim)
                    {
                        throw new JsonDeserializationException(JsonReader.ErrorExpectedPropertyNameDelim, this.index);
                    }

                    // consume delim
                    this.index++;
                    if (this.index >= this.SourceLength)
                    {
                        throw new JsonDeserializationException(JsonReader.ErrorUnterminatedObject, this.index);
                    }

                    // parse object member value
                    object value = this.Read(memberType, false);

                    if (result is IDictionary)
                    {
                        if (objectType == null && this.Settings.IsTypeHintName(memberName))
                        {
                            result = this.Settings.Coercion.ProcessTypeHint((IDictionary)result, value as string, out objectType, out memberMap);
                        }
                        else
                        {
                            ((IDictionary)result)[memberName] = value;
                        }
                    }
                    else if (objectType.GetInterface(JsonReader.TypeGenericIDictionary) != null)
                    {
                        throw new JsonDeserializationException(
                                  String.Format(JsonReader.ErrorGenericIDictionary, objectType),
                                  this.index);
                    }
                    else
                    {
                        this.Settings.Coercion.SetMemberValue(result, memberType, memberInfo, value);
                    }

                    // get next token
                    token = this.Tokenize();
                } while (token == JsonToken.ValueDelim);

                if (token != JsonToken.ObjectEnd)
                {
                    throw new JsonDeserializationException(JsonReader.ErrorUnterminatedObject, this.index);
                }

                // consume closing brace
                this.index++;

                return(result);
            }
示例#12
0
    protected virtual object ReadObject(Type objectType)
    {
        if (Source[index] != '{')
        {
            throw new JsonDeserializationException("Expected JSON object.", index);
        }
        Type type = null;
        Dictionary <string, MemberInfo> memberMap = null;
        object obj;

        if (objectType != null)
        {
            obj = Settings.Coercion.InstantiateObject(objectType, out memberMap);
            if (memberMap == null)
            {
                Type @interface = objectType.GetInterface("System.Collections.Generic.IDictionary`2");
                if (@interface != null)
                {
                    Type[] genericArguments = @interface.GetGenericArguments();
                    if (genericArguments.Length == 2)
                    {
                        if (genericArguments[0] != typeof(string))
                        {
                            throw new JsonDeserializationException($"Types which implement Generic IDictionary<TKey, TValue> need to have string keys to be deserialized. ({objectType})", index);
                        }
                        if (genericArguments[1] != typeof(object))
                        {
                            type = genericArguments[1];
                        }
                    }
                }
            }
        }
        else
        {
            obj = new Dictionary <string, object>();
        }
        JsonToken jsonToken;

        do
        {
            index++;
            if (index >= SourceLength)
            {
                throw new JsonDeserializationException("Unterminated JSON object.", index);
            }
            jsonToken = Tokenize(Settings.AllowUnquotedObjectKeys);
            switch (jsonToken)
            {
            default:
                throw new JsonDeserializationException("Expected JSON object property name.", index);

            case JsonToken.String:
            case JsonToken.UnquotedName:
            {
                string     text = (jsonToken != JsonToken.String) ? ReadUnquotedKey() : ((string)ReadString(null));
                Type       type2;
                MemberInfo memberInfo;
                if (type == null && memberMap != null)
                {
                    type2 = TypeCoercionUtility.GetMemberInfo(memberMap, text, out memberInfo);
                }
                else
                {
                    type2      = type;
                    memberInfo = null;
                }
                jsonToken = Tokenize();
                if (jsonToken != JsonToken.NameDelim)
                {
                    throw new JsonDeserializationException("Expected JSON object property name delimiter.", index);
                }
                index++;
                if (index >= SourceLength)
                {
                    throw new JsonDeserializationException("Unterminated JSON object.", index);
                }
                object obj2 = Read(type2, typeIsHint: false);
                if (obj is IDictionary)
                {
                    if (objectType == null && Settings.IsTypeHintName(text))
                    {
                        obj = Settings.Coercion.ProcessTypeHint((IDictionary)obj, obj2 as string, out objectType, out memberMap);
                    }
                    else
                    {
                        ((IDictionary)obj)[text] = obj2;
                    }
                }
                else
                {
                    if (objectType.GetInterface("System.Collections.Generic.IDictionary`2") != null)
                    {
                        throw new JsonDeserializationException($"Types which implement Generic IDictionary<TKey, TValue> also need to implement IDictionary to be deserialized. ({objectType})", index);
                    }
                    Settings.Coercion.SetMemberValue(obj, type2, memberInfo, obj2);
                }
                goto IL_0270;
            }

            case JsonToken.ObjectEnd:
                break;
            }
            break;
IL_0270:
            jsonToken = Tokenize();
        }while (jsonToken == JsonToken.ValueDelim);
        if (jsonToken != JsonToken.ObjectEnd)
        {
            throw new JsonDeserializationException("Unterminated JSON object.", index);
        }
        index++;
        return(obj);
    }
示例#13
0
        private object ConsumeObject(IStream <Token <ModelTokenType> > tokens, Type targetType)
        {
            Token <ModelTokenType> token = tokens.Pop();

            // verify correct starting state
            if (token.TokenType != ModelTokenType.ObjectBegin)
            {
                throw new TokenException <ModelTokenType>(
                          token,
                          String.Format(ModelAnalyzer.ErrorExpectedObject, token.TokenType));
            }

            IDictionary <string, MemberMap> maps = this.Settings.Resolver.LoadMaps(targetType);

            Type   itemType    = TypeCoercionUtility.GetDictionaryItemType(targetType);
            object objectValue = this.Coercion.InstantiateObjectDefaultCtor(targetType);

            while (!tokens.IsCompleted)
            {
                token = tokens.Peek();

                // consume the property key
                string propertyName;
                switch (token.TokenType)
                {
                case ModelTokenType.Property:
                {
                    tokens.Pop();
                    propertyName = token.Name.LocalName;
                    break;
                }

                case ModelTokenType.ObjectEnd:
                {
                    // end of the object loop
                    tokens.Pop();
                    // modified by Diz to add finalizer method
                    var returnObject = this.Coercion.CoerceType(targetType, objectValue);

                    if (targetType != null)
                    {
                        foreach (var method in targetType.GetMethods(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance))
                        {
                            if (method.GetCustomAttributes(typeof(Json.JsonFinalizerAttribute), true).Length > 0)
                            {
                                method.Invoke(returnObject, null);
                                break;
                            }
                        }
                    }

                    return(returnObject);
                    // end modified
                }

                default:
                {
                    // these are invalid here
                    tokens.Pop();
                    throw new TokenException <ModelTokenType>(
                              token,
                              String.Format(ModelAnalyzer.ErrorExpectedPropertyName, token.TokenType));
                }
                }

                MemberMap propertyMap;
                Type      propertyType;
                if (itemType != null)
                {
                    // properties all of the same type
                    propertyMap  = null;
                    propertyType = itemType;
                }
                else if ((maps != null) &&
                         maps.TryGetValue(propertyName, out propertyMap))
                {
                    propertyType = (propertyMap != null) ? propertyMap.Type : null;
                }
                else
                {
                    propertyMap  = null;
                    propertyType = null;
                }

                // consume the property value
                object propertyValue = this.ConsumeValue(tokens, propertyType);

                // set member to the result
                this.Coercion.SetMemberValue(objectValue, targetType, propertyMap, propertyName, propertyValue);
            }

            // end of input
            throw new TokenException <ModelTokenType>(
                      ModelGrammar.TokenNone,
                      ModelAnalyzer.ErrorUnterminatedObject);
        }