Есть ли способ пометить поле класса только для десериализации и не сериализации? - PullRequest
2 голосов
/ 05 февраля 2010

Звучит странно, но это именно то, что я хочу, потому что я использую структуру данных с именем "Project", которая сериализуется в файл сохранения. Я хотел бы иметь возможность десериализовать старую версию файла сохранения с устаревшими полями в такте, но затем повторно сериализовать его, используя только используемые в настоящее время поля. Проблема в том, что я хочу избавиться от этих устаревших полей при повторной сериализации структуры, чтобы минимизировать размер файла. Можно ли пометить поле как «десериализуемый только»?

Edit:

Спасибо за идеи! Я решил использовать в основном предложения NickLarsen и создать старую версию структуры проекта со всеми устаревшими полями в отдельном пространстве имен. Разница в том, что я решил выполнить обновление после десериализации. Это здорово, потому что я могу сделать все это в одной строке (надеюсь, вы сможете понять суть того, что я делаю здесь):

Project myProject = new Project((Depreciated.Project)myFormatter.Deserialize(myStream));

Конструктор просто возвращает новый экземпляр свежей минимальной структуры данных на основе старой раздутой.

Второе редактирование:

Я решил последовать совету бибопа и создать новые классы для каждой версии проекта с самой старой версией, включая все устаревшие и новые поля. Затем конструктор каждого проекта обновляется до следующей версии, по пути избавляясь от устаревших полей. Вот иллюстративный пример конвертации из версии 1.0.0 -> 1.0.5 -> current.

Project myProject = new Project(new Project105((Project100)myFormatter.Deserialize(myStream)));

Одним из ключей к этому является принудительное использование десериализованного файла и любых полей в более старых версиях классов с помощью SerializationBinder .

Ответы [ 5 ]

1 голос
/ 05 февраля 2010

У вас есть пара вариантов для этого.

Сначала вы можете создать версию вашего класса (возможно, в другом пространстве имен) для старого формата, а другую - для нового формата. В старом классе перегрузите функцию serialize, чтобы выдать ошибку, или преобразуйте себя в новый класс и сериализуйте это.

Во-вторых, вы могли бы просто написать свой собственный сериализатор, который был бы немного более сложным. Существует множество ресурсов , которые могут вам помочь.

1 голос
/ 05 февраля 2010

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

Наличие нового класса для каждого изменения формата позволит избежать необходимости изменять какой-либо существующий код при изменении структуры данных, и ваше приложение может не знать о том, что файл сохранения был какой-то более старой версией. Это позволит вам загружать из любой предыдущей версии, а не только из последней.

Подобные вещи, реализуемые цепочкой ответственности , могут упростить переход на новый формат с минимальными изменениями в существующем коде.

Хотя цепочка ответственности в учебниках не реализована, вы можете реализовать что-то вроде этого:

(ПРИМЕЧАНИЕ: непроверенный код)

public interface IProductFactory<T> where T : class
{
    T CreateProduct(string filename);
    T DeserializeInstance(string filename);
}

public abstract  class ProductFactoryBase<T> :  IProductFactory<T> where T : class
{
    public abstract T CreateProduct(string filename);

    public T DeserializeInstance(string filename)
    {
        var myFormatter = new BinaryFormatter();
        using (FileStream stream = File.Open(filename, FileMode.Open))
        {
            return myFormatter.Deserialize(stream) as T;
        }

    }
}

public class ProductV1Factory : ProductFactoryBase<ProductV1>
{
    public override ProductV1 CreateProduct(string filename)
    {
        return DeserializeInstance(filename);
    }
}

public class ProductV2Factory : ProductFactoryBase<ProductV2>
{
    ProductV1Factory successor = new ProductV1Factory();
    public override ProductV2 CreateProduct(string filename)
    {
        var product = DeserializeInstance(filename);
        if (product==null)
        {
            product = new ProductV2(successor.CreateProduct(filename));
        }
        return product;
    }

}

public class ProductV2
{
    public ProductV2(ProductV1 product)
    {
        //construct from V1 information
    }
}


public class ProductV1  
{
}

это имеет то преимущество, что когда вы хотите добавить ProductV3, вам нужно всего лишь изменить класс, который вы используете в своем приложении, на тип ProductV3, что вам нужно сделать в любом случае, а затем изменить код загрузки, чтобы он использовал ProductV3Factory, который в основном совпадает с ProductV2Factory, но использует в качестве преемника ProductV2Factory. Вам не нужно менять какие-либо существующие классы. Вы, вероятно, могли бы немного реорганизовать это, чтобы ввести в базовый класс обычную комбинацию CreateProduct, но это дает представление.

0 голосов
/ 05 февраля 2010

Я думаю, вы ищете OptionalFieldAttribute .

0 голосов
/ 05 февраля 2010

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

0 голосов
/ 05 февраля 2010

Насколько я знаю, .NET очень осторожен, чтобы сериализовать только те вещи, которые он может десериализовать, и наоборот.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...