/// <summary> /// Constructs a serialization helper object separate from I/O /// </summary> /// <param name="settings"></param> /// <param name="riskyChars"></param> /// <param name="forceQualifierTypes"></param> public SerializationHelper(CSVSettings settings, char[] riskyChars, Dictionary <Type, int> forceQualifierTypes) { _settings = settings; if (_settings == null) { _settings = CSVSettings.CSV; } // Extract properties and fields that are not excluded var excluded = new ExcludedColumnHelper(_settings); var props = new List <PropertyInfo>(); foreach (var prop in typeof(T).GetProperties()) { if (!excluded.IsExcluded(prop.Name)) { props.Add(prop); } } var fields = new List <FieldInfo>(); foreach (var field in typeof(T).GetFields()) { if (!excluded.IsExcluded(field.Name)) { fields.Add(field); } } _properties = props.ToArray(); _fields = fields.ToArray(); _riskyChars = riskyChars; _forceQualifierTypes = forceQualifierTypes; }
/// <summary> /// Construct a new deserialization helper for a specific class containing all the information necessary /// for optimized deserialization /// </summary> /// <param name="settings"></param> /// <param name="headers"></param> public DeserializationHelper(CSVSettings settings, string[] headers) { var settings1 = settings; if (settings1 == null) { settings1 = CSVSettings.TSV; } if (headers == null) { throw new Exception("CSV must have headers to be deserialized"); } var return_type = typeof(T); _numColumns = headers.Length; // Set binding flags correctly var bindings = BindingFlags.Public | BindingFlags.Instance; if (!settings1.HeadersCaseSensitive) { bindings |= BindingFlags.IgnoreCase; } // Set up the list of excluded columns var excluded = new ExcludedColumnHelper(settings1); // Determine how to handle each column in the file - check properties, fields, and methods _columnTypes = new Type[_numColumns]; _converters = new TypeConverter[_numColumns]; _properties = new PropertyInfo[_numColumns]; _fields = new FieldInfo[_numColumns]; _methods = new MethodInfo[_numColumns]; for (var i = 0; i < _numColumns; i++) { // Is this column excluded? if (excluded.IsExcluded(headers[i])) { continue; } // Check if this is a property _properties[i] = return_type.GetProperty(headers[i], bindings); if (_properties[i] != null && !_properties[i].CanWrite) { if (settings1.IgnoreReadOnlyProperties && settings1.IgnoreHeaderErrors) { _properties[i] = null; continue; } throw new Exception($"The column header '{headers[i]}' matches a read-only property. To ignore this exception, enable IgnoreReadOnlyProperties and IgnoreHeaderErrors."); } // If we failed to get a property handler, let's try a field handler if (_properties[i] == null) { _fields[i] = return_type.GetField(headers[i], bindings); // If we failed to get a field handler, let's try a method if (_fields[i] == null) { // Methods must be treated differently - we have to ensure that the method has a single parameter var mi = return_type.GetMethod(headers[i], bindings); if (mi != null) { if (mi.GetParameters().Length == 1) { _methods[i] = mi; _columnTypes[i] = mi.GetParameters()[0].ParameterType; } else if (!settings1.IgnoreHeaderErrors) { throw new Exception( $"The column header '{headers[i]}' matched a method with more than one parameter."); } } else if (!settings1.IgnoreHeaderErrors) { throw new Exception( $"The column header '{headers[i]}' was not found in the class '{return_type.FullName}'."); } } else { _columnTypes[i] = _fields[i].FieldType; } } else { _columnTypes[i] = _properties[i].PropertyType; } if (_columnTypes[i] != null) { _converters[i] = TypeDescriptor.GetConverter(_columnTypes[i]); if (_converters[i] == null && !settings1.IgnoreHeaderErrors) { throw new Exception( $"The column {headers[i]} (type {_columnTypes[i]}) does not have a type converter."); } } } }