XmlSerializer在IXmlSerializable内校验出错时抛异常并触发验证事件的原因
Let’s break down your observations into two key parts: why the InvalidOperationException only happens with IXmlSerializable classes, and why a local try-catch causes an infinite loop.
1. Why InvalidOperationException Throws for IXmlSerializable, But Not Attribute-Marked Classes
The core difference lies in how XmlSerializer handles unknown elements for these two types:
Attribute-marked classes (e.g.,
[XmlRoot],[XmlElement]):
The default deserialization logic for these classes is designed to be forgiving. When the XSD validator detects an unknown element, it triggers the validation event as expected—but the serializer itself doesn’t throw an exception. It simply skips the unknown element (unless you’ve explicitly configured strict behavior) and continues deserializing the rest of the document. The validation event runs independently of the serializer’s core deserialization flow here.IXmlSerializable classes:
When you implementIXmlSerializable, you take full control of the deserialization process viaReadXml(orReadXmlElementsin your custom collection). When you callXmlSerializer.Deserializeinside this method to read child items, the serializer doesn’t have the built-in "skip unknown elements" logic that it uses for attribute-marked classes.
When the XSD validator flags an unknown element, the validation event still fires (since schema validation scans the entire XML regardless of deserialization progress), but theDeserializecall will immediately throw aSystem.InvalidOperationException—it encounters an element that doesn’t match the expected structure, and your custom implementation doesn’t include logic to handle this case.
2. Why Local try-catch Causes Infinite Loops (And Top-Level Fixes It)
The infinite loop stems from how XmlReader and IXmlSerializable interact during deserialization:
When you add a
try-catchinside your custom collection’sReadXmlElementsmethod around theDeserializecall:- The
Deserializecall hits the unknown element and throws an exception. - Your catch block captures the exception, but you don’t advance the
XmlReaderpast the problematic element. - The serializer expects the
ReadXmlmethod to properly position theXmlReaderafter processing the current node. Since the reader is still pointing at the unknown element, the next iteration of your deserialization logic will attempt to read the same element again. - This creates a loop: throw → catch → re-read the same element → throw again, indefinitely.
- The
Top-level try-catch works:
When you wrap the entire top-levelXmlSerializer.Deserializecall in a try-catch, the exception is caught once, and the entire deserialization process terminates immediately. There’s no retry or reprocessing of the same node because the serializer stops executing entirely after the exception is thrown. TheXmlReaderdoesn’t get stuck in a loop because we don’t attempt to continue deserializing after the error.
Quick Fix for Local try-catch (If You Need It)
If you must handle the exception locally instead of at the top level, you need to manually advance the XmlReader past the unknown element in your catch block:
try { // Your Deserialize call here } catch (InvalidOperationException) { // Skip the unknown element to avoid looping reader.Skip(); }
This moves the reader past the problematic node, so the next deserialization attempt processes the next valid element instead of re-reading the same one.
内容的提问来源于stack exchange,提问作者Tim Calladene

