2016-08-09 1 views
0

Вот мой список классов: -Как сериализовать класс со свойствами, объявленными как интерфейсы?

public interface IUniquelyIdentifiable 
    { 
     string AuthorName { get; set; } 
    } 
    public interface IUniquelyIdentifiable1 
    { 
     string CategoryName { get; set; } 

    } 
    public interface IUniquelyIdentifiable2 
    { 
     string PublisherName { get; set; } 
    } 

    [Serializable] 
    public class Book 
    { 
     //BookId, Category, Title, Author, Publisher, Description, Price, ISBN, PublicationDate. 
     public IUniquelyIdentifiable Author { get; set; } 
     public IUniquelyIdentifiable1 Category { get; set; } 
     public IUniquelyIdentifiable2 Publisher { get; set; } 
     public int BookId { get; set; } 
     public string Title { get; set; } 
     public string Description { get; set; } 
     public int ISBN { get; set; } 
     public int Price { get; set; } 
     public string PublicationDate { get; set; } 
    } 

    [Serializable] 
    class Author : IUniquelyIdentifiable 
    { 
     //AuthorId, AuthorName, DateOfBirth, State, City, Phone 
     public int AuthorId { get; set; } 
     public string AuthorName { get; set; } 
     public string DateOfBirth { get; set; } 
     public string State { get; set; } 
     public string City { get; set; } 
     public int Phone { get; set; } 
    } 

    [Serializable] 
    class Category : IUniquelyIdentifiable1 
    { 
     //CategoryId, CategoryName, Description 
     public int CategoryId { get; set; } 
     public string CategoryName { get; set; } 
     public string Description { get; set; } 
    } 

    [Serializable] 
    class Publisher : IUniquelyIdentifiable2 
    { 
     //PublisherId, PublisherName, DateOfBirth, State, City, Phone. 
     public int PublisherId { get; set; } 
     public string PublisherName { get; set; } 
     public string DateOfBirth { get; set; } 
     public string State { get; set; } 
     public string City { get; set; } 
     public int Phone { get; set; } 
    } 

ниже метод, который пытается сериализовать объекты, созданные из вышеперечисленных классов: -

public static void XmlSerializeMyObject() 
     { 

      XmlSerializer writer = new XmlSerializer(typeof(Book)); 
      //overview.title = "Serialization Overview"; 
      var path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "//SerializationOverview.xml"; 
      FileStream file = File.Create(path); 
      writer.Serialize(file,bookList); 
      file.Close(); 
     } 

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

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

+0

Возможный дубликат [Сериализация интерфейсов] (http://stackoverflow.com/questions/4659248/serializing-interfaces) –

+1

Нет, это не так. Этот вопрос не закрыт. У него даже нет правильного ответа. Пожалуйста, проверьте правильность, прежде чем пометить любые вопросы. –

+0

попробуйте с помощью сериализатора datacontract https://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractserializer.aspx – Thorarins

ответ

2

См. Комментарии в конце. Мое первое решение напрямую отвечает на вопрос, но я не рекомендую это делать, если у вас нет выбора. Краткая версия. Я рекомендую решить проблему, используя конкретные типы Author, Category и Publisher вместо интерфейсов в классе Book.


Для того, чтобы сериализовать тип, должен быть определен способ определения конкретных типов элементов. Возможно, что что-то может сериализовать экземпляр Book с использованием реализации IUniquelyIdentifiable, которая неизвестна десериализации программы.

Вы можете изменить свой Book класс как это:

[Serializable][DataContract][KnownType(typeof(Author))] 
[KnownType(typeof(Category))] 
[KnownType(typeof(Publisher))] 
public class Book 
{ 
    [DataMember]public IUniquelyIdentifiable Author { get; set; } 
    [DataMember]public IUniquelyIdentifiable1 Category { get; set; } 
    [DataMember]public IUniquelyIdentifiable2 Publisher { get; set; } 
    [DataMember]public int BookId { get; set; } 
    [DataMember]public string Title { get; set; } 
    [DataMember]public string Description { get; set; } 
    [DataMember]public int ISBN { get; set; } 
    [DataMember]public int Price { get; set; } 
    [DataMember]public string PublicationDate { get; set; } 
} 

Затем используйте DataContractSerializer сериализации. Вот пример:

using (var sw = new StringWriter()) 
{ 
    using (var xw = new XmlTextWriter(sw)) 
    { 
     var book = new Book(); 
     book.Author = new Author { AuthorName = "Bob" }; 
     book.Category = new Category { CategoryId = 5 }; 
     book.Publisher = new Publisher { City = "Clearwater" }; 
     var serializer = new DataContractSerializer(typeof(Book)); 
     serializer.WriteObject(xw, book); 
     var output = sw.ToString(); 
     Assert.IsNotNull(sw); 
    } 
} 

Это отвечает на вопрос, но он не решает никаких проблем. Фактически, это создает новую проблему.

Если вы просто объявить Author, Category и Publisher свойства Book как конкретных типов, то вы вынуждены использовать эти типы. Компилятор покажет ошибку, если вы попытаетесь установить это свойство с помощью любого класса, который не является Author.

Но если вы добавите атрибут KnownType, как указано выше, проблема еще хуже, потому что она скрыта. Теперь вы можете установить Author на все, что будет реализовано IUniquelyIdentifiable. Но когда вы это делаете (возможно, в какой-то другой части вашего приложения), вы не можете знать, что он будет терпеть неудачу при сериализации. Ограничение по-прежнему существует - вам еще нужно использоватьAuthor. Разница в том, что теперь вы получаете исключение во время выполнения вместо ошибки компиляции.

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

Но это все еще проблематично. Это скрытое ограничение. Вы говорите, что тип свойства: IUniquelyIdentifiable. В соответствии с надлежащим дизайном ООП и принципом замещения Лискова вы должны использовать любую реализацию. Но на самом деле вы не можете использовать какую-либо реализацию. Вы должны использовать тот, который может или не может быть помечен как «известный» тип где-то в другом месте (или в нескольких местах) в вашем коде. Кто-то может разорвать ваше приложение в любое время, не вызывая ошибки компиляции.

Основываясь на этом, я бы сказал, что используйте только вышеуказанные методы, если у вас нет выбора, например, если вам нужно сериализовать то, что вы не разработали. Если вы пишете свои собственные классы, я бы просто объявил Book с использованием конкретных типов Author, Category и Publisher.

+0

Привет, Скотт. Первоначально я использовал только конкретные типы. Но, как вы можете видеть сверху, мне нужны только одно поле данных из классов Author, Publisher и Category. Так вы можете помочь мне разобраться, как это сделать? –

+0

Вы можете просто положить 'AuthorName' в класс' Book' как строку. Я заметил, что интерфейсы используют имена как уникальные идентификаторы. Я бы рекомендовал создать класс типа 'AuthorId', который содержит только свойства, необходимые для идентификации автора, и использовать их в' Book' вместо строки или int. См. [Это сообщение в блоге] (http://scotthannen.org/blog/2016/03/26/define-types-instead-of-using-primitives.html). –

1

Вы не можете сериализовать интерфейсы. Это не работает. ваше решения изменить свойство книги к реальным сериализуемым классам:

[Serializable] 
public class Book 
{ 
    //BookId, Category, Title, Author, Publisher, Description, Price, ISBN, PublicationDate. 
    public Author Author { get; set; } 
    public Category Category { get; set; } 
    public Publisher Publisher { get; set; } 
    public int BookId { get; set; } 
    public string Title { get; set; } 
    public string Description { get; set; } 
    public int ISBN { get; set; } 
    public int Price { get; set; } 
    public string PublicationDate { get; set; } 
} 

Вопрос @Richard_Everett связан содержит один и тот же ответ. Извините, я не могу предложить лучшего решения.

+1

Это правда, что вы не можете сериализовать интерфейсы. Но вы * можете * сериализовывать члены, объявленные как интерфейсы, если вы используете «DataContractSerializer» и идентифицируете типы, которые могут представлять интерфейсы, используя атрибут «KnownType». –

Смежные вопросы