Доступ к информации о схеме во время проверки XML - PullRequest
1 голос
/ 07 октября 2019

У меня есть плохой XML, который не проходит проверку по своей схеме. Ошибки почти все одинаковы - пустые элементы, которые нарушают модель документа, - но они встречаются для потенциально сотен различных элементов в документе.

Мое предполагаемое решение состоит в том, чтобы проверить документ, отловить оскорбительныепустые элементы из свойства SourceObject объектов исключений, сгенерированных (если они есть) в списке XElement, затем удалите эти элементы из документа. Однако свойство SourceObject всегда имеет значение null.

Прочитав об этом, я узнал, что объект документа не заполняется информацией схемы, пока не произойдет проверка. Однако, принимая это во внимание, я все еще не могу получить какую-либо полезную информацию из процесса проверки, потому что соответствующие свойства объекта всегда равны нулю, независимо от того, когда я пытаюсь получить к ним доступ.

Вот что у меня есть до сих пор:

public void FixXml(string xmlDoc)
{
    XDocument doc = XDocument.Parse(xmlDoc);
    XmlSchemaSet schema = new XmlSchemaSet();
    schema.Add("", @"../../test.xsd");
    schema.Compile();

    doc.Validate(schema, (Callback));

    foreach (XElement element in errors)
    {
        // This is where I'd start making changes to the document if the list didn't contain a bunch of nulls.
    }
}

Метод обратного вызова: (вероятно, я вставлю это в лямбду, когда я уверен, что код работает).

private void Callback(object sender, ValidationEventArgs eventArgs)
{
    XmlSchemaValidationException ex = (eventArgs.Exception as XmlSchemaValidationException);

    if (ex != null)
    {
        XElement element = (ex.SourceObject as XElement);
        errors.Add(element);
    }
}

Этот вопрос и его ответ были полезны для меня, и я применил некоторые части решения для своего собственного проекта, но, похоже, он все еще не работает. Я чувствую, что упускаю что-то очевидное и глупое здесь.

1 Ответ

1 голос
/ 08 октября 2019

Причина, по которой XmlSchemaValidationException.SourceObject составляет null, объясняется в документах

, когда во время проверки выдается XmlSchemaValidationExceptionКласс, который реализует интерфейс IXPathNavigable, такой как класс XPathNavigator или XmlNode, объект, возвращаемый свойством SourceObject, является экземпляром класса, который реализует интерфейс IXPathNavigable.

КогдаXmlSchemaValidationException генерируется во время проверки валидирующим объектом XmlReader, значение свойства SourceObject равно нулю.

К сожалению, XDocument не реализуетIXPathNavigable и, таким образом, SourceObject, как задокументировано, null.

Если все, что вам нужно, это SourceObject, вы можете создать вызов Extensions.CreateNavigator(this XNode node), чтобы создать навигатор для документа, затем подтвердите, используя XPathNavigator.CheckValidity(XmlSchemaSet, ValidationEventHandler), например:

var errors = new List<XmlSchemaValidationException>();

ValidationEventHandler callback = (sender, args) =>
{
    var exception = (args.Exception as XmlSchemaValidationException);
    if (exception != null)
    {
        errors.Add(exception);
    }
};          

var navigator = doc.CreateNavigator();
navigator.CheckValidity(schema, callback);          

foreach (var exception in errors)
{
    var node = (XObject)exception.SourceObject;

    // Do something with the node.
    Console.WriteLine();
    Console.WriteLine(exception);
    Console.WriteLine("{0}: {1}", node.GetType(), node.ToString());
    Assert.IsTrue(node != null, "node != null");
}

Однако эксперимент показывает, что XmlSchemaException.SourceSchemaObject всегда кажетсябыть нулевым с этим подходом, а также XElement.IXmlSerializable.GetSchema() не заполняется. Я не уверен, почему исходный объект схемы не передается, но тестирование в .NET Core 3.0.0 показывает, что это не так. (Возможно, это связано с , проблема # 38748: ошибки проверки XSD - отсутствуют сведения о коде ошибки схемы xsd , который был закрыт как не реализованный в настоящее время.)

Если вам нужен объект исходной схемывам также нужно будет следовать подходу из документации для Extensions.GetSchemaInfo() и проверить XDocument, используя XDocument.Validate(XDocument, XmlSchemaSet, ValidationEventHandler, Boolean addSchemaInfo). Это заполняет информацию о схеме в дереве LINQ to XML, но, к сожалению, не позволяет установить SourceObject. Вместо этого при обнаружении ошибок вам нужно будет пройти по иерархии XElement, ища элементы и атрибуты, для которых GetSchemaInfo() возвращает IXmlSchemaInfo, для которых Validity не Valid:

var errors = new List<XmlSchemaValidationException>();

ValidationEventHandler callback = (sender, args) =>
{
    var exception = (args.Exception as XmlSchemaValidationException);
    if (exception != null)
    {
        errors.Add(exception);
    }
};          

doc.Validate(schema, callback, true);           

foreach (var exception in errors)
{
    // Handle the exception itself.
    Console.WriteLine(exception);
}

if (errors.Count > 0)
{
    // If there were any errors, traverse the entire document looking for invalid nodes:
    DumpInvalidNodes(doc.Root);
}

Где пример метода DumpInvalidNodes изменен из документов Microsoft

//Taken from https://docs.microsoft.com/en-us/dotnet/api/system.xml.schema.extensions.getschemainfo?view=netframework-4.8#System_Xml_Schema_Extensions_GetSchemaInfo_System_Xml_Linq_XElement_
//with an added null check:
static void DumpInvalidNodes(XElement el)  
{  
    if (el.GetSchemaInfo().Validity != XmlSchemaValidity.Valid)  
        Console.WriteLine("Invalid Element {0}",  
            el.AncestorsAndSelf()  
            .InDocumentOrder()  
            .Aggregate("", (s, i) => s + "/" + i.Name.ToString()));  
    foreach (XAttribute att in el.Attributes())  
    {
        var si = att.GetSchemaInfo();

        // MUST CHECK FOR NULL HERE
        // Because w3 standard attributes like xmlns:xsi will have null SchemaInfo
        // when not included in the schema, rather than being reported as Invalid.
        if (si != null && si.Validity != XmlSchemaValidity.Valid)  
            Console.WriteLine("Invalid Attribute {0}",  
                att  
                .Parent  
                .AncestorsAndSelf()  
                .InDocumentOrder()  
                .Aggregate("",  
                    (s, i) => s + "/" + i.Name.ToString()) + "/@" + att.Name.ToString()  
                );  
    }
    foreach (XElement child in el.Elements())  
        DumpInvalidNodes(child);  
}

Обратите внимание, что мое тестирование показало, что код документации необходимо изменить, чтобы проверить, не вернул ли XAttribute.GetSchemaInfo() 1085 *. Похоже, это происходит для стандартных атрибутов w3c, таких как xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance", когда они явно не включены в схему.

Демо-скрипта # 2 здесь .

Обновление : похоже, doc.CreateNavigator().CheckValidity(schema, callback) не работает в более ранних версиях Full Framework;например, в .Net 4.7 выдается исключение System.NotSupportedException: This XPathNavigator does not support XSD validation. Демонстрационная скрипка № 3 здесь . Если вы столкнулись с этой проблемой, вам придется использовать второй подход.

...