我正在使用XAML来序列化某些对象,并且它在大多数情况下都很有效。
我现在面临的问题是,当我更改数据结构时,所有旧对象都会产生如下所示的异常。我不介意价值是否会丢失。
有没有办法关闭这些异常并让xaml阅读器忽略未知属性?如果现在没有办法做到这一点,新的System.Xaml命名空间中是否有可能做到的事情?
System.Windows.Markup.XamlParseException: The property 'BorderPadding' does not exist in XML namespace 'clr-namespace:TemplateGenerator;assembly=App_Code'. Line '1' Position '158'.
at System.Windows.Markup.XamlParser.ThrowExceptionWithLine(String message, Int32 lineNumber, Int32 linePosition)
at System.Windows.Markup.XamlParser.ThrowException(String id, String value1, String value2, Int32 lineNumber, Int32 linePosition)
at System.Windows.Markup.XamlParser.WriteUnknownAttribute(XamlUnknownAttributeNode xamlUnknownAttributeNode)
at System.Windows.Markup.XamlParser.ProcessXamlNode(XamlNode xamlNode, Boolean& cleanup, Boolean& done)
at System.Windows.Markup.XamlParser.ReadXaml(Boolean singleRecordMode)
at System.Windows.Markup.TreeBuilderXamlTranslator._Parse()
at System.Windows.Markup.XamlParser.Parse()
at System.Windows.Markup.XamlTreeBuilder.ParseFragment()
at System.Windows.Markup.TreeBuilder.Parse()
at System.Windows.Markup.XamlReader.XmlTreeBuildDefault(ParserContext pc, XmlReader reader, Boolean wrapWithMarkupCompatReader, XamlParseMode parseMode, Boolean etwTracingEnabled)
at System.Windows.Markup.XamlReader.Load(XmlReader reader)
at System.Windows.Markup.XamlReader.Parse(String xamlText)
答案 0 :(得分:1)
如果您希望代码处理旧属性,那么您将不得不明确地捕获异常,然后继续读取文件。
通过更改数据结构,您使旧的XAML无效,并且解析器非常正确地反对。
答案 1 :(得分:1)
事实证明,完成起来并不像乍看起来那样困难。这里的关键信息是,提到的异常不是由XamlReader
引发的,而是由XamlObjectWriter
引发的,它负责消耗XamlReader
并创建和填充结果对象。因此,我们所需要做的就是提供一个定制的XamlReader
,它将简单地跳过未知属性。我认为最通用的方法是创建一个读者,该读者将包裹另一个(任意)读者。这个想法可以总结如下:
Read
方法中,我们从基础阅读器读取了一次XamlReader.Member.IsUnknown
为XamlReader.NodeType
时通过检查StartMember
即可轻松确定,我们将继续读取直到达到成员定义的末尾( strong>对应的 1 EndMember
节点),然后再阅读一次,移至下一个节点;如果下一个节点也是未知属性,则重复该过程这样一来,对Read
的一次调用将跳过未知的属性,可能会导致从底层读取器进行多次读取,但是这种行为对使用者是透明的。
这是示例代码:
public class LaxXamlReader : XamlReader
{
public override bool Read()
{
//Read once from the underlying reader
_Reader.Read();
//Check if current node is an unknown property
while (NodeType == XamlNodeType.StartMember && Member.IsUnknown)
{
//We need to track member nesting level so that we can correctly
//identify the corresponding EndMember node
var level = 1;
while (level > 0)
{
_Reader.Read();
if (NodeType == XamlNodeType.StartMember)
level++;
else if (NodeType == XamlNodeType.EndMember)
level--;
}
//At this point we're at the corresponsing EndMember node, so we
//advance to the next node; if it's also an unknown property, it
//will be caught by the while loop
_Reader.Read();
}
//If we've reached the end of input return false
return !IsEof;
}
public override XamlReader ReadSubtree()
=> new LaxXamlReader(_Reader.ReadSubtree());
protected override void Dispose(bool disposing)
{
//Only dispose the underlying reader if Dispose() was called;
//otherwise let GC do the job
if (disposing)
((IDisposable)_Reader).Dispose();
base.Dispose(disposing);
}
//The code below simply forwards the functionality from the underlying reader
public LaxXamlReader(XamlReader reader)
{
_Reader = reader;
}
private readonly XamlReader _Reader;
public override bool IsEof => _Reader.IsEof;
public override XamlMember Member => _Reader.Member;
public override NamespaceDeclaration Namespace => _Reader.Namespace;
public override XamlNodeType NodeType => _Reader.NodeType;
public override XamlSchemaContext SchemaContext => _Reader.SchemaContext;
public override XamlType Type => _Reader.Type;
public override object Value => _Reader.Value;
public override void Skip() => _Reader.Skip();
}
用法示例:
var xaml = "<Object Foo=\"Bar\" xmlns=\"clr-namespace:System;assembly=mscorlib\" />";
var obj = XamlServices.Load(new LaxXamlReader(new XamlXmlReader(new StringReader(xaml))));
请注意,XamlReader
(以及其他提到的 XAML 相关类型)是System.Xaml
命名空间中定义的类型。
1 由于 XAML 中的属性可以写为元素并包含具有自己属性的对象,因此我们需要忽略与这些嵌套属性相对应的EndMember
节点
答案 2 :(得分:0)
看起来我需要使用的是.NET 4.0中新DontThrowOnErrors
类中的System.Xaml.XamlReaderSettings
标志。
不幸的是,该属性没有进入.NET 4的最终版本,因此没有简单的方法来执行此操作。