Here is a solution that uses the usual old reflection and a custom attribute. It will serialize / deserialize one item for each file, but you can easily add support for multiple items for each file.
// Attribute making it possible public class FlatFileAttribute : Attribute { public int Position { get; set; } public int Length { get; set; } public Padding Padding { get; set; } /// <summary> /// Initializes a new instance of the <see cref="FlatFileAttribute"/> class. /// </summary> /// <param name="position">Each item needs to be ordered so that /// serialization/deserilization works even if the properties /// are reordered in the class.</param> /// <param name="length">Total width in the text file</param> /// <param name="padding">How to do the padding</param> public FlatFileAttribute(int position, int length, Padding padding) { Position = position; Length = length; Padding = padding; } } public enum Padding { Left, Right } /// <summary> /// Serializer making the actual work /// </summary> public class Serializer { private static IEnumerable<PropertyInfo> GetProperties(Type type) { var attributeType = typeof(FlatFileAttribute); return type .GetProperties() .Where(prop => prop.GetCustomAttributes(attributeType, false).Any()) .OrderBy( prop => ((FlatFileAttribute)prop.GetCustomAttributes(attributeType, false).First()). Position); } public static void Serialize(object obj, Stream target) { var properties = GetProperties(obj.GetType()); using (var writer = new StreamWriter(target)) { var attributeType = typeof(FlatFileAttribute); foreach (var propertyInfo in properties) { var value = propertyInfo.GetValue(obj, null).ToString(); var attr = (FlatFileAttribute)propertyInfo.GetCustomAttributes(attributeType, false).First(); value = attr.Padding == Padding.Left ? value.PadLeft(attr.Length) : value.PadRight(attr.Length); writer.Write(value); } writer.WriteLine(); } } public static T Deserialize<T>(Stream source) where T : class, new() { var properties = GetProperties(typeof(T)); var obj = new T(); using (var reader = new StreamReader(source)) { var attributeType = typeof(FlatFileAttribute); foreach (var propertyInfo in properties) { var attr = (FlatFileAttribute)propertyInfo.GetCustomAttributes(attributeType, false).First(); var buffer = new char[attr.Length]; reader.Read(buffer, 0, buffer.Length); var value = new string(buffer).Trim(); if (propertyInfo.PropertyType != typeof(string)) propertyInfo.SetValue(obj, Convert.ChangeType(value, propertyInfo.PropertyType), null); else propertyInfo.SetValue(obj, value.Trim(), null); } } return obj; } }
And a small demonstration:
// Sample class using the attributes public class MyObject { // First field in the file, total width of 5 chars, pad left [FlatFile(1, 5, Padding.Left)] public int Age { get; set; } // Second field in the file, total width of 40 chars, pad right [FlatFile(2, 40, Padding.Right)] public string Name { get; set; } } private static void Main(string[] args) { // Serialize an object using (var stream = File.OpenWrite("C:\\temp.dat")) { var obj = new MyObject { Age = 10, Name = "Sven" }; Serializer.Serialize(obj, stream); } // Deserialzie it from the file MyObject readFromFile = null; using (var stream = File.OpenRead("C:\\temp.dat")) { readFromFile = Serializer.Deserialize<MyObject>(stream); } }
jgauffin
source share