Установить только при десериализации - PullRequest
4 голосов
/ 12 июля 2009

Проблема:

У меня есть класс, скажем, Foo, который реализует свойство Id. Фу должно быть сериализации. Foo.Id должен быть инициализирован новым GUID при инициализации Фу. Идентификатор Foo.Id не должен изменяться после его установки. При десериализации будет пытаться установить Foo.Id, поэтому он должен быть открыт.

Private _Id As String=system.Guid.NewGuid.tostring
Public Property Id As String
    Get
        return _Id
    End Get
    Set(ByVal value As String)
        _Id = value
    End Set
End Property

или для клиентов # 1008 *

private string _Id = system.Guid.NewGuid().ToString();
public string Id {
    get { return _Id; }
    set { _Id = value; }
}

Решение Мысли:

Единственное решение, по-видимому, заключается в создании исключения времени выполнения при установке Foo.Id, но это вызовет проблемы при десериализации. Итак, как-то мы должны сделать убедитесь, что исключение выдается только тогда, когда попытка Set Foo.Id сделано вне сериализатора. Какой-то флаг или что-то в конструкторе?

Редактировать, метод десериализации ...

public static Foo DeserializeFromFile(string sFilespec)
{
    Xml.Serialization.XmlSerializer oSerializer = new Xml.Serialization.XmlSerializer(typeof(Foo));
    System.IO.FileStream oStream = new System.IO.FileStream(sFilespec, IO.FileMode.Open);
    Foo oObject = oSerializer.Deserialize(oStream);
    oStream.Close();
    return oObject;
}

Ответы [ 5 ]

9 голосов
/ 12 июля 2009

Я не уверен, что понимаю вашу проблему, но вы можете попробовать реализовать интерфейс ISerializable в своем классе, чтобы вручную настроить процессы сериализации / десериализации.

[Serializable]
public class YourClass : ISerializable
{    
    private Guid _Id = Guid.NewGuid();

    public string Id
    {
            get { return _Id; }
            private set { _Id = value; }
    }

    public YourClass() // Normal constructor
    {
       // ...
    }

    // This constructor is for deserialization only
    private YourClass(SerializationInfo information, StreamingContext context)
    {
        Id = (Guid)information.GetValue("Id", typeof(Guid)); // etc
    }

    void ISerializable.GetObjectData(SerializationInfo information,
        StreamingContext context)
    {
        // You serialize stuff like this
        information.AddValue("Id", Id, typeof(Guid)); 
    }
}

Также ознакомьтесь с классом SerializationInfo для получения дополнительной информации о сериализации и десериализации наиболее распространенных типов.

3 голосов
/ 12 июля 2009

Из описания я предполагаю, что вы используете XmlSerializer. Дело в том, что XmlSerializer не хватает детализации для этого. Он не поддерживает обратные вызовы (которые позволят вам обнаружить сериализацию) и т. Д.

Параметры:

  • реализовать IXmlSerializable - много работы (с XmlReader / XmlWriter) и легко ошибиться ... не делайте этого, если вам не нужно
  • использует другой сериализатор (например, DataContractSerializer, который также поддерживает частные методы получения / установки или поля, но не поддерживает полный элемент управления xml) - например, см. Здесь
  • использовать отдельный DTO; то есть используйте ваш класс с приватным сеттером в приложении и отдельный (более простой) класс (public get / set) для сериализации, возможно, с неявным оператором преобразования между ними
  • сделайте get / set общедоступным и не беспокойтесь об этом

Думаю, я бы выбрал вариант DTO; он сохраняет простой, но полный контроль над форматированием XML и не требует больших усилий.

using System;
using System.Xml.Serialization;
[XmlType("foo"), XmlRoot("foo")]
public class FooDto {
    [XmlAttribute("bar")]
    public string Bar { get; set; }

    public static implicit operator Foo(FooDto value) {
        return value == null ? null :
            new Foo(value.Bar);
    }
    public static implicit operator FooDto(Foo value) {
        return value == null ? null :
            new FooDto { Bar = value.Bar };
    }
}
public class Foo {
    private readonly string bar;
    public Foo(string bar) { this.bar = bar; }
    public string Bar { get { return bar; } }
}
static class Program {
    static void Main() {
        Foo foo = new Foo("abcdefg");
        FooDto dto = foo;
        new XmlSerializer(dto.GetType()).Serialize(
            Console.Out, dto);
    }
}
0 голосов
/ 20 апреля 2018

Если его нужно установить только один раз, вы можете добавить bool ...

private string _Id = system.Guid.NewGuid().ToString();
private bool _IdSet = false;
public string Id {
    get { return _Id; }
    set 
    { 
        if(!_IdSet)
        {
            _Id = value;
            _IdSet = true;
        }
        // else throw exception?
    }
}
0 голосов
/ 12 июля 2009

Способ, который описал Дриёкепу, кажется правильным. Точно так же, если вам необходимо использовать XmlSerialization для какой-либо причины / ограничения / требования, вы должны реализовать интерфейс IXmlSerializable. Однако реализация IXmlSerializable может быть не такой интуитивной, как реализация ISerializable.

В этом случае вы можете попробовать следующий прием в качестве обходного пути:).

  1. Сделать свойство Foo.Id доступным только для чтения
  2. Создать контейнерный объект, Boo. Boo включает в себя только один и тот же тип переменных-членов для сериализации. В этом примере Boo будет иметь свойство Id с общедоступными сеттерами и геттерами. Boo не должен включать методы-члены Foo.
  3. Используйте Boo для сериализации и десериализации xml и используйте Foo для обработки бизнес-логики. логика.

См. Пример кода ниже:

using System;
using System.IO;
using System.Xml.Serialization;

namespace SerializationSample
{
    class Program
    {
        private const string STR_CtempSerilizationxml = @"c:\temp\Serilization.xml";
        static void Main(string[] args)
        {
            Foo foo = new Foo(Guid.NewGuid().ToString());
            Console.WriteLine("Foo.Id = " + foo.Id);

            SerializeToFile(foo, STR_CtempSerilizationxml);

            Foo fooDeserialized = DeserializeFromFile(STR_CtempSerilizationxml);

            Console.WriteLine("Foo.Id = " + fooDeserialized.Id);

        }

        private static void SerializeToFile(Foo foo, string sFilespec)
        {
            XmlSerializer iSerializer = new XmlSerializer(typeof(Boo));
            using (FileStream stream = new FileStream(sFilespec, FileMode.Create))
            {
                iSerializer.Serialize(stream, new Boo(foo));
            }
        }

        public static Foo DeserializeFromFile(string sFilespec)
        {
            XmlSerializer oSerializer = new XmlSerializer(typeof(Boo));
            using (System.IO.FileStream oStream = new FileStream(sFilespec, FileMode.Open))
            {
                return new Foo(((Boo)oSerializer.Deserialize(oStream)).Id);
            }
        }
    }

    public class Foo
    {
        private readonly string _Id;
        public string Id 
        { 
            get { return _Id; }
        }

        public Foo(string id) { _Id = id; }      
    }

    public class Boo
    {
        public string Id { get; set; }

        public Boo(Foo foo) { Id = foo.Id; }

        public Boo() { }
    }
}
0 голосов
/ 12 июля 2009

Прежде всего, я не думаю, что ваш код компилируется. Вы должны привести к (Foo). Вам также не хватает блока "using":

public static Foo DeserializeFromFile(string sFilespec)
{
        Xml.Serialization.XmlSerializer oSerializer = new Xml.Serialization.XmlSerializer(typeof(Foo));
        using (System.IO.FileStream oStream = new System.IO.FileStream(sFilespec, IO.FileMode.Open)) {
            return (Foo) oSerializer.Deserialize(oStream);
        }
}

Что еще более важно, вам не повезет, если вы не внедрите интерфейс IXmlSerializable и не проведете собственную десериализацию. При десериализации свойства Id вы можете установить поле _Id напрямую, без использования свойства.

...