Почему XmlSerializer генерирует исключение и вызывает ValidationEvent, когда внутри IXmlSerializable.ReadXml() возникает ошибка проверки схемы

Я написал несколько тестов для чтения файла XML и проверки его по схеме XSD. Мои объекты данных используют сочетание основанной на атрибутах и ​​пользовательской реализации IXmlSerializable, и я использую XmlSerializer для выполнения десериализации.

Мой тест включает вставку неизвестного элемента в XML, чтобы он не соответствовал схеме. Затем я проверяю, срабатывает ли событие проверки.

Если неизвестный элемент помещается в XML, так что он является дочерним элементом одного из классов данных на основе атрибутов (т. е. свойства украшены атрибутами XmlAttribute и XmlElement), то проверка выполняется правильно.

Однако, если неизвестный элемент помещается в XML, так что он является дочерним элементом одного из классов IXmlSerializable, возникает исключение System.InvalidOperationException, но проверка все равно срабатывает.

Код внутри ReadXmlElements пользовательской коллекции создает новый XmlSerializer для чтения в дочерних элементах, это вызов Deserialize, в котором создается InvalidOperationException.

Если я помещу вокруг этого вызова блок try..catch, он застрянет в бесконечном цикле. Единственное решение, по-видимому, состоит в том, чтобы поместить блок try-catch вокруг вызова XmlSerializer.Deserialize верхнего уровня (как показано в тесте).

Кто-нибудь знает, почему XmlSerializer ведет себя таким образом? В идеале я хотел бы попытаться перехватить исключение там, где оно возникло, а не использовать обработчик исключений верхнего уровня, поэтому возникает второстепенный вопрос, почему код застревает в бесконечном цикле, если блок try..catch добавлен в класс коллекции.

Вот возникшее исключение:

System.InvalidOperationException: There is an error in XML document (13, 10). ---> System.InvalidOperationException: There is an error in XML document (13, 10). ---> System.InvalidOperationException: <UnknownElement xmlns='example'> was not expected.
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderGroup.Read1_Group()
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events)
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader)
   at XmlSerializerTest.EntityCollection~1.ReadXmlElements(XmlReader reader) in C:\source\repos\XmlSerializerTest\XmlSerializerTest\EntityCollection.cs:line 55
   at XmlSerializerTest.EntityCollection~1.ReadXml(XmlReader reader) in C:\Users\NGGMN9O\source\repos\XmlSerializerTest\XmlSerializerTest\EntityCollection.cs:line 41
   at System.Xml.Serialization.XmlSerializationReader.ReadSerializable(IXmlSerializable serializable, Boolean wrappedAny)
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderExample.Read2_Example(Boolean isNullable, Boolean checkType)
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderExample.Read3_Example()
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events)
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader)
   at XmlSerializerTest.StackOverflowExample.InvalidElementInGroupTest() in C:\source\repos\XmlSerializerTest\XmlSerializerTest\XmlSerializerTest.cs:line 35

Схема.xsd

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema xmlns:local="example"
           attributeFormDefault="unqualified"
           elementFormDefault="qualified"
           targetNamespace="example"
           version="1.0"
           xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <!--  Attribute Groups -->
  <xs:attributeGroup name="Identifiers">
    <xs:attribute name="Id"
                  type="xs:string"
                  use="required" />
    <xs:attribute name="Name"
                  type="xs:string"
                  use="required" />
  </xs:attributeGroup>
  <!-- Complex Types -->
  <xs:complexType abstract="true"
                  name="Entity">
    <xs:sequence>
      <xs:element name="Description"
                  type="xs:string"
                  minOccurs="0"
                  maxOccurs="1" />
    </xs:sequence>
    <xs:attributeGroup ref="local:Identifiers" />
  </xs:complexType>
  <xs:complexType name="DerivedEntity">
    <xs:complexContent>
      <xs:extension base="local:Entity">
        <xs:attribute name="Parameter"
                      use="required" />
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>
  <xs:complexType name="Groups">
      <xs:sequence>
          <xs:element name="Group" type="local:Group" minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
  </xs:complexType>
  <xs:complexType name="Group">
    <xs:complexContent>
      <xs:extension base="local:Entity">
        <xs:sequence>
          <xs:element name="DerivedEntity"
                      type="local:DerivedEntity"
                      minOccurs="0"
                      maxOccurs="unbounded" />
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>
  <!-- Main Schema Definition -->
  <xs:element name="Example">
      <xs:complexType>
          <xs:sequence>
              <xs:element name="Groups"
                          type="local:Groups"
                          minOccurs="1"
                          maxOccurs="1" />
          </xs:sequence>
      </xs:complexType>
  </xs:element>
</xs:schema>

InvalidElementInGroup.xml

<?xml version="1.0"?>
<Example xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="example">
    <Groups>
        <Group Name="abc" Id="123">
            <DerivedEntity Id="123" Name="xyz" Parameter="ijk">
                <Description>def</Description>
            </DerivedEntity>
            <DerivedEntity Id="234" Name="bob" Parameter="12"/>
        </Group>
        <Group Name="def" Id="124">
            <Description>This is a description.</Description>
        </Group>
        <UnknownElement/>
    </Groups>
</Example>

Реализация Примечание. Код, показанный в этом примере, не является рабочим кодом. Я знаю, что мог бы просто использовать реализацию List<T>, которая поддерживает сериализацию без необходимости реализации IXmlSerializable.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

namespace XmlSerializerTest
{
    public class Example
    {
        public Example()
        {
            Groups = new Groups();
        }

        public Groups Groups { get; set; }
    }

    public class Groups : EntityCollection<Group>
    {

    }
    public class Group : Entity, IXmlSerializable
    {
        private EntityCollection<DerivedEntity> entityCollection;

        public Group()
        {
            this.entityCollection = new EntityCollection<DerivedEntity>();
        }

        #region IXmlSerializable Implementation

        public XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(XmlReader reader)
        {
            reader.MoveToContent();

            // Read the attributes
            ReadXmlAttributes(reader);

            // Consume the start element
            bool isEmptyElement = reader.IsEmptyElement;
            reader.ReadStartElement();
            if (!isEmptyElement)
            {
                ReadXmlElements(reader);
                reader.ReadEndElement();
            }
        }

        /// <summary>
        /// Reads the XML elements.
        /// </summary>
        /// <param name="reader">The reader.</param>
        public override void ReadXmlElements(XmlReader reader)
        {
            // Handle the optional base class description element
            base.ReadXmlElements(reader);

            entityCollection.ReadXmlElements(reader);
        }

        public void WriteXml(XmlWriter writer)
        {
            throw new NotImplementedException();
        }

        #endregion
    }

    public class EntityCollection<T> : IXmlSerializable, IList<T> where T : Entity
    {
        private List<T> childEntityField;

        public EntityCollection()
        {
            childEntityField = new List<T>();
        }

        #region IXmlSerializable Implementation

        public XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(XmlReader reader)
        {
            reader.MoveToContent();

            // Read the attributes
            ReadXmlAttributes(reader);

            // Consume the start element
            bool isEmptyElement = reader.IsEmptyElement;
            reader.ReadStartElement();
            if (!isEmptyElement)
            {
                ReadXmlElements(reader);
                reader.ReadEndElement();
            }
        }

        public virtual void ReadXmlAttributes(XmlReader reader)
        {
        }

        public virtual void ReadXmlElements(XmlReader reader)
        {
            XmlSerializer deserializer = new XmlSerializer(typeof(T), "example");
            while (reader.IsStartElement())
            {
                T item = (T)deserializer.Deserialize(reader);  // throws an InvalidOperationException if an unknown element is encountered.
                if (item != null)
                {
                    Add(item);
                }
            }
        }

        public void WriteXml(XmlWriter writer)
        {
            throw new NotImplementedException();
        }
        #endregion

        #region IList Implementation

        public IEnumerator<T> GetEnumerator()
        {
            return childEntityField.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return ((IEnumerable)childEntityField).GetEnumerator();
        }

        public void Add(T item)
        {
            childEntityField.Add(item);
        }

        public void Clear()
        {
            childEntityField.Clear();
        }

        public bool Contains(T item)
        {
            return childEntityField.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            childEntityField.CopyTo(array, arrayIndex);
        }

        public bool Remove(T item)
        {
            return childEntityField.Remove(item);
        }

        public int Count => childEntityField.Count;

        public bool IsReadOnly => ((ICollection<T>)childEntityField).IsReadOnly;

        public int IndexOf(T item)
        {
            return childEntityField.IndexOf(item);
        }

        public void Insert(int index, T item)
        {
            childEntityField.Insert(index, item);
        }

        public void RemoveAt(int index)
        {
            childEntityField.RemoveAt(index);
        }

        public T this[int index]
        {
            get => childEntityField[index];
            set => childEntityField[index] = value;
        }

        #endregion
    }

    [System.Xml.Serialization.XmlIncludeAttribute(typeof(DerivedEntity))]
    public abstract class Entity
    {

        public string Description { get; set; }

        public string Id { get; set; }

        public string Name { get; set; }

        public virtual void ReadXmlAttributes(XmlReader reader)
        {
            Id = reader.GetAttribute("Id");
            Name = reader.GetAttribute("Name");
        }

        public virtual void ReadXmlElements(XmlReader reader)
        {
            if (reader.IsStartElement("Description"))
            {
                Description = reader.ReadElementContentAsString();
            }
        }
    }

    public class DerivedEntity : Entity
    {
        public string Parameter { get; set; }
    }
}

Тест

namespace XmlSerializerTest
{
    using System;
    using System.IO;
    using System.Xml;
    using System.Xml.Schema;
    using System.Xml.Serialization;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    [TestClass]
    public class StackOverflowExample
    {
        [TestMethod]
        [DeploymentItem(@"Schema.xsd")]
        [DeploymentItem(@"InvalidElementInGroup.xml")]
        public void InvalidElementInGroupTest()
        {
            // Open the file
            FileStream stream = new FileStream("InvalidElementInGroup.xml", FileMode.Open);

            // Configure settings
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.Schemas.Add(null, @"Schema.xsd");
            settings.ValidationType = ValidationType.Schema;
            settings.ValidationEventHandler += OnValidationEvent;

            XmlSerializer xmlDeserializer = new XmlSerializer(typeof(Example), "example");

            // Deserialize from the stream
            stream.Position = 0;
            XmlReader xmlReader = XmlReader.Create(stream, settings);

            try
            {
                Example deserializedObject = (Example)xmlDeserializer.Deserialize(xmlReader);
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception: " + e);
            }
        }

        private void OnValidationEvent(object sender, ValidationEventArgs e)
        {
            Console.WriteLine("Validation Event: " + e.Message);
        }
    }
}

person Tim Calladene    schedule 28.02.2020    source источник
comment
Не могли бы вы отредактировать свой вопрос, включив в него минимальный воспроизводимый пример -- минимальные модели и XML, воспроизводящие проблему?   -  person dbc    schedule 28.02.2020
comment
Теперь я обновил сообщение, чтобы включить воспроизводимый пример. При этом кажется, что я фактически ответил на первоначальный вопрос, почему событие проверки не срабатывает, поэтому я также немного изменил формулировку вопроса.   -  person Tim Calladene    schedule 01.03.2020
comment
@TimCalladene Что ты пытаешься сделать? Почему вы предоставляете пользовательскую реализацию IXmlSerializable, когда вы просто сериализуете/десериализуете список? Можете ли вы отредактировать свой вопрос, чтобы объяснить, в чем проблема, что проверка не проходит и выдается InvalidOperationException? Вроде все работает как надо, валидация ругается и метод Deserialize() не может продолжаться.   -  person Progman    schedule 01.03.2020
comment
docs.microsoft.com/ en-us/dotnet/api/   -  person Hans Passant    schedule 01.03.2020


Ответы (1)


Ваша основная проблема заключается в том, что у вас есть абстрактный базовый класс Entity, наследники которого иногда реализуют IXmlSerializable, а иногда и нет, и когда они это делают, они включаются в коллекцию, которая также реализует IXmlSerializable и смешивает свойства коллекции с дочерними элементами коллекции в своем XML. Где-то в процессе чтения этого XML вы неправильно продвигаете свой XmlReader, и десериализация завершается ошибкой.

При реализации IXmlSerializable вам необходимо придерживаться правил, указанных в этом ответе на Правильный способ реализации IXmlSerializable? от Марка Гравелла а также документация:

Для IXmlSerializable.WriteXml(XmlWriter) :

Предоставляемая вами реализация WriteXml должна записывать XML-представление объекта. Платформа записывает элемент-оболочку и позиционирует модуль записи XML после его запуска. Ваша реализация может записывать свое содержимое, включая дочерние элементы. Затем фреймворк закрывает элемент-оболочку.

Для IXmlSerializable.ReadXml(XmlReader) :

Метод ReadXml должен воссоздать ваш объект, используя информацию, записанную методом WriteXml.

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

Обратите особое внимание на то, что ReadXml() должен полностью потреблять элемент контейнера. Это оказывается проблематичным в сценариях наследования; отвечает ли базовый класс за использование внешнего элемента или производного класса? Кроме того, если какой-либо производный класс неправильно размещает XmlReader во время чтения, это может пройти незамеченным модульными тестами, но приведет к тому, что последующие данные в файле XML будут проигнорированы или повреждены в рабочей среде.

Таким образом, имеет смысл создать структуру расширения для чтения и записи IXmlSerializable объектов, чьи базовые и производные классы имеют собственную логику (де)сериализации, в которой обработка элемента-контейнера, каждого атрибута и каждого дочернего элемента разделена:

public static class XmlSerializationExtensions
{
    public static void ReadIXmlSerializable(XmlReader reader, Func<XmlReader, bool> handleXmlAttribute, Func<XmlReader, bool> handleXmlElement, Func<XmlReader, bool> handleXmlText)
    {
        //https://docs.microsoft.com/en-us/dotnet/api/system.xml.serialization.ixmlserializable.readxml?view=netframework-4.8#remarks
        //When this method is called, the reader is positioned on the start tag that wraps the information for your type. 
        //That is, directly on the start tag that indicates the beginning of a serialized object. 
        //When this method returns, it must have read the entire element from beginning to end, including all of its contents. 
        //Unlike the WriteXml method, the framework does not handle the wrapper element automatically. Your implementation must do so. 
        //Failing to observe these positioning rules may cause code to generate unexpected runtime exceptions or corrupt data.
        reader.MoveToContent();
        if (reader.NodeType != XmlNodeType.Element)
            throw new XmlException(string.Format("Invalid NodeType {0}", reader.NodeType));
        if (reader.HasAttributes)
        {
            for (int i = 0; i < reader.AttributeCount; i++)
            {
                reader.MoveToAttribute(i);
                handleXmlAttribute(reader);
            }
            reader.MoveToElement(); // Moves the reader back to the element node.
        }
        if (reader.IsEmptyElement)
        {
            reader.Read();
            return;
        }
        reader.ReadStartElement(); // Advance to the first sub element of the wrapper element.
        while (reader.NodeType != XmlNodeType.EndElement)
        {
            if (reader.NodeType == XmlNodeType.Element)
            {
                using (var subReader = reader.ReadSubtree())
                {
                    subReader.MoveToContent();
                    handleXmlElement(subReader);
                }
                // ReadSubtree() leaves the reader positioned ON the end of the element, so read that also.
                reader.Read();
            }
            else if (reader.NodeType == XmlNodeType.Text || reader.NodeType == XmlNodeType.CDATA)
            {
                var type = reader.NodeType;
                handleXmlText(reader);
                // Ensure that the reader was not advanced.
                if (reader.NodeType != type)
                    throw new XmlException(string.Format("handleXmlText incorrectly advanced the reader to a new node {0}", reader.NodeType));
                reader.Read();
            }
            else // Whitespace, comment
            {
                // Skip() leaves the reader positioned AFTER the end of the node.
                reader.Skip();
            }
        }
        // Move past the end of the wrapper element
        reader.ReadEndElement();
    }

    public static void WriteIXmlSerializable(XmlWriter writer, Action<XmlWriter> writeAttributes, Action<XmlWriter> writeNodes)
    {
        //https://docs.microsoft.com/en-us/dotnet/api/system.xml.serialization.ixmlserializable.writexml?view=netframework-4.8#remarks
        //The WriteXml implementation you provide should write out the XML representation of the object. 
        //The framework writes a wrapper element and positions the XML writer after its start. Your implementation may write its contents, including child elements. 
        //The framework then closes the wrapper element.
        writeAttributes(writer);
        writeNodes(writer);
    }
}

Затем измените модель данных следующим образом:

public class Constants
{
    public const string ExampleNamespace = "example";
}

[XmlRoot(Namespace = Constants.ExampleNamespace)]
public class Example
{
    public Example()
    {
        Groups = new Groups();
    }

    public Groups Groups { get; set; }
}

public class Groups : EntityCollection<Group>
{

}

public class EntityCollection<T> : IXmlSerializable, IList<T> where T : Entity
{
    private List<T> childEntityField;

    public EntityCollection()
    {
        childEntityField = new List<T>();
    }

    #region IXmlSerializable Implementation

    public XmlSchema GetSchema() { return null; }

    protected internal virtual bool HandleXmlAttribute(XmlReader reader) { return false; }

    protected internal virtual void WriteAttributes(XmlWriter writer) { }

    protected internal virtual bool HandleXmlElement(XmlReader reader)
    {
        var serializer = new XmlSerializer(typeof(T), Constants.ExampleNamespace);
        if (serializer.CanDeserialize(reader))
        {
            T item = (T)serializer.Deserialize(reader);
            if (item != null)
                Add(item);
            return true;
        }
        return false;
    }

    protected internal virtual void WriteNodes(XmlWriter writer)
    {
        var serializer = new XmlSerializer(typeof(T), Constants.ExampleNamespace);
        foreach (var item in this)
        {
            serializer.Serialize(writer, item);
        }
    }

    public void ReadXml(XmlReader reader)
    {
        XmlSerializationExtensions.ReadIXmlSerializable(reader, r => HandleXmlAttribute(r), r => HandleXmlElement(r), r => false);
    }

    public void WriteXml(XmlWriter writer)
    {
        XmlSerializationExtensions.WriteIXmlSerializable(writer, w => WriteAttributes(w), w => WriteNodes(w));
    }

    #endregion

    #region IList Implementation

    public IEnumerator<T> GetEnumerator()
    {
        return childEntityField.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return ((IEnumerable)childEntityField).GetEnumerator();
    }

    public void Add(T item)
    {
        childEntityField.Add(item);
    }

    public void Clear()
    {
        childEntityField.Clear();
    }

    public bool Contains(T item)
    {
        return childEntityField.Contains(item);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        childEntityField.CopyTo(array, arrayIndex);
    }

    public bool Remove(T item)
    {
        return childEntityField.Remove(item);
    }

    public int Count { get { return childEntityField.Count; } }

    public bool IsReadOnly { get { return ((ICollection<T>)childEntityField).IsReadOnly; } }

    public int IndexOf(T item)
    {
        return childEntityField.IndexOf(item);
    }

    public void Insert(int index, T item)
    {
        childEntityField.Insert(index, item);
    }

    public void RemoveAt(int index)
    {
        childEntityField.RemoveAt(index);
    }

    public T this[int index]
    {
        get { return childEntityField[index]; }
        set { childEntityField[index] = value; }
    }

    #endregion
}

public class Group : Entity, IXmlSerializable
{
    private EntityCollection<DerivedEntity> entityCollection;

    public Group()
    {
        this.entityCollection = new EntityCollection<DerivedEntity>();
    }

    #region IXmlSerializable Implementation

    public XmlSchema GetSchema()
    {
        return null;
    }

    protected override bool HandleXmlElement(XmlReader reader)
    {
        if (base.HandleXmlElement(reader))
            return true;
        return entityCollection.HandleXmlElement(reader);
    }

    protected override void WriteNodes(XmlWriter writer)
    {
        base.WriteNodes(writer);
        entityCollection.WriteNodes(writer);
    }

    protected override bool HandleXmlAttribute(XmlReader reader)
    {
        if (base.HandleXmlAttribute(reader))
            return true;
        if (entityCollection.HandleXmlAttribute(reader))
            return true;
        return false;
    }

    protected override void WriteAttributes(XmlWriter writer)
    {
        base.WriteAttributes(writer);
        entityCollection.WriteAttributes(writer);
    }

    public void ReadXml(XmlReader reader)
    {
        XmlSerializationExtensions.ReadIXmlSerializable(reader, r => HandleXmlAttribute(r), r => HandleXmlElement(r), r => false);
    }

    public void WriteXml(XmlWriter writer)
    {
        XmlSerializationExtensions.WriteIXmlSerializable(writer, w => WriteAttributes(w), w => WriteNodes(w));
    }

    #endregion
}

public class DerivedEntity : Entity
{
    [XmlAttribute]
    public string Parameter { get; set; }
}

[System.Xml.Serialization.XmlIncludeAttribute(typeof(DerivedEntity))]
public abstract class Entity
{
    [XmlElement]
    public string Description { get; set; }

    [XmlAttribute]
    public string Id { get; set; }

    [XmlAttribute]
    public string Name { get; set; }

    protected virtual void WriteAttributes(XmlWriter writer)
    {
        if (Id != null)
            writer.WriteAttributeString("Id", Id);
        if (Name != null)
            writer.WriteAttributeString("Name", Name);
    }

    protected virtual bool HandleXmlAttribute(XmlReader reader)
    {
        if (reader.LocalName == "Id")
        {
            Id = reader.Value;
            return true;
        }
        else if (reader.LocalName == "Name")
        {
            Name = reader.Value;
            return true;
        }
        return false;
    }

    protected virtual void WriteNodes(XmlWriter writer)
    {
        if (Description != null)
        {
            writer.WriteElementString("Description", Description);
        }
    }

    protected virtual bool HandleXmlElement(XmlReader reader)
    {
        if (reader.LocalName == "Description")
        {
            Description = reader.ReadElementContentAsString();
            return true;
        }
        return false;
    }
}

И вы сможете успешно десериализовать и повторно сериализовать Example. Демонстрационная скрипта здесь.

Примечания:

  • Серьезно подумайте об упрощении этой архитектуры. Это все слишком сложно.

  • Одно событие проверки будет правильно вызвано для <UnknownElement/> внутри <Groups>, так как в схеме нет такого элемента.

  • XmlSerializer.Deserialize() выдаст InvalidOperationException, если имя и пространство имен корневого XML-элемента не совпадают с ожидаемыми именем и пространством имен. Вы можете проверить правильность имени и пространства имен, вызвав XmlSerializer.CanDeserialize(XmlReader).

  • Обязательно протестируйте десериализацию XML с отступами и без них. Иногда метод ReadXml() продвинет читатель на один узел слишком далеко, но если XML содержит незначительный отступ (т. е. форматирование), то никакого вреда не будет, поскольку будет пропущен только незначительный узел пробела.

  • Когда Entity.HandleXmlElement(XmlReader reader) переопределяется в производном классе, сначала должен вызываться метод базового класса. Если метод базового класса обрабатывает элемент, возвращается true, и производный класс не должен пытаться его обработать. Точно так же, если производный класс обрабатывает элемент, true следует возвращать другим производным классам, указывая на то, что элемент был обработан. false возвращается, когда ни класс, ни базовый класс не могут обработать элемент.

  • XmlReader.ReadSubtree() можно использовать, чтобы гарантировать, что какой-либо производный класс не сможет неправильно расположить XmlReader внутри HandleXmlElement(XmlReader reader).

  • Если вы используете какой-либо конструктор, кроме new XmlSerializer(Type) и new XmlSerializer(Type, String) для создания XmlSerializer необходимо создать его только один раз и статически кэшировать во избежание серьезной утечки памяти. Почему см. документация и утечка памяти с использованием StreamReader и XmlSerializer. Вы не создаете сериализатор таким образом в своем примере кода, но можете делать это в своем рабочем коде.

person dbc    schedule 03.03.2020
comment
Спасибо за ваш подробный ответ. Это очень полезно. - person Tim Calladene; 04.03.2020