当IXmlSerializable.ReadXml()中发生架构验证错误时,XmlSerializer为什么会引发异常并引发ValidationEvent。

问题描述 投票:1回答:1
我已经编写了一些测试来读取XML文件并针对XSD架构对其进行验证。我的数据对象混合使用基于属性和自定义IXmlSerializable的实现,并且使用XmlSerializer进行反序列化。

我的测试包括将未知元素插入XML,以使其不符合架构。然后,我测试验证事件是否触发。

如果未知元素放置在XML中,那么它是基于属性的数据类之一的子元素(即,属性用XmlAttribute和XmlElement属性修饰),则验证会正确触发。

但是,如果将未知元素放在XML中,那么它是IXmlSerializable类之一的子类,那么将抛出System.InvalidOperationException,但仍然会触发验证。

自定义集合的ReadXmlElements中的代码创建一个新的XmlSerializer来读取子项,这是在Deserialize调用中引发InvalidOperationException的地方。

如果我在该调用周围放置try .. catch块,它将陷入无限循环。唯一的解决方案似乎是在顶级XmlSerializer.Deserialize调用周围放置try-catch块(如测试所示)。

有人知道为什么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

Schema.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); } } }
我已经编写了一些测试来读取XML文件并针对XSD架构对其进行验证。我的数据对象混合使用基于属性和自定义IXmlSerializable的实现,并且正在使用...
c# xmlserializer ixmlserializable
1个回答
1
投票
您的基本问题是,您有一个抽象基类Entity,其继承者

有时

© www.soinside.com 2019 - 2024. All rights reserved.