在.NET中自定义对象的序列化

时间:2011-06-09 13:53:58

标签: .net serialization

我需要将对象列表序列化为平面文件。电话会是这样的:

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

当我序列化这个对象时,应该在ascii编码的平面文件中写入一条记录。现在,字段x的长度应为10个字符(右对齐),字段y应为20个字符(右对齐),fiels a应为40(左对齐),字段b应为100个字符(左对齐)。 我怎样才能做到这一点。

序列化对象应如下所示:

        25                   8                                     akjsrj                                                                                          jug

我在想,我可以将自定义属性属性应用于字段,并可以在运行时决定如何序列化字段..

5 个答案:

答案 0 :(得分:13)

这是一个使用普通旧反射和自定义属性的解决方案。它只会为每个文件序列化/反序列化一个项目,但您可以轻松地为每个文件添加对多个项目的支持。

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

}

还有一个小演示:

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

}

答案 1 :(得分:3)

是的,您可以通过添加自定义属性并创建自己的序列化程序来实现此目的。

本文提供了创建自定义二进制序列化程序的示例。

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

答案 2 :(得分:1)

抱歉,我误解了你的问题。我虽然你正在寻找属性,这将自己处理序列化。

当然,您可以创建自己的属性并通过反射处理自己的序列化。如果我这样做,它将是优先解决方案。我会鼓励它,因为有属性:

  • 您可以指定项目的顺序。
  • 您可以指定字段长度。

至于具体实施,简单的反射和字符串格式化是合适的。

旧答案: 这是非常具体的情况。所以我不相信有任何.NET功能会让.NET处理它。

但像这样的硬编码序列化和反序列化不应超过20行代码..

答案 3 :(得分:-1)

这种格式是否已修复?如果您对如何格式化输出有说法,我强烈建议使用 protobuf-net 。这是一个非常快速的库,它将为您的对象使用二进制序列化方法,而开销最小,并且(我重复一遍)令人难以置信的性能。该协议是谷歌特别为这些优势发明的。

如果无法更改格式,则可以创建自定义属性并在运行时将其读出。但请记住,根据您的序列化需求,反射可能会有点慢。如果你只有一种类型的对象,也许最好提供一个特殊的序列化服务,它将属性直接写入文件。

链接: http://code.google.com/p/protobuf-net/

答案 4 :(得分:-1)

平面文件没有特殊的序列化程序。使用string formatting和操纵功能,例如String.Format ("{0,10}{1,-20}{2,-40}{3,-100}", x, y, a, b)应生成根据您的格式格式化的行。