Как получить XamlReader.Parse, чтобы не выбрасывать «свойство не существует» XamlParseException? - PullRequest
2 голосов
/ 30 сентября 2009

Я использую 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)

Ответы [ 3 ]

1 голос
/ 27 июня 2018

Оказывается, это не так сложно, как может показаться на первый взгляд. Ключевая информация здесь заключается в том, что упомянутое исключение выдается не XamlReader, а скорее XamlObjectWriter, который отвечает за потребление XamlReader и создание и заполнение результирующего объекта. Так что все, что нам нужно сделать, это предоставить настроенный XamlReader, который бы просто пропускал неизвестные свойства. На мой взгляд, самый универсальный подход заключается в создании читателя, который бы обернулся вокруг другого (произвольного) читателя. Идея может быть обобщена следующим образом:

  • В методе Read мы читаем один раз из основного читателя
  • Если мы сталкиваемся с неизвестным свойством, которое можно легко определить, исследуя XamlReader.Member.IsUnknown всякий раз, когда XamlReader.NodeType равен StartMember, мы просто продолжаем чтение, пока не достигнем конца определения члена (a соответствует 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 узлы, соответствующие этим вложенным свойствам

1 голос
/ 30 сентября 2009

Если вы хотите, чтобы ваш код справлялся со старыми атрибутами, вам придется явно перехватить исключение и затем продолжить чтение файла.

Изменяя структуру данных, вы сделали старый XAML недействительным, и синтаксический анализатор вполне правильно возражает.

0 голосов
/ 01 октября 2009

Похоже, мне нужно использовать флаг DontThrowOnErrors в новом классе System.Xaml.XamlReaderSettings в .NET 4.0.

См. http://msdn.microsoft.com/en-us/library/system.xaml.xamlreadersettings.dontthrowonerrors%28VS.100%29.aspx

К сожалению, это свойство не вошло в финальную версию .NET 4, поэтому не существует простого способа сделать это .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...