Есть ли причина, почему базовый класс, украшенный XmlInclude, по-прежнему будет генерировать неизвестное значение типа при сериализации? - программирование
Подтвердить что ты не робот

Есть ли причина, почему базовый класс, украшенный XmlInclude, по-прежнему будет генерировать неизвестное значение типа при сериализации?

Я упрощу код, чтобы сэкономить место, но представленное иллюстрирует основную проблему.

У меня есть класс, который имеет свойство, которое является базовым типом. Существует 3 производных класса, которые могут быть назначены этому свойству.

Если я назначу какой-либо из производных классов контейнеру и попытаюсь сериализовать контейнер, XmlSerializer выбрасывает ужасный:

"Тип x не ожидался. Используйте атрибут XmlInclude или SoapInclude, чтобы указать типы, которые не известны статически".

Однако мой базовый класс уже украшен этим атрибутом, поэтому я считаю, что должно быть дополнительное "скрытое" требование.

Нечетная часть состоит в том, что по умолчанию для сериализатора WCF нет проблем с этой иерархией классов.

Класс контейнера

[DataContract]
[XmlRoot(ElementName = "TRANSACTION", Namespace = Constants.Namespace)]
public class PaymentSummaryRequest : CommandRequest
{
    [DataMember]
    public PaymentSummary Summary { get; set; }

    public PaymentSummaryRequest()
    {
        Mechanism = CommandMechanism.PaymentSummary;
    }
}

Базовый класс

[DataContract]
[XmlInclude(typeof(xPaymentSummary))]
[XmlInclude(typeof(yPaymentSummary))]
[XmlInclude(typeof(zPaymentSummary))]
[KnownType(typeof(xPaymentSummary))]
[KnownType(typeof(yPaymentSummary))]
[KnownType(typeof(zPaymentSummary))]
public abstract class PaymentSummary
{     
}

Один из производных классов

[DataContract]
public class xPaymentSummary : PaymentSummary
{
}

Код сериализации

var serializer = new XmlSerializer(typeof(PaymentSummaryRequest));
serializer.Serialize(Console.Out,new PaymentSummaryRequest{Summary = new xPaymentSummary{}});

Исключение

System.InvalidOperationException: При создании XML-документа произошла ошибка. --- > System.InvalidOperationException: Тип xPaymentSummary не ожидался. Используйте атрибут XmlInclude или SoapInclude, чтобы указать типы, которые не известны статически. в

Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterPaymentSummaryRequest.Write13_PaymentSummary (String   n, String ns, PaymentSummary o,   Boolean isNullable, Boolean needType)   в

     
  

Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterPaymentSummaryRequest.Write14_PaymentSummaryRequest (String     n, String ns, PaymentSummaryRequest o,     Boolean isNullable, Boolean needType)     в

         
    

Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterPaymentSummaryRequest.Write15_TRANSACTION (Объект       o) --- Конец внутреннего стека исключений       trace --- at

             
      

System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter         xmlWriter, Object o,         Пространства имен XmlSerializerNamespaces,         String encodingStyle, String id) в

                 
        

System.Xml.Serialization.XmlSerializer.Serialize(TextWriter           textWriter, Object o,           Пространства имен XmlSerializerNamespaces)
          в UserQuery.RunUserAuthoredQuery() в           C:\Users\Tedford\AppData\Local\Temp\uqacncyo.0.cs: линия           47

        
    
  
4b9b3361

Ответ 1

Проблема, которую вы видите, связана с тем, что PaymentSummaryRequest устанавливает пространство имен. Вы можете либо удалить пространство имен, либо добавить пространство имен в класс PaymentSummary:

[XmlRoot(Namespace = Constants.Namespace)]
[XmlInclude(typeof(xxxPaymentSummary))]
public abstract class PaymentSummary
{
}

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

Похоже, что при создании сборки XML Serialization, поскольку Root node имеет пространство имен и базовый класс не включает в себя логику XML Include в сгенерированной сборке сериализации.

Другим подходом к решению проблемы будет удаление деклараций пространства имен из самих классов и определение пространства имен в конструкторе XmlSerializer:

var serializer = new XmlSerializer(
    typeof(PaymentSummaryRequest), 
    Constants.Namespace
);

Ответ 2

У меня была та же проблема, и некоторые из Google привели меня сюда. Я не мог принять наличие атрибута XMLInclude для каждой реализации в базовом классе. Я получил его для работы с общим методом сериализации. Прежде всего, некоторые из оригинального примера OP, но немного модифицированные для ясности:

public abstract class PaymentSummary
{
  public string _summary;
  public string _type;
}

public class xPaymentSummary : PaymentSummary
{
  public xPaymentSummary() { }

  public xPaymentSummary(string summary)
  {
     _summary = summary;
     _type = this.GetType().ToString();
  }
}

В дополнение к вышесказанному, есть также yPaymentSummary и zPaymentSummary с точно такой же реализацией. Они добавляются в коллекцию, и затем метод сериализации может быть вызван для каждого:

List<PaymentSummary> summaries = new List<PaymentSummary>();
summaries.Add(new xPaymentSummary("My summary is X."));
summaries.Add(new yPaymentSummary("My summary is Y."));
summaries.Add(new zPaymentSummary("My summary is Z."));

foreach (PaymentSummary sum in summaries)
  SerializeRecord(sum);

Наконец, метод сериализации - простой универсальный метод, который использует тип записи при сериализации:

static void SerializeRecord<T>(T record) where T: PaymentSummary
{
  var serializer = new XmlSerializer(record.GetType());
  serializer.Serialize(Console.Out, record);

  Console.WriteLine(" ");
  Console.WriteLine(" ");
}

Выше приведено следующее:

enter image description here