我的测试包括将未知元素插入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的实现,并且正在使用...
Entity
,其继承者有时