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); }
/// <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)); } }
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); }
/// <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); }
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); }
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); }
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); }
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); }
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); }