反序列化 xml 时忽略未知类型
Posted
技术标签:
【中文标题】反序列化 xml 时忽略未知类型【英文标题】:Ignore unknown types when deserializing xml 【发布时间】:2012-10-19 15:37:53 【问题描述】:我有这个代码:
[XmlType( "Metadata" )]
[Serializable]
public class MetadataContainer : List<MetadataBase>
[XmlType( "Meta" )]
[XmlInclude( typeof( ReadonlyMetadata ) )]
[Serializable]
public abstract class MetadataBase
[XmlType( "Readonly" )]
[Serializable]
public class ReadonlyMetadata : MetadataBase
[TestFixture]
public class SerializationTests
[Test]
public void Can_deserialize_with_known_type()
const string text = @"<Metadata xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"">
<Meta xsi:type=""Readonly"" />
</Metadata>";
var serializer = new XmlSerializer( typeof( MetadataContainer ) );
var metas = (MetadataContainer)serializer.Deserialize( XmlReader.Create( new StringReader( text ) ) );
Assert.That( metas.Count, Is.EqualTo( 1 ) );
Assert.That( metas.First(), Is.InstanceOf<ReadonlyMetadata>() );
[Test]
public void Can_deserialize_with_unknown_type()
const string text = @"<Metadata xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"">
<Meta xsi:type=""Hello"" />
</Metadata>";
var serializer = new XmlSerializer( typeof( MetadataContainer ) );
var metas = (MetadataContainer)serializer.Deserialize( XmlReader.Create( new StringReader( text ) ) );
Assert.That( metas.Count, Is.EqualTo( 0 ) );
第一个测试有效,但是当我运行第二个时,我得到了这个错误:
System.InvalidOperationException : XML 文档中存在错误 (2, 9)。 ----> System.InvalidOperationException : 指定类型未被识别:name='Hello', namespace='', at .
我希望它忽略无法识别的类型,而不是收到此错误。有没有办法做到这一点?
【问题讨论】:
我认为这将是订阅一个或多个Unknown
- events 的情况,但尝试它似乎没有帮助。神秘。
我也认为,结果很奇怪。虽然我没有设法找到解决方案。
【参考方案1】:
类似问题的通用解决方案:
看看unknown element event (link) 和unknown attribute event (link) 看看他们是否能解决问题,否则我们就得搞砸了。继续阅读...
此问题的有效解决方案
请记住,我不知道您的任务是什么,AFAIK 它将 xml 序列化到您的数据结构中。如果您可以更改数据结构,我建议您查看 Linq2XML 并为您的目的创建一个智能工厂。
[TestMethod]
public void TestLinq2Xml()
const string text = @"<Metadata xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"">
<Meta xsi:type=""Readonly"" />
<Meta xsi:type=""Garbage"" />
</Metadata>";
// Get the "names" of all implementors of MetadataBase
var types = AppDomain.CurrentDomain.GetAssemblies().ToList()
.SelectMany(s => s.GetTypes())
.Where(p => typeof(MetadataBase).IsAssignableFrom(p) && !p.IsAbstract && !p.IsInterface)
.Where(t => t.GetCustomAttributes(typeof(XmlTypeAttribute), false).Any())
.Select(t => t.GetCustomAttributes(typeof(XmlTypeAttribute), false)
.Cast<XmlTypeAttribute>().First().TypeName);
// Create a parser
var parser = new XmlSerializer(typeof(MetadataBase));
// Create metadatacontainer to fill
var metas = new MetadataContainer();
// Fill it with matching from from the XML
metas.AddRange((from t in XDocument.Parse(text).Descendants("Meta")
where types.Contains(t.Attribute(XName.Get("type", "http://www.w3.org/2001/XMLSchema-instance")).Value)
select (MetadataBase)parser.Deserialize(t.CreateReader())).ToList());
// Should be one guy present
Assert.AreEqual(metas.Count, 1);
【讨论】:
【参考方案2】:捕获数组中的所有未知元素。您仍然可以使用它们并稍后尝试反序列化,但这允许反序列化完成。您将需要在您定义的每个类中使用它来反序列化您怀疑存在未知元素的元素。
每http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlanyelementattribute.aspx:
Public Class XClass
' Apply the XmlAnyElementAttribute to a field returning an array
' of XmlElement objects.
<XmlAnyElement()> Public AllElements() As XmlElement
End Class 'XClass
【讨论】:
投反对票很好,但请留下反馈以帮助我修改答案。 非常有趣的答案。必须尝试一下。以上是关于反序列化 xml 时忽略未知类型的主要内容,如果未能解决你的问题,请参考以下文章