Игнорирование предоставленных пространств имен при проверке XML с помощью XSD - PullRequest
12 голосов
/ 04 января 2012

Справочная информация:

Мы создаем приложение, которое позволяет нашим клиентам предоставлять данные в предопределенном (т.е. мы не контролируем) формате XML.XSD предоставляется нам третьей стороной, и мы ожидаем получить файл XML, который проходит проверку схемы до того, как мы его обработаем.

Проблема:

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

Вопросы:

Можно ли заставить .NET выполнить проверку и игнорировать пространство имен в предоставленном XML и XSD.т. е. каким-то образом «предположить», что пространство имен было присоединено.

  1. Можно ли легко и надежно удалить пространства имен в памяти?
  2. Какова наилучшая практика в этихситуации?

Решения, которые у меня есть до сих пор:

  1. Удаление пространства имен из XSD при каждом его обновлении (не должно быть очень часто.Это не обходит стороной тот факт, что если они предоставят пространство имен, оно все равно пройдет проверку.
  2. Удалите пространство имен из XSD и найдите способ убирать пространство имен из входящего XML каждый раз.как много кода для выполнения чего-то простого.
  3. Выполняет некоторую предварительную квалификацию файла XML перед его проверкой, чтобы убедиться, что он имеет правильное пространство имен. Кажется неправильным отказывать им из-за недопустимого пространства имен, если содержимоефайла верны.
  4. Создайте дубликат XSD, у которого нет пространства имен, однако, если они просто предоставляют неправильное пространство имен или другое пространство именe, тогда он все равно пройдет.

Пример Xml:

<?xml version="1.0"?>
<xsd:schema version='3.09' elementFormDefault='qualified' attributeFormDefault='unqualified' id='blah' targetNamespace='urn:schemas-blah.com:blahExample' xmlns='urn:blah:blahExample' xmlns:xsd='http://www.w3.org/2001/XMLSchema'>
...
</xsd:schema>

с пространством имен, которое отличается

 <?xml version="1.0" encoding="UTF-8" ?> 
<root xmlns="urn:myCompany.com:blahExample1" attr1="2001-03-03" attr2="google" >
...
</root>

безпространство имен вообще.

 <?xml version="1.0" encoding="UTF-8" ?> 
<root attr1="2001-03-03" attr2="google" >
...
</root>

Ответы [ 3 ]

6 голосов
/ 14 июня 2012

Попытка решить ту же проблему. Я пришел к тому, что я считаю довольно чистым решением. Для ясности я пропустил некоторую проверку входных параметров.

Во-первых, сценарий : существует веб-служба, которая получает файл, который должен быть "правильно сформированным" xml и действительным для XSD. Разумеется, мы не верим ни «хорошей форме», ни тому, что в отношении XSD справедливо, что «мы знаем» является правильным.

Код для такого метода веб-сервиса представлен ниже, я думаю, он не требует пояснений.

Основной интерес представляет порядок, в котором происходит проверка, вы не проверяете пространство имен перед загрузкой, вы проверяете после, но чисто.

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

private DataTable xmlErrors;
[WebMethod]
public string Upload(byte[] f, string fileName) {
    string ret = "This will have the response";

    // this is the namespace that we want to use
    string xmlNs = "http://mydomain.com/ns/upload.xsd";

    // you could put a public url of xsd instead of a local file
    string xsdFileName = Server.MapPath("~") + "//" +"shiporder.xsd"; 

    // a simple table to store the eventual errors 
    // (more advanced ways possibly exist)
    xmlErrors = new DataTable("XmlErrors");
    xmlErrors.Columns.Add("Type");
    xmlErrors.Columns.Add("Message");

    try {
        XmlDocument doc = new XmlDocument(); // create a document

        // bind the document, namespace and xsd
        doc.Schemas.Add(xmlNs, xsdFileName); 

        // if we wanted to validate if the XSD has itself XML errors
        // doc.Schemas.ValidationEventHandler += 
        // new ValidationEventHandler(Schemas_ValidationEventHandler);

        // Declare the handler that will run on each error found
        ValidationEventHandler xmlValidator = 
            new ValidationEventHandler(Xml_ValidationEventHandler);

        // load the document 
        // will trhow XML.Exception if document is not "well formed"
        doc.Load(new MemoryStream(f));

        // Check if the required namespace is present
        if (doc.DocumentElement.NamespaceURI == xmlNs) {

            // Validate against xsd 
            // will call Xml_ValidationEventHandler on each error found
            doc.Validate(xmlValidator);

            if (xmlErrors.Rows.Count == 0) {
                ret = "OK";
            } else {
                // return the complete error list, this is just to proove it works
                ret = "File has " + xmlErrors.Rows.Count + " xml errors ";
                ret += "when validated against our XSD.";
            }
        } else {
            ret = "The xml document has incorrect or no namespace.";                
        }
    } catch (XmlException ex) {
        ret = "XML Exception: probably xml not well formed... ";
        ret += "Message = " + ex.Message.ToString();
    } catch (Exception ex) {
        ret = "Exception: probably not XML related... "
        ret += "Message = " + ex.Message.ToString();
    }
    return ret;
}

private void Xml_ValidationEventHandler(object sender, ValidationEventArgs e) {
    xmlErrors.Rows.Add(new object[] { e.Severity, e.Message });
}

Теперь xsd будет выглядеть примерно так:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="shiporder"
    targetNamespace="http://mydomain.com/ns/upload.xsd"
    elementFormDefault="qualified"
    xmlns="http://mydomain.com/ns/upload.xsd"
    xmlns:mstns="http://mydomain.com/ns/upload.xsd"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
    <xs:simpleType name="stringtype">
      <xs:restriction base="xs:string"/>
    </xs:simpleType>
    ...
    </xs:schema>

А «хороший» XML будет выглядеть примерно так:

<?xml version="1.0" encoding="utf-8" ?>
<shiporder orderid="889923"  xmlns="http://mydomain.com/ns/upload.xsd">
  <orderperson>John Smith</orderperson>
  <shipto>
    <names>Ola Nordmann</names>
    <address>Langgt 23</address>

Я тестировал, «неверный формат XML», «неверный ввод в соответствии с XSD», «неверное пространство имен».

ссылка:

Чтение из памяти

Попытка избежать обработки исключительных ситуаций для проверки работоспособности

При проверке XSD выявляются ошибки

Интересный пост о проверке встроенной схемы


Привет Мартин , раздел комментариев слишком короткий для моего ответа, поэтому я дам его здесь, это может быть или не быть полным ответом, давайте улучшим его вместе:)

Я сделал следующие тесты:

  • Test: xmlns = "blaa"
  • Результат: файл отклонен из-за неправильного пространства имен.
  • Test: xmlns = "http://mydomain.com/ns/upload.xsd" и xmlns: a =" blaa ", а элементы имеют" a: someElement "
  • Результат: ошибка при возврате файла говорит о том, что он не ожидает "a: someElement"
  • Test: xmlns = "http://mydomain.com/ns/upload.xsd" и xmlns: a =" blaa ", а у элементов был элемент" someElement "с отсутствующим обязательным атрибутом
  • Результат: файл возвращает ошибку о том, что атрибут отсутствует

Следовала стратегия (которую я предпочитаю): если документ не соответствует требованиям, не принять, но предоставить некоторую информацию о причине (например, "." неправильное пространство имен ").

Эта стратегия, кажется, противоречит тому, что вы ранее сказали:

однако, если клиент пропустит объявление пространства имен в представленном им XML, я хотел бы сказать, что мы все еще можем его проверить. Я не хочу просто сказать: «Вы все испортили, теперь исправьте!»

В этом случае кажется, что вы можете просто игнорировать определенное пространство имен в XML. Для этого вы должны пропустить проверку правильности пространства имен:

    ...
    // Don't Check if the required namespace is present
    //if (doc.DocumentElement.NamespaceURI == xmlNs) {

        // Validate against xsd 
        // will call Xml_ValidationEventHandler on each error found
        doc.Validate(xmlValidator);

        if (xmlErrors.Rows.Count == 0) {
            ret = "OK - is valid against our XSD";
        } else {
            // return the complete error list, this is just to proove it works
            ret = "File has " + xmlErrors.Rows.Count + " xml errors ";
            ret += "when validated against our XSD.";
        }
    //} else {
    //    ret = "The xml document has incorrect or no namespace.";                
    //}
    ...


Другие идеи ...

В параллельном плане, чтобы заменить предоставленное пространство имен своим, возможно, вы могли бы установить doc.DocumentElement.NamespaceURI = "mySpecialNamespace", заменяя таким образом пространство имен корневого элемента.

Ссылка :

добавить многодисковых пространств имен-к-корневой элемент

0 голосов
/ 26 августа 2016

Я использую флаг XmlSchemaValidationFlags.ReportValidationWarnings.В противном случае xml с неизвестным пространством имен (или без пространства имен) автоматически пройдет проверку.

public static void Validate(string xml, string schemaPath)
{
    //oops: no ValidationFlag property, cant use linq
    //var d = XDocument.Parse(xml);
    //var sc = new XmlSchemaSet();
    //sc.Add(null, schemaPath);
    //sc.CompilationSettings.EnableUpaCheck = false;
    //d.Validate(sc, null);

    XmlReaderSettings Xsettings = new XmlReaderSettings();
    Xsettings.Schemas.Add(null, schemaPath);
    Xsettings.ValidationType = ValidationType.Schema;
    Xsettings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
    Xsettings.Schemas.CompilationSettings.EnableUpaCheck = false;
    Xsettings.ValidationEventHandler += new ValidationEventHandler(ValidationCallBack);

    XmlReader reader = XmlReader.Create(new StringReader(xml), Xsettings);
    while (reader.Read())
    {
    }
}

private static void ValidationCallBack(object sender, ValidationEventArgs e)
{
    if (e.Severity == XmlSeverityType.Warning)
        throw new Exception(string.Format("No validation occurred. {0}", e.Message));
    else
        throw new Exception(string.Format("Validation error: {0}", e.Message));
}
0 голосов
/ 04 января 2012

Смысл схемы XSD в том, что она превращает нетипизированный XML в строго типизированный XML.

Тип XML можно определить как комбинацию имени узла и пространства имен.

Если кто-то отправляет вам XML без пространства имен, то, несмотря на намерения, XML не ссылается на типы, определенные схемой XSD.

С точки зрения проверки XML, XML действителен до тех пор, пока

  1. Хорошо сформирован
  2. Это подтверждает любое типизированное определение XML, как указано атрибутом xmlns
...