Пользовательская сериализация объекта в.NET

У меня есть требование сериализации списка объектов в плоский файл. Звонки будут примерно такими:

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

Когда я сериализую этот объект, запись должна быть записана в плоский файл в кодировке ASCII. Теперь длина поля x должна составлять 10 символов (выровнено по правому краю), поле y должно быть 20 символов (выровнено по правому краю), поля a должны быть 40 (выровнены по левому краю), а поле b должно быть 100 символов (выровнено по левому краю). Как я могу достичь такой вещи.

Сериализованный объект должен выглядеть так:

        25                   8                                     akjsrj                                                                                          jug

Я думал, что может быть, я могу применить атрибуты пользовательских атрибутов к полям и может решить во время выполнения, как сериализовать поле..

5 ответов

Вот решение, которое использует простое старое отражение и пользовательский атрибут. Он будет сериализовать / десериализовать только один элемент на файл, но вы можете легко добавить поддержку нескольких элементов на файл.

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

}

Да, этого можно добиться, добавив пользовательские атрибуты и создав собственный сериализатор.

В этой статье приводятся примеры создания собственного двоичного сериализатора.

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

Извините, я неправильно понял ваш вопрос. Я думал, что вы искали атрибуты, которые сами справились бы с сериализацией.

Конечно, вы можете создавать свои собственные атрибуты и обрабатывать свои собственные сериализации посредством отражения. И если бы я делал это, было бы предпочтительным решением. Я бы предпочел это, потому что с атрибутами:

  • Вы можете указать порядок пунктов.
  • Вы можете указать длину поля.

Что касается конкретной реализации, простое отражение и форматирование строки были бы уместны.

Старый ответ: это довольно специфический сценарий. Поэтому я не верю, что есть какие-либо функции.NET, которые бы помогли.NET справиться с этим.

Но жестко запрограммированные сериализация и десериализация, подобные этой, не должны быть длиннее 20 строк кода.

Специальных сериализаторов для плоских файлов не существует. Используйте функции форматирования строк и манипуляции, например String.Format ("{0,10}{1,-20}{2,-40}{3,-100}", x, y, a, b) должен создать строку, отформатированную в соответствии с вашим форматом.

Исправлен ли этот формат? Если у вас есть поговорка о том, как форматировать вывод, я настоятельно рекомендую использовать protobuf-net. Это невероятно быстрая библиотека, которая будет использовать метод двоичной сериализации для ваших объектов с минимальными издержками и (повторюсь) невероятной производительностью. Протокол был изобретен Google специально для этих преимуществ.

Если вы не можете изменить формат, вы можете создавать собственные атрибуты и считывать их во время выполнения. Но имейте в виду, что это отражение может быть немного медленным, в зависимости от ваших потребностей в сериализации. Если у вас есть только один тип объектов, возможно, лучше предоставить специальный сервис сериализации, который записывает свойства непосредственно в файл.

Ссылка: http://code.google.com/p/protobuf-net/

Другие вопросы по тегам