/// <summary> /// Maps a member to a CSV field. /// </summary> /// <param name="classType">The type of the class this map is for. This may not be the same type /// as the member.DeclaringType or the current ClassType due to nested member mappings.</param> /// <param name="member">The member to map.</param> /// <param name="useExistingMap">If true, an existing map will be used if available. /// If false, a new map is created for the same member.</param> /// <returns>The member mapping.</returns> public MemberMap Map(Type classType, MemberInfo member, bool useExistingMap = true) { if (useExistingMap) { var existingMap = MemberMaps.Find(member); if (existingMap != null) { return(existingMap); } } var memberMap = MemberMap.CreateGeneric(classType, member); memberMap.Data.Index = GetMaxIndex() + 1; MemberMaps.Add(memberMap); return(memberMap); }
/// <summary> /// Auto maps the given map and checks for circular references as it goes. /// </summary> /// <param name="map">The map to auto map.</param> /// <param name="configuration">The configuration.</param> /// <param name="mapParents">The list of parents for the map.</param> /// <param name="indexStart">The index starting point.</param> protected virtual void AutoMapMembers(ClassMap map, CsvConfiguration configuration, LinkedList <Type> mapParents, int indexStart = 0) { var type = map.GetGenericType(); var flags = BindingFlags.Instance | BindingFlags.Public; if (configuration.IncludePrivateMembers) { flags = flags | BindingFlags.NonPublic; } var members = new List <MemberInfo>(); if ((configuration.MemberTypes & MemberTypes.Properties) == MemberTypes.Properties) { // We need to go up the declaration tree and find the actual type the property // exists on and use that PropertyInfo instead. This is so we can get the private // set method for the property. var properties = new List <PropertyInfo>(); foreach (var property in ReflectionHelper.GetUniqueProperties(type, flags)) { if (properties.Any(p => p.Name == property.Name)) { // Multiple properties could have the same name if a child class property // is hiding a parent class property by using `new`. It's possible that // the order of the properties returned continue; } properties.Add(ReflectionHelper.GetDeclaringProperty(type, property, flags)); } members.AddRange(properties); } if ((configuration.MemberTypes & MemberTypes.Fields) == MemberTypes.Fields) { var fields = new List <MemberInfo>(); foreach (var field in type.GetFields(flags)) { if (!field.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Any()) { fields.Add(field); } } members.AddRange(fields); } foreach (var member in members) { var typeConverterType = configuration.TypeConverterCache.GetConverter(member).GetType(); if (configuration.HasHeaderRecord && enumerableConverters.Contains(typeConverterType)) { // Enumerable converters can't write the header properly, so skip it. continue; } var memberTypeInfo = member.MemberType().GetTypeInfo(); var isDefaultConverter = typeConverterType == typeof(DefaultTypeConverter); if (isDefaultConverter) { // If the type is not one covered by our type converters // and it has a parameterless constructor, create a // reference map for it. if (configuration.IgnoreReferences) { continue; } if (CheckForCircularReference(member.MemberType(), mapParents)) { continue; } mapParents.AddLast(type); var refMapType = typeof(DefaultClassMap <>).MakeGenericType(member.MemberType()); var refMap = (ClassMap)ReflectionHelper.CreateInstance(refMapType); if (memberTypeInfo.HasConstructor() && !memberTypeInfo.HasParameterlessConstructor() && !memberTypeInfo.IsUserDefinedStruct()) { AutoMapConstructorParameters(refMap, configuration, mapParents, Math.Max(map.GetMaxIndex() + 1, indexStart)); } // Need to use Max here for nested types. AutoMapMembers(refMap, configuration, mapParents, Math.Max(map.GetMaxIndex() + 1, indexStart)); mapParents.Drop(mapParents.Find(type)); if (refMap.MemberMaps.Count > 0 || refMap.ReferenceMaps.Count > 0) { var referenceMap = new MemberReferenceMap(member, refMap); if (configuration.ReferenceHeaderPrefix != null) { referenceMap.Data.Prefix = configuration.ReferenceHeaderPrefix(member.MemberType(), member.Name); } ApplyAttributes(referenceMap); map.ReferenceMaps.Add(referenceMap); } } else { // Only add the member map if it can be converted later on. // If the member will use the default converter, don't add it because // we don't want the .ToString() value to be used when auto mapping. // Use the top of the map tree. This will maps that have been auto mapped // to later on get a reference to a map by doing map.Map( m => m.A.B.C.Id ) // and it will return the correct parent map type of A instead of C. var classType = mapParents.First?.Value ?? map.ClassType; var memberMap = MemberMap.CreateGeneric(classType, member); // Use global values as the starting point. memberMap.Data.TypeConverterOptions = TypeConverterOptions.Merge(new TypeConverterOptions(), configuration.TypeConverterOptionsCache.GetOptions(member.MemberType()), memberMap.Data.TypeConverterOptions); memberMap.Data.Index = map.GetMaxIndex() + 1; ApplyAttributes(memberMap); map.MemberMaps.Add(memberMap); } } map.ReIndex(indexStart); }
/// <summary> /// Auto maps the given map and checks for circular references as it goes. /// </summary> /// <param name="map">The map to auto map.</param> /// <param name="configuration">The configuration.</param> /// <param name="mapParents">The list of parents for the map.</param> /// <param name="indexStart">The index starting point.</param> protected virtual void AutoMapMembers(ClassMap map, Configuration configuration, LinkedList <Type> mapParents, int indexStart = 0) { var type = map.GetGenericType(); var flags = BindingFlags.Instance | BindingFlags.Public; if (configuration.IncludePrivateMembers) { flags = flags | BindingFlags.NonPublic; } var members = new List <MemberInfo>(); if ((configuration.MemberTypes & MemberTypes.Properties) == MemberTypes.Properties) { // We need to go up the declaration tree and find the actual type the property // exists on and use that PropertyInfo instead. This is so we can get the private // set method for the property. var properties = new List <PropertyInfo>(); foreach (var property in type.GetProperties(flags)) { properties.Add(ReflectionHelper.GetDeclaringProperty(type, property, flags)); } members.AddRange(properties); } if ((configuration.MemberTypes & MemberTypes.Fields) == MemberTypes.Fields) { var fields = new List <MemberInfo>(); foreach (var field in type.GetFields(flags)) { if (!field.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Any()) { fields.Add(field); } } members.AddRange(fields); } foreach (var member in members) { var typeConverterType = configuration.TypeConverterFactory.GetConverter(member.MemberType()).GetType(); if (configuration.HasHeaderRecord && enumerableConverters.Contains(typeConverterType)) { // Enumerable converters can't write the header properly, so skip it. continue; } var memberTypeInfo = member.MemberType().GetTypeInfo(); var isDefaultConverter = typeConverterType == typeof(DefaultTypeConverter); if (isDefaultConverter && (memberTypeInfo.HasParameterlessConstructor() || memberTypeInfo.IsUserDefinedStruct())) { // If the type is not one covered by our type converters // and it has a parameterless constructor, create a // reference map for it. if (configuration.IgnoreReferences) { continue; } if (CheckForCircularReference(member.MemberType(), mapParents)) { continue; } mapParents.AddLast(type); var refMapType = typeof(DefaultClassMap <>).MakeGenericType(member.MemberType()); var refMap = (ClassMap)ReflectionHelper.CreateInstance(refMapType); // Need to use Max here for nested types. AutoMapMembers(refMap, configuration, mapParents, Math.Max(map.GetMaxIndex() + 1, indexStart)); mapParents.Drop(mapParents.Find(type)); if (refMap.MemberMaps.Count > 0 || refMap.ReferenceMaps.Count > 0) { var referenceMap = new MemberReferenceMap(member, refMap); if (configuration.PrefixReferenceHeaders) { referenceMap.Prefix(); } map.ReferenceMaps.Add(referenceMap); } } else { var memberMap = MemberMap.CreateGeneric(map.ClassType, member); // Use global values as the starting point. memberMap.Data.TypeConverterOptions = TypeConverterOptions.Merge(new TypeConverterOptions(), configuration.TypeConverterOptionsFactory.GetOptions(member.MemberType()), memberMap.Data.TypeConverterOptions); memberMap.Data.Index = map.GetMaxIndex() + 1; if (!isDefaultConverter) { // Only add the member map if it can be converted later on. // If the member will use the default converter, don't add it because // we don't want the .ToString() value to be used when auto mapping. map.MemberMaps.Add(memberMap); } } } map.ReIndex(indexStart); }