/// <summary> /// Determines whether the type should be handled. /// </summary> /// <param name="type">The type.</param> /// <param name="serializerTypeInfo">The serializer type info.</param> /// <returns><c>true</c> if the type should be handled; otherwise, <c>false</c>.</returns> protected virtual bool ShouldTypeBeIgnored(Type type, XmlSerializerTypeInfo serializerTypeInfo) { if (type == null) { return(true); } // Note, although resharper says this isn't possible, it might be var fullName = type.GetSafeFullName(); if (string.IsNullOrWhiteSpace(fullName)) { serializerTypeInfo.AddTypeAsHandled(type); return(true); } // Ignore non-generic .NET if (!type.IsGenericTypeEx() && type.GetSafeFullName().StartsWith("System.")) { // Log.Debug("Non-generic .NET system type, can be ignored"); serializerTypeInfo.AddTypeAsHandled(type); return(true); } if (type.IsCOMObjectEx()) { serializerTypeInfo.AddTypeAsHandled(type); return(true); } return(serializerTypeInfo.ContainsKnownType(type) || serializerTypeInfo.IsTypeAlreadyHandled(type) || serializerTypeInfo.IsCollectionAlreadyHandled(type)); }
/// <summary> /// Gets the Data Contract serializer for a specific type. This method caches serializers so the /// performance can be improved when a serializer is used more than once. /// </summary> /// <param name="serializingType">The type that is currently (de)serializing.</param> /// <param name="typeToSerialize">The type to (de)serialize.</param> /// <param name="xmlName">Name of the property as known in XML.</param> /// <param name="rootNamespace">The root namespace.</param> /// <param name="additionalKnownTypes">A list of additional types to add to the known types.</param> /// <returns><see cref="DataContractSerializer" /> for the given type.</returns> /// <exception cref="ArgumentNullException">The <paramref name="serializingType" /> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException">The <paramref name="typeToSerialize" /> is <c>null</c>.</exception> /// <exception cref="ArgumentException">The <paramref name="xmlName" /> is <c>null</c> or whitespace.</exception> public virtual DataContractSerializer GetDataContractSerializer(Type serializingType, Type typeToSerialize, string xmlName, string rootNamespace = null, List <Type> additionalKnownTypes = null) { Argument.IsNotNull("serializingType", serializingType); Argument.IsNotNull("typeToSerialize", typeToSerialize); Argument.IsNotNullOrWhitespace("xmlName", xmlName); string key = string.Format("{0}|{1}", typeToSerialize.GetSafeFullName(), xmlName); return(_dataContractSerializersCache.GetFromCacheOrFetch(key, () => { Log.Debug("Getting known types for xml serialization of '{0}'", typeToSerialize.GetSafeFullName()); var serializerTypeInfo = new XmlSerializerTypeInfo(serializingType, typeToSerialize, additionalKnownTypes); GetKnownTypes(typeToSerialize, serializerTypeInfo); var knownTypesViaAttributes = GetKnownTypesViaAttributes(serializingType); foreach (var knownTypeViaAttribute in knownTypesViaAttributes) { GetKnownTypes(knownTypeViaAttribute, serializerTypeInfo); } if (additionalKnownTypes != null) { foreach (var additionalKnownType in additionalKnownTypes) { GetKnownTypes(additionalKnownType, serializerTypeInfo); } } var xmlSerializer = new DataContractSerializer(typeToSerialize, xmlName, rootNamespace ?? string.Empty, serializerTypeInfo.KnownTypes); return xmlSerializer; })); }
/// <summary> /// Adds the type to the known types if the type is serializable. /// </summary> /// <param name="typeToAdd">The type to add.</param> /// <param name="serializerTypeInfo">The serializer type info.</param> /// <returns><c>true</c> if the type is serializable; otherwise <c>false</c>.</returns> protected virtual bool AddTypeToKnownTypesIfSerializable(Type typeToAdd, XmlSerializerTypeInfo serializerTypeInfo) { // Collection first, this collection of types is smaller so if we have a hit, we exit sooner if (serializerTypeInfo.IsCollectionAlreadyHandled(typeToAdd)) { return(true); } if (serializerTypeInfo.ContainsKnownType(typeToAdd)) { return(true); } serializerTypeInfo.AddCollectionAsHandled(typeToAdd); // If this is a special collection type (generic), then we need to make sure that if the inner type is // an interface, we do not add it again if already added. // See this issue http://catel.codeplex.com/workitem/7167 if (serializerTypeInfo.IsSpecialCollectionType(typeToAdd)) { // Always ignore return(false); } return(serializerTypeInfo.AddKnownType(typeToAdd)); }
private void AddTypeMembers(Type type, XmlSerializerTypeInfo serializerTypeInfo) { var typesToCheck = new List <Type>(); var isModelBase = (type == typeof(ModelBase)) || typeof(ModelBase).IsAssignableFromEx(type); if (isModelBase) { // No need to check members, they will be serialized by ModelBase //var catelTypeInfo = PropertyDataManager.Default.GetCatelTypeInfo(type); //var modelBaseProperties = catelTypeInfo.GetCatelProperties(); //foreach (var modelBaseProperty in modelBaseProperties) //{ // typesToCheck.Add(modelBaseProperty.Value.Type); //} } else { bool allowNonPublicReflection = AllowNonPublicReflection(type); // Fields var fields = type.GetFieldsEx(BindingFlagsHelper.GetFinalBindingFlags(false, false, allowNonPublicReflection)); foreach (var field in fields) { typesToCheck.Add(field.FieldType); } // Properties var properties = type.GetPropertiesEx(BindingFlagsHelper.GetFinalBindingFlags(false, false, allowNonPublicReflection)); foreach (var property in properties) { typesToCheck.Add(property.PropertyType); } } foreach (var typeToCheck in typesToCheck) { if (serializerTypeInfo.IsTypeAlreadyHandled(typeToCheck)) { continue; } if (!IsTypeSerializable(typeToCheck, serializerTypeInfo)) { serializerTypeInfo.AddTypeAsHandled(typeToCheck); continue; } var propertyTypeFullName = typeToCheck.GetSafeFullName(); if (propertyTypeFullName == null) { serializerTypeInfo.AddTypeAsHandled(typeToCheck); continue; } GetKnownTypes(typeToCheck, serializerTypeInfo); } }
private void AddTypeMembers(Type type, XmlSerializerTypeInfo serializerTypeInfo) { var isModelBase = (type == typeof(ModelBase)) || typeof(ModelBase).IsAssignableFromEx(type); if (isModelBase) { var catelTypeInfo = PropertyDataManager.Default.GetCatelTypeInfo(type); var modelBaseProperties = catelTypeInfo.GetCatelProperties(); foreach (var modelBaseProperty in modelBaseProperties) { var propertyType = modelBaseProperty.Value.Type; if (propertyType.FullName != null) { GetKnownTypes(propertyType, serializerTypeInfo); } else { serializerTypeInfo.AddTypeAsHandled(propertyType); } } } else { bool allowNonPublicReflection = AllowNonPublicReflection(type); // Fields var fields = type.GetFieldsEx(BindingFlagsHelper.GetFinalBindingFlags(false, false, allowNonPublicReflection)); foreach (var field in fields) { var fieldType = field.FieldType; if (fieldType.FullName != null) { GetKnownTypes(fieldType, serializerTypeInfo); } else { serializerTypeInfo.AddTypeAsHandled(fieldType); } } // Properties var properties = type.GetPropertiesEx(BindingFlagsHelper.GetFinalBindingFlags(false, false, allowNonPublicReflection)); foreach (var property in properties) { var propertyType = property.PropertyType; if (propertyType.FullName != null) { GetKnownTypes(propertyType, serializerTypeInfo); } else { serializerTypeInfo.AddTypeAsHandled(propertyType); } } } }
/// <summary> /// Gets the known types of IEnumerable type. /// </summary> /// <param name="type">The type.</param> /// <param name="serializerTypeInfo">The serializer type info.</param> /// <returns>Array of <see cref="Type"/> that are found in the object type.</returns> private void GetKnownTypesForItems(Type type, XmlSerializerTypeInfo serializerTypeInfo) { if (type.ImplementsInterfaceEx <IEnumerable>()) { foreach (var argument in type.GetGenericArgumentsEx()) { GetKnownTypes(argument, serializerTypeInfo); } } }
private void GetKnownTypesForItemsInstance(object obj, XmlSerializerTypeInfo serializerTypeInfo) { var ienumerable = obj as IEnumerable; if (ienumerable != null) { foreach (var item in ienumerable) { GetKnownTypesForInstance(item, serializerTypeInfo); } } }
/// <summary> /// Adds the type to the known types if the type is serializable. /// </summary> /// <param name="typeToAdd">The type to add.</param> /// <param name="serializerTypeInfo">The serializer type info.</param> /// <returns><c>true</c> if the type is serializable; otherwise <c>false</c>.</returns> protected virtual bool AddTypeToKnownTypesIfSerializable(Type typeToAdd, XmlSerializerTypeInfo serializerTypeInfo) { // Collection first, this collection of types is smaller so if we have a hit, we exit sooner if (serializerTypeInfo.IsCollectionAlreadyHandled(typeToAdd)) { return(true); } if (serializerTypeInfo.ContainsKnownType(typeToAdd)) { return(true); } serializerTypeInfo.AddCollectionAsHandled(typeToAdd); // If this is a special collection type (generic), then we need to make sure that if the inner type is // an interface, we do not add it again if already added. // See this issue http://catel.codeplex.com/workitem/7167 if (serializerTypeInfo.IsSpecialCollectionType(typeToAdd)) { // Always ignore as a test return(false); // TODO: Also check interfaces // // It might be a base type, loop all // var baseType = typeToAdd; // while (baseType != null) // { // if (baseType.IsGenericTypeEx()) // { // if (baseType.GetGenericArgumentsEx()[0].IsInterfaceEx()) // { // var genericTypeDefinition = baseType.GetGenericTypeDefinitionEx(); // var allPossibleMatchingTypes = (from type in serializerTypeInfo.SpecialGenericCollectionTypes // where type.GetGenericTypeDefinitionEx() == genericTypeDefinition // select type).ToList(); // if (allPossibleMatchingTypes.Count > 0) // { // Log.Debug("Skipping type '{0}' because there already exists such a type which does the same", typeToAdd.GetSafeFullName()); // return false; // } // } // } // baseType = baseType.GetBaseTypeEx(); // } } return(serializerTypeInfo.AddKnownType(typeToAdd)); }
protected virtual void GetKnownTypesForInstance(object obj, XmlSerializerTypeInfo serializerTypeInfo) { if (obj == null) { return; } var objectType = obj.GetType(); if (ShouldTypeBeIgnored(objectType, serializerTypeInfo)) { return; } GetKnownTypesForItemsInstance(obj, serializerTypeInfo); GetKnownTypes(objectType, serializerTypeInfo); }
/// <summary> /// Gets the known types for a specific object instance. /// </summary> /// <param name="obj">The object to retrieve the known types for.</param> /// <param name="serializerTypeInfo">The serializer type info.</param> /// <returns>Array of <see cref="Type"/> that are found in the object instance.</returns> protected virtual void GetKnownTypesForInstance(object obj, XmlSerializerTypeInfo serializerTypeInfo) { if (obj == null) { return; } Type objectType = obj.GetType(); if (ShouldTypeBeIgnored(objectType, serializerTypeInfo)) { return; } GetKnownTypes(objectType, serializerTypeInfo); // Note: the code below is specific for instants of objects, cannot be moved to the GetKnownTypes if (objectType == typeof(List <KeyValuePair <string, object> >)) { foreach (var keyValuePair in ((List <KeyValuePair <string, object> >)obj)) { GetKnownTypesForInstance(keyValuePair.Value, serializerTypeInfo); } } else if (objectType == typeof(List <PropertyValue>)) { foreach (var propertyValue in ((List <PropertyValue>)obj)) { GetKnownTypesForInstance(propertyValue.Value, serializerTypeInfo); } } // Collections might contain interface types, so if this is an IEnumerable, we need to loop all the instances (performance warning!) else if ((obj is IEnumerable) && (!(obj is string))) { var objAsIEnumerable = (IEnumerable)obj; foreach (object item in objAsIEnumerable) { if (item != null) { GetKnownTypesForInstance(item, serializerTypeInfo); } } } }
/// <summary> /// Determines whether the type should be handled. /// </summary> /// <param name="type">The type.</param> /// <param name="serializerTypeInfo">The serializer type info.</param> /// <returns><c>true</c> if the type should be handled; otherwise, <c>false</c>.</returns> protected virtual bool ShouldTypeBeIgnored(Type type, XmlSerializerTypeInfo serializerTypeInfo) { if (type == null) { return(true); } // Never include generic type definitions, otherwise we will get this: // Error while getting known types for Type 'Catel.Tests.Data.PropertyDataManagerFacts+SupportsGenericClasses+GenericClass`1[T]'. The type must not be an open or partial generic class. if (type.IsGenericTypeDefinitionEx()) { return(true); } // Note, although resharper says this isn't possible, it might be var fullName = type.GetSafeFullName(false); if (string.IsNullOrWhiteSpace(fullName)) { serializerTypeInfo.AddTypeAsHandled(type); return(true); } // Ignore non-generic .NET if (!type.IsGenericTypeEx() && fullName.StartsWith("System.")) { // Log.Debug("Non-generic .NET system type, can be ignored"); serializerTypeInfo.AddTypeAsHandled(type); return(true); } if (type.IsCOMObjectEx()) { serializerTypeInfo.AddTypeAsHandled(type); return(true); } return(serializerTypeInfo.ContainsKnownType(type) || serializerTypeInfo.IsTypeAlreadyHandled(type) || serializerTypeInfo.IsCollectionAlreadyHandled(type)); }
/// <summary> /// Gets the Data Contract serializer for a specific type. This method caches serializers so the /// performance can be improved when a serializer is used more than once. /// </summary> /// <param name="serializingType">The type that is currently (de)serializing.</param> /// <param name="typeToSerialize">The type to (de)serialize.</param> /// <param name="xmlName">Name of the property as known in XML.</param> /// <param name="rootNamespace">The root namespace.</param> /// <param name="serializingObject">The object to create the serializer for. When the object is not <c>null</c>, the types that are /// a child object of this object are added to the known types of the serializer.</param> /// <param name="additionalKnownTypes">A list of additional types to add to the known types.</param> /// <returns><see cref="DataContractSerializer" /> for the given type.</returns> /// <exception cref="ArgumentNullException">The <paramref name="serializingType" /> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException">The <paramref name="typeToSerialize" /> is <c>null</c>.</exception> /// <exception cref="ArgumentException">The <paramref name="xmlName" /> is <c>null</c> or whitespace.</exception> public virtual DataContractSerializer GetDataContractSerializer(Type serializingType, Type typeToSerialize, string xmlName, string rootNamespace = null, object serializingObject = null, List<Type> additionalKnownTypes = null) { Argument.IsNotNull("serializingType", serializingType); Argument.IsNotNull("typeToSerialize", typeToSerialize); Argument.IsNotNullOrWhitespace("xmlName", xmlName); string key = string.Format("{0}|{1}", typeToSerialize.GetSafeFullName(), xmlName); return _dataContractSerializersCache.GetFromCacheOrFetch(key, () => { Log.Debug("Getting known types for xml serialization of '{0}'", typeToSerialize.GetSafeFullName()); var serializerTypeInfo = new XmlSerializerTypeInfo(serializingType, typeToSerialize, additionalKnownTypes); if (serializingObject != null) { GetKnownTypesForInstance(serializingObject, serializerTypeInfo); } else { GetKnownTypes(typeToSerialize, serializerTypeInfo); } var knownTypesViaAttributes = GetKnownTypesViaAttributes(serializingType); foreach (var knownTypeViaAttribute in knownTypesViaAttributes) { GetKnownTypes(knownTypeViaAttribute, serializerTypeInfo); } if (additionalKnownTypes != null) { foreach (var additionalKnownType in additionalKnownTypes) { GetKnownTypes(additionalKnownType, serializerTypeInfo); } } var xmlSerializer = new DataContractSerializer(typeToSerialize, xmlName, rootNamespace ?? string.Empty, serializerTypeInfo.KnownTypes); return xmlSerializer; }); }
/// <summary> /// Determines whether the specified type is serializable. /// </summary> /// <param name="type">The type.</param> /// <param name="serializerTypeInfo">The serializer type information.</param> /// <returns><c>true</c> if the specified type is serializable; otherwise, <c>false</c>.</returns> protected virtual bool IsTypeSerializable(Type type, XmlSerializerTypeInfo serializerTypeInfo) { if (type is null) { return(false); } // DataContract attribute if (type.IsDecoratedWithAttribute <DataContractAttribute>()) { return(true); } #if NET || NETCORE || NETSTANDARD // Implements ISerializable if (type.ImplementsInterfaceEx <ISerializable>()) { return(true); } #endif // Implements IXmlSerializer if (type.ImplementsInterfaceEx <System.Xml.Serialization.IXmlSerializable>()) { return(true); } // Is ModelBase if (type.IsModelBase()) { return(true); } // IsSerializable return(type.IsSerializableEx()); }
/// <summary> /// Gets the known types for a specific type. This method caches the lists so the /// performance can be improved when a type is used more than once. /// </summary> /// <param name="serializingType">The type that is currently (de)serializing.</param> /// <param name="typeToSerialize">The type to (de)serialize.</param> /// <param name="additionalKnownTypes">A list of additional types to add to the known types.</param> /// <returns><see cref="DataContractSerializer" /> for the given type.</returns> /// <exception cref="ArgumentNullException">The <paramref name="serializingType" /> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException">The <paramref name="typeToSerialize" /> is <c>null</c>.</exception> public virtual List <Type> GetKnownTypes(Type serializingType, Type typeToSerialize, List <Type> additionalKnownTypes = null) { Argument.IsNotNull("serializingType", serializingType); Argument.IsNotNull("typeToSerialize", typeToSerialize); var serializingTypeName = serializingType.GetSafeFullName(false); var typeToSerializeName = typeToSerialize.GetSafeFullName(false); var key = string.Format("{0}|{1}|{2}", serializingTypeName, typeToSerializeName, additionalKnownTypes?.Count ?? 0); return(_knownTypesCache.GetFromCacheOrFetch(key, () => { #if ENABLE_DETAILED_LOGGING Log.Debug("Getting known types for xml serialization of '{0}'", typeToSerializeName); #endif var serializerTypeInfo = new XmlSerializerTypeInfo(serializingType, typeToSerialize, additionalKnownTypes); GetKnownTypes(typeToSerialize, serializerTypeInfo); var knownTypesViaAttributes = GetKnownTypesViaAttributes(serializingType); foreach (var knownTypeViaAttribute in knownTypesViaAttributes) { GetKnownTypes(knownTypeViaAttribute, serializerTypeInfo); } if (additionalKnownTypes != null) { foreach (var additionalKnownType in additionalKnownTypes) { GetKnownTypes(additionalKnownType, serializerTypeInfo); } } return serializerTypeInfo.KnownTypes.ToList(); })); }
/// <summary> /// Determines whether the specified type is serializable. /// </summary> /// <param name="type">The type.</param> /// <param name="serializerTypeInfo">The serializer type information.</param> /// <returns><c>true</c> if the specified type is serializable; otherwise, <c>false</c>.</returns> protected virtual bool IsTypeSerializable(Type type, XmlSerializerTypeInfo serializerTypeInfo) { if (type == null) { return(false); } // DataContract attribute if (AttributeHelper.IsDecoratedWithAttribute <DataContractAttribute>(type)) { return(true); } #if NET // Implements ISerializable if (type.ImplementsInterfaceEx <ISerializable>()) { return(true); } #endif // Implements IXmlSerializer if (type.ImplementsInterfaceEx <System.Xml.Serialization.IXmlSerializable>()) { return(true); } // Is ModelBase if (typeof(ModelBase).IsAssignableFromEx(type)) { return(true); } // IsSerializable return(type.IsSerializableEx()); }
/// <summary> /// Gets the known types of IEnumerable type. /// </summary> /// <param name="type">The type.</param> /// <param name="serializerTypeInfo">The serializer type info.</param> /// <returns>Array of <see cref="Type"/> that are found in the object type.</returns> private void GetKnownTypesForItems(Type type, XmlSerializerTypeInfo serializerTypeInfo) { if (type.ImplementsInterfaceEx<IEnumerable>()) { foreach (var argument in type.GetGenericArgumentsEx()) { GetKnownTypes(argument, serializerTypeInfo); } } }
/// <summary> /// Gets the known types inside the specific type. /// </summary> /// <param name="type">The type.</param> /// <param name="serializerTypeInfo">The serializer type info.</param> /// <param name="resolveAbstractClassesAndInterfaces">if set to <c>true</c> [resolve abstract classes and interfaces].</param> /// <returns>Array of <see cref="Type" /> that are found in the object type.</returns> protected virtual void GetKnownTypes(Type type, XmlSerializerTypeInfo serializerTypeInfo, bool resolveAbstractClassesAndInterfaces = true) { if (ShouldTypeBeIgnored(type, serializerTypeInfo)) { return; } #if ENABLE_DETAILED_LOGGING Log.Debug("Getting known types for '{0}'", type.GetSafeFullName()); #endif GetKnownTypesForItems(type, serializerTypeInfo); // If this is an interface or abstract, we need to retieve all items that might possible implement or derive var isInterface = type.IsInterfaceEx(); var isAbstract = type.IsAbstractEx(); if (isInterface || isAbstract) { if (!serializerTypeInfo.IsTypeAlreadyHandled(type) && resolveAbstractClassesAndInterfaces) { // Interfaces / abstract classes are not a type, and in fact a LOT of types can be added (in fact every object implementing // the interface). For serialization, this is not a problem (we know the exact type), but for deserialization this IS an // issue because we should expect EVERY type that implements the type in the whole AppDomain. // This is huge performance hit, but it's the cost for dynamic easy on-the-fly serialization in WPF and Silverlight. Luckily // we already implemented caching. // Don't check this type again in children checks serializerTypeInfo.AddTypeAsHandled(type); #if ENABLE_DETAILED_LOGGING Log.Debug("Type is an interface / abstract class, checking all types implementing / deriving"); #endif if (isInterface) { var typesImplementingInterface = TypeCache.GetTypesImplementingInterface(type); foreach (var typeImplementingInterface in typesImplementingInterface) { if (typeImplementingInterface != type) { GetKnownTypes(typeImplementingInterface, serializerTypeInfo); } } } if (isAbstract) { var typesDerivingFromClass = TypeCache.GetTypes(type.IsAssignableFromEx); foreach (var typeDerivingFromClass in typesDerivingFromClass) { if (typeDerivingFromClass != type) { GetKnownTypes(typeDerivingFromClass, serializerTypeInfo); } } } #if ENABLE_DETAILED_LOGGING Log.Debug("Finished checking all types implementing / deriving"); #endif } // The interface itself is ignored return; } if (serializerTypeInfo.IsSpecialCollectionType(type) && !type.IsInterfaceEx()) { #if ENABLE_DETAILED_LOGGING Log.Debug("Type is a special collection type, adding it to the array of known types"); #endif AddTypeToKnownTypesIfSerializable(type, serializerTypeInfo); } // Fix generics if (type.GetSafeFullName().StartsWith("System.")) { var genericArguments = type.GetGenericArgumentsEx(); foreach (var genericArgument in genericArguments) { #if ENABLE_DETAILED_LOGGING Log.Debug("Retrieving known types for generic argument '{0}' of '{1}'", genericArgument.GetSafeFullName(), type.GetSafeFullName()); #endif GetKnownTypes(genericArgument, serializerTypeInfo); } return; } if (!AddTypeToKnownTypesIfSerializable(type, serializerTypeInfo)) { serializerTypeInfo.AddTypeAsHandled(type); } AddTypeMembers(type, serializerTypeInfo); // If this isn't the base type, check that as well var baseType = type.GetBaseTypeEx(); if (baseType != null) { #if ENABLE_DETAILED_LOGGING Log.Debug("Checking base type of '{0}' for known types", type.GetSafeFullName()); #endif if (baseType.FullName != null) { GetKnownTypes(baseType, serializerTypeInfo); } else { serializerTypeInfo.AddTypeAsHandled(baseType); } } // Last but not least, check if the type is decorated with KnownTypeAttributes var knowTypesByAttributes = GetKnownTypesViaAttributes(type); if (knowTypesByAttributes.Length > 0) { #if ENABLE_DETAILED_LOGGING Log.Debug("Found {0} additional known types for type '{1}'", knowTypesByAttributes.Length, type.GetSafeFullName()); #endif foreach (var knownTypeByAttribute in knowTypesByAttributes) { var attributeType = knownTypeByAttribute; var attributeTypeFullName = attributeType.GetSafeFullName(); if (attributeTypeFullName != null) { GetKnownTypes(knownTypeByAttribute, serializerTypeInfo); } else { serializerTypeInfo.AddTypeAsHandled(attributeType); } } } }
/// <summary> /// Determines whether the type should be handled. /// </summary> /// <param name="type">The type.</param> /// <param name="serializerTypeInfo">The serializer type info.</param> /// <returns><c>true</c> if the type should be handled; otherwise, <c>false</c>.</returns> protected virtual bool ShouldTypeBeIgnored(Type type, XmlSerializerTypeInfo serializerTypeInfo) { if (type == null) { return true; } // Never include generic type definitions, otherwise we will get this: // Error while getting known types for Type 'Catel.Test.Data.PropertyDataManagerFacts+SupportsGenericClasses+GenericClass`1[T]'. The type must not be an open or partial generic class. if (type.IsGenericTypeDefinitionEx()) { return true; } // Note, although resharper says this isn't possible, it might be var fullName = type.GetSafeFullName(); if (string.IsNullOrWhiteSpace(fullName)) { serializerTypeInfo.AddTypeAsHandled(type); return true; } // Ignore non-generic .NET if (!type.IsGenericTypeEx() && fullName.StartsWith("System.")) { // Log.Debug("Non-generic .NET system type, can be ignored"); serializerTypeInfo.AddTypeAsHandled(type); return true; } if (type.IsCOMObjectEx()) { serializerTypeInfo.AddTypeAsHandled(type); return true; } return serializerTypeInfo.ContainsKnownType(type) || serializerTypeInfo.IsTypeAlreadyHandled(type) || serializerTypeInfo.IsCollectionAlreadyHandled(type); }
/// <summary> /// Gets the known types inside the specific type. /// </summary> /// <param name="type">The type.</param> /// <param name="serializerTypeInfo">The serializer type info.</param> /// <returns>Array of <see cref="Type"/> that are found in the object type.</returns> protected virtual void GetKnownTypes(Type type, XmlSerializerTypeInfo serializerTypeInfo) { if (ShouldTypeBeIgnored(type, serializerTypeInfo)) { return; } Log.Debug("Getting known types for '{0}'", type.GetSafeFullName()); // If this is an interface, HOUSTON, WE HAVE A PROBLEM if (type.IsInterfaceEx()) { if (!serializerTypeInfo.IsTypeAlreadyHandled(type)) { Log.Debug("Type is an interface, checking all types deriving from this interface"); // Don't check this interface again in children checks serializerTypeInfo.AddTypeAsHandled(type); // Interfaces are not a type, and in fact a LOT of types can be added (in fact every object implementing the interface). For // serialization, this is not a problem (we know the exact type), but for deserialization this IS an issue because we should // expect EVERY type that implements the type in the whole AppDomain. // This is huge performance hit, but it's the cost for dynamic easy on-the-fly serialization in WPF and Silverlight. Luckily // we already implemented caching. var typesDerivingFromInterface = TypeCache.GetTypes(t => t.ImplementsInterfaceEx(type)); foreach (var typeDerivingFromInterface in typesDerivingFromInterface) { if (typeDerivingFromInterface != type) { GetKnownTypes(typeDerivingFromInterface, serializerTypeInfo); } } Log.Debug("Finished checking all types deriving from this interface"); } // The interface itself is ignored return; } if (serializerTypeInfo.IsSpecialCollectionType(type) && !type.IsInterfaceEx()) { Log.Debug("Type is a special collection type, adding it to the array of known types"); AddTypeToKnownTypesIfSerializable(type, serializerTypeInfo); } // Fix generics if (type.GetSafeFullName().StartsWith("System.")) { var genericArguments = type.GetGenericArgumentsEx(); foreach (var genericArgument in genericArguments) { Log.Debug("Retrieving known types for generic argument '{0}' of '{1}'", genericArgument.GetSafeFullName(), type.GetSafeFullName()); GetKnownTypes(genericArgument, serializerTypeInfo); } return; } if (!AddTypeToKnownTypesIfSerializable(type, serializerTypeInfo)) { serializerTypeInfo.AddTypeAsHandled(type); } AddTypeMembers(type, serializerTypeInfo); // If this isn't the base type, check that as well var baseType = type.GetBaseTypeEx(); if (baseType != null) { Log.Debug("Checking base type of '{0}' for known types", type.GetSafeFullName()); if (baseType.FullName != null) { GetKnownTypes(baseType, serializerTypeInfo); } else { serializerTypeInfo.AddTypeAsHandled(baseType); } } // Last but not least, check if the type is decorated with KnownTypeAttributes var knowTypesByAttributes = GetKnownTypesViaAttributes(type); if (knowTypesByAttributes.Length > 0) { Log.Debug("Found {0} additional known types for type '{1}'", knowTypesByAttributes.Length, type.GetSafeFullName()); foreach (var knownTypeByAttribute in knowTypesByAttributes) { var attributeType = knownTypeByAttribute; if (attributeType.FullName != null) { GetKnownTypes(knownTypeByAttribute, serializerTypeInfo); } else { serializerTypeInfo.AddTypeAsHandled(attributeType); } } } }
/// <summary> /// Gets the known types for a specific object instance. /// </summary> /// <param name="obj">The object to retrieve the known types for.</param> /// <param name="serializerTypeInfo">The serializer type info.</param> /// <returns>Array of <see cref="Type"/> that are found in the object instance.</returns> protected virtual void GetKnownTypesForInstance(object obj, XmlSerializerTypeInfo serializerTypeInfo) { if (obj == null) { return; } Type objectType = obj.GetType(); if (ShouldTypeBeIgnored(objectType, serializerTypeInfo)) { return; } GetKnownTypes(objectType, serializerTypeInfo); // Note: the code below is specific for instants of objects, cannot be moved to the GetKnownTypes if (objectType == typeof(List<KeyValuePair<string, object>>)) { foreach (var keyValuePair in ((List<KeyValuePair<string, object>>)obj)) { GetKnownTypesForInstance(keyValuePair.Value, serializerTypeInfo); } } else if (objectType == typeof(List<PropertyValue>)) { foreach (var propertyValue in ((List<PropertyValue>)obj)) { GetKnownTypesForInstance(propertyValue.Value, serializerTypeInfo); } } // Collections might contain interface types, so if this is an IEnumerable, we need to loop all the instances (performance warning!) else if ((obj is IEnumerable) && (!(obj is string))) { var objAsIEnumerable = (IEnumerable)obj; foreach (object item in objAsIEnumerable) { if (item != null) { GetKnownTypesForInstance(item, serializerTypeInfo); } } } }
private void AddTypeMembers(Type type, XmlSerializerTypeInfo serializerTypeInfo) { var typesToCheck = new List<Type>(); var isModelBase = type.IsModelBase(); if (isModelBase) { // No need to check members, they will be serialized by ModelBase //var catelTypeInfo = PropertyDataManager.Default.GetCatelTypeInfo(type); //var modelBaseProperties = catelTypeInfo.GetCatelProperties(); //foreach (var modelBaseProperty in modelBaseProperties) //{ // typesToCheck.Add(modelBaseProperty.Value.Type); //} } else { var allowNonPublicReflection = AllowNonPublicReflection(type); // Fields var fields = type.GetFieldsEx(BindingFlagsHelper.GetFinalBindingFlags(false, false, allowNonPublicReflection)); foreach (var field in fields) { typesToCheck.Add(field.FieldType); } // Properties var properties = type.GetPropertiesEx(BindingFlagsHelper.GetFinalBindingFlags(false, false, allowNonPublicReflection)); foreach (var property in properties) { typesToCheck.Add(property.PropertyType); } } foreach (var typeToCheck in typesToCheck) { if (serializerTypeInfo.IsTypeAlreadyHandled(typeToCheck)) { continue; } if (!IsTypeSerializable(typeToCheck, serializerTypeInfo)) { serializerTypeInfo.AddTypeAsHandled(typeToCheck); continue; } var propertyTypeFullName = typeToCheck.GetSafeFullName(); if (propertyTypeFullName == null) { serializerTypeInfo.AddTypeAsHandled(typeToCheck); continue; } GetKnownTypes(typeToCheck, serializerTypeInfo); } }
/// <summary> /// Determines whether the type should be handled. /// </summary> /// <param name="type">The type.</param> /// <param name="serializerTypeInfo">The serializer type info.</param> /// <returns><c>true</c> if the type should be handled; otherwise, <c>false</c>.</returns> protected virtual bool ShouldTypeBeIgnored(Type type, XmlSerializerTypeInfo serializerTypeInfo) { if (type == null) { return true; } // Note, although resharper says this isn't possible, it might be if (type.FullName == null) { serializerTypeInfo.AddTypeAsHandled(type); return true; } // Ignore non-generic .NET if (!type.IsGenericTypeEx() && type.GetSafeFullName().StartsWith("System.")) { // Log.Debug("Non-generic .NET system type, can be ignored"); serializerTypeInfo.AddTypeAsHandled(type); return true; } return serializerTypeInfo.ContainsKnownType(type) || serializerTypeInfo.IsTypeAlreadyHandled(type); }
/// <summary> /// Determines whether the specified type is serializable. /// </summary> /// <param name="type">The type.</param> /// <param name="serializerTypeInfo">The serializer type information.</param> /// <returns><c>true</c> if the specified type is serializable; otherwise, <c>false</c>.</returns> protected virtual bool IsTypeSerializable(Type type, XmlSerializerTypeInfo serializerTypeInfo) { if (type == null) { return false; } // DataContract attribute if (AttributeHelper.IsDecoratedWithAttribute<DataContractAttribute>(type)) { return true; } #if NET // Implements ISerializable if (type.ImplementsInterfaceEx<ISerializable>()) { return true; } #endif // Implements IXmlSerializer if (type.ImplementsInterfaceEx<System.Xml.Serialization.IXmlSerializable>()) { return true; } // Is ModelBase if (type.IsModelBase()) { return true; } // IsSerializable return type.IsSerializableEx(); }
/// <summary> /// Adds the type to the known types if the type is serializable. /// </summary> /// <param name="typeToAdd">The type to add.</param> /// <param name="serializerTypeInfo">The serializer type info.</param> /// <returns><c>true</c> if the type is serializable; otherwise <c>false</c>.</returns> protected virtual bool AddTypeToKnownTypesIfSerializable(Type typeToAdd, XmlSerializerTypeInfo serializerTypeInfo) { // Collection first, this collection of types is smaller so if we have a hit, we exit sooner if (serializerTypeInfo.IsCollectionAlreadyHandled(typeToAdd)) { return true; } if (serializerTypeInfo.ContainsKnownType(typeToAdd)) { return true; } serializerTypeInfo.AddCollectionAsHandled(typeToAdd); // If this is a special collection type (generic), then we need to make sure that if the inner type is // an interface, we do not add it again if already added. // See this issue http://catel.codeplex.com/workitem/7167 if (serializerTypeInfo.IsSpecialCollectionType(typeToAdd)) { // Always ignore as a test return false; // TODO: Also check interfaces // // It might be a base type, loop all // var baseType = typeToAdd; // while (baseType != null) // { // if (baseType.IsGenericTypeEx()) // { // if (baseType.GetGenericArgumentsEx()[0].IsInterfaceEx()) // { // var genericTypeDefinition = baseType.GetGenericTypeDefinitionEx(); // var allPossibleMatchingTypes = (from type in serializerTypeInfo.SpecialGenericCollectionTypes // where type.GetGenericTypeDefinitionEx() == genericTypeDefinition // select type).ToList(); // if (allPossibleMatchingTypes.Count > 0) // { // Log.Debug("Skipping type '{0}' because there already exists such a type which does the same", typeToAdd.GetSafeFullName()); // return false; // } // } // } // baseType = baseType.GetBaseTypeEx(); // } } return serializerTypeInfo.AddKnownType(typeToAdd); }
/// <summary> /// Adds the type to the known types if the type is serializable. /// </summary> /// <param name="typeToAdd">The type to add.</param> /// <param name="serializerTypeInfo">The serializer type info.</param> /// <returns><c>true</c> if the type is serializable; otherwise <c>false</c>.</returns> protected virtual bool AddTypeToKnownTypesIfSerializable(Type typeToAdd, XmlSerializerTypeInfo serializerTypeInfo) { // Collection first, this collection of types is smaller so if we have a hit, we exit sooner if (serializerTypeInfo.IsCollectionAlreadyHandled(typeToAdd)) { return true; } if (serializerTypeInfo.ContainsKnownType(typeToAdd)) { return true; } serializerTypeInfo.AddCollectionAsHandled(typeToAdd); // If this is a special collection type (generic), then we need to make sure that if the inner type is // an interface, we do not add it again if already added. // See this issue http://catel.codeplex.com/workitem/7167 if (serializerTypeInfo.IsSpecialCollectionType(typeToAdd)) { // Always ignore return false; } return serializerTypeInfo.AddKnownType(typeToAdd); }
/// <summary> /// Gets the known types inside the specific type. /// </summary> /// <param name="type">The type.</param> /// <param name="serializerTypeInfo">The serializer type info.</param> /// <returns>Array of <see cref="Type"/> that are found in the object type.</returns> protected virtual void GetKnownTypes(Type type, XmlSerializerTypeInfo serializerTypeInfo) { if (ShouldTypeBeIgnored(type, serializerTypeInfo)) { return; } Log.Debug("Getting known types for '{0}'", type.GetSafeFullName()); GetKnownTypesForItems(type, serializerTypeInfo); // If this is an interface or abstract, we need to retieve all items that might possible implement or derive bool isInterface = type.IsInterfaceEx(); bool isAbstract = type.IsAbstractEx(); if (isInterface || isAbstract) { if (!serializerTypeInfo.IsTypeAlreadyHandled(type)) { // Interfaces / abstract classes are not a type, and in fact a LOT of types can be added (in fact every object implementing // the interface). For serialization, this is not a problem (we know the exact type), but for deserialization this IS an // issue because we should expect EVERY type that implements the type in the whole AppDomain. // This is huge performance hit, but it's the cost for dynamic easy on-the-fly serialization in WPF and Silverlight. Luckily // we already implemented caching. // Don't check this type again in children checks serializerTypeInfo.AddTypeAsHandled(type); Log.Debug("Type is an interface / abstract class, checking all types implementing / deriving"); if (isInterface) { var typesImplementingInterface = TypeCache.GetTypesImplementingInterface(type); //var typesImplementingInterface = TypeCache.GetTypes(t => t.ImplementsInterfaceEx(type)); foreach (var typeImplementingInterface in typesImplementingInterface) { if (typeImplementingInterface != type) { GetKnownTypes(typeImplementingInterface, serializerTypeInfo); } } } if (isAbstract) { var typesDerivingFromClass = TypeCache.GetTypes(type.IsAssignableFromEx); foreach (var typeDerivingFromClass in typesDerivingFromClass) { if (typeDerivingFromClass != type) { GetKnownTypes(typeDerivingFromClass, serializerTypeInfo); } } } Log.Debug("Finished checking all types implementing / deriving"); } // The interface itself is ignored return; } if (serializerTypeInfo.IsSpecialCollectionType(type) && !type.IsInterfaceEx()) { Log.Debug("Type is a special collection type, adding it to the array of known types"); AddTypeToKnownTypesIfSerializable(type, serializerTypeInfo); } // Fix generics if (type.GetSafeFullName().StartsWith("System.")) { var genericArguments = type.GetGenericArgumentsEx(); foreach (var genericArgument in genericArguments) { Log.Debug("Retrieving known types for generic argument '{0}' of '{1}'", genericArgument.GetSafeFullName(), type.GetSafeFullName()); GetKnownTypes(genericArgument, serializerTypeInfo); } return; } if (!AddTypeToKnownTypesIfSerializable(type, serializerTypeInfo)) { serializerTypeInfo.AddTypeAsHandled(type); } AddTypeMembers(type, serializerTypeInfo); // If this isn't the base type, check that as well var baseType = type.GetBaseTypeEx(); if (baseType != null) { Log.Debug("Checking base type of '{0}' for known types", type.GetSafeFullName()); if (baseType.FullName != null) { GetKnownTypes(baseType, serializerTypeInfo); } else { serializerTypeInfo.AddTypeAsHandled(baseType); } } // Last but not least, check if the type is decorated with KnownTypeAttributes var knowTypesByAttributes = GetKnownTypesViaAttributes(type); if (knowTypesByAttributes.Length > 0) { Log.Debug("Found {0} additional known types for type '{1}'", knowTypesByAttributes.Length, type.GetSafeFullName()); foreach (var knownTypeByAttribute in knowTypesByAttributes) { var attributeType = knownTypeByAttribute; var attributeTypeFullName = attributeType.GetSafeFullName(); if (attributeTypeFullName != null) { GetKnownTypes(knownTypeByAttribute, serializerTypeInfo); } else { serializerTypeInfo.AddTypeAsHandled(attributeType); } } } }