Подтвердить что ты не робот

Игнорирование предоставленных пространств имен при проверке XML с помощью XSD

Фон:

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

Проблема:

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

Вопросы:

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

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

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

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

Пример 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>
4b9b3361

Ответ 1

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

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

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

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

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

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", "неправильное пространство имен".

ссылки:

Чтение из memystream

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

Проверка против XSD, улавливание ошибок

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


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

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

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

Ссылка:

add-multiple-namespaces-to-the-root-element

Ответ 2

Весь смысл схемы XSD заключается в том, что он превращает нетипизированный XML в строго типизированный XML.

Тип XML может быть определен как комбинация node -name и пространства имен.

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

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

  • Он хорошо сформирован
  • Он подтверждает любое типизированное определение XML, указанное в атрибуте xmlns

Ответ 3

Я использую флаг 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));
}