开发者

Custom serialization of an object in .NET

I have a requirement to serialize a list of objects to a flat file. The calls will be something like:

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

When I serialize this object, a record should be written in a ascii encoded flat file. Now, the length of field x should be 10 characters (right aligned), field y should be 20 characters (right aligned), fiels a should be 40 (left aligned) and field b should be 100 characters (left aligned). How can I achieve such a thing.

A serialized object should look like :

        25                   8                                     akjsrj                                                                                   开发者_运维百科       jug

I was thinking that may be I can apply custom attributes attributes to the fields and can decide at runtime how to serialize the field..


Here is a solution that uses plain old reflection and a custom attribute. It will only serialize/deserialize one item per file, but you could easily add support for multiple items per 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 demo:

// 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);
    }

}


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

This article gives examples of creating a custom binary serializer.

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


Sorry, I misread your question. I though you were looking for attributes, that would handle the serialization themselves.

Of course, you can create your own attributes and handle your own serialization throught reflection. And if I was doing it, it would be preffered solution. I would preffer it because with attributes:

  • You can specify order of items.
  • You can specify length of field.

As for concrete implementation, simple reflection and string formating would be appropriate.

Old answer: This is quite specific scenario. So I don't believe there are any .NET features, that would make .NET handle it.

But hard-coded serialization and deserialization like this shouldn't be longer than 20 lines of code..


Is this format fixed? If you have a saying in how to format the output, I would strongly suggest using protobuf-net. It's an incredibly fast library which will use a binary serialization method for your objects with minimal overhead and (I repeat myself) incredible performance. The protocol was invented by google especially for these advantages.

If you cannot change the format, you can create custom attributes and read them out at runtime. But take in mind, that reflection can be a bit slow, depending on your serialization needs. If you only have one type of objects, maybe it's better to provider a special serialization service which writes the properties directly to file.

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


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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜