Custom serialization of an object in .NET

前端 未结 5 2073
盖世英雄少女心
盖世英雄少女心 2020-12-09 06:00

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;
           


        
相关标签:
5条回答
  • 2020-12-09 06:40

    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);
        }
    
    }
    
    0 讨论(0)
  • 2020-12-09 06:46

    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 讨论(0)
  • 2020-12-09 06:53

    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

    0 讨论(0)
  • 2020-12-09 06:59

    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..

    0 讨论(0)
  • 2020-12-09 07:04

    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/

    0 讨论(0)
提交回复
热议问题