Custom object serialization in .NET.

I have a requirement for serializing a list of objects in a flat file. The calls will look something like this:

class MyObject { public int x; public int y; public string a; public string b; } 

When I serialize this object, the record should be written to an ascii encoded file. Now the length of the x field should be 10 characters (right justified), the y field should be 20 characters (right justified), the value should be 40 (left justified), and the field b should be 100 characters (left justified) edge). How can I achieve such a goal.

The serialized object should look like this:

  25 8 akjsrj jug 

I thought I could apply custom attribute attributes to fields and at runtime decide how to serialize the field.

+7
source share
5 answers

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); } } 
+12
source

Yes, you could achieve this by adding custom attributes and creating your own serializer.

This article provides examples of creating a custom binary serializer.

http://www.codeproject.com/KB/dotnet/CustomSerializationPart2.aspx

+3
source

Sorry, I misunderstood your question. I though you are looking for attributes that will handle serialization.

Of course, you can create your own attributes and handle your own serialization through reflection. And if I did, that would be the preferred solution. I would prefer this because with the attributes:

  • You can specify the order of the elements.
  • You can specify the length of the field.

As for the specific implementation, it would be advisable to simply reflect and form lines.

Old answer: This is a rather specific scenario. Therefore, I do not believe that there are any .NET features that will allow .NET to handle it.

But hard-coded serialization and deserialization like this should be no more than 20 lines of code.

+1
source

Is this format fixed? If you have a suggestion on how to format the output, I would strongly suggest using protobuf-net . This is an incredibly fast library that will use the binary serialization method for your objects with minimal overhead and (I repeat) incredible performance. The protocol was invented by Google specifically for these benefits.

If you cannot change the format, you can create custom attributes and read them at runtime. But keep in mind that reflection can be a bit slow, depending on your serialization needs. If you have only one type of object, it might be better to provide a special serialization service that writes properties directly to a file.

Link: http://code.google.com/p/protobuf-net/

-one
source

There are no special serializers for flat files. Use string formatting and manipulation functions, for example. String.Format ("{0,10}{1,-20}{2,-40}{3,-100}", x, y, a, b) should contain a string formatted according to your format.

-one
source

All Articles