Проверка XML с помощью XSD ... но все же допускает расширяемость - PullRequest
31 голосов
/ 28 июля 2010

Может быть, это я, но, похоже, что если у вас есть XSD

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="User">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="GivenName" />
                <xs:element name="SurName" />
            </xs:sequence>
            <xs:attribute name="ID" type="xs:unsignedByte" use="required" />
        </xs:complexType>
    </xs:element>
</xs:schema>

, который определяет схему для этого документа

<?xml version="1.0" encoding="utf-8" ?>
<User ID="1">
    <GivenName></GivenName>
    <SurName></SurName>
</User>

Не удалось бы проверить, если вы добавили еще одинэлемент, скажем EmailAddress, и перепутайте порядок

<?xml version="1.0" encoding="utf-8" ?>
<User ID="1">
    <SurName></SurName>
    <EmailAddress></EmailAddress>
    <GivenName></GivenName>
</User>

Я не хочу добавлять EmailAddress в документ и помечать его как необязательный.

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

Есть ли способ сделать это?

РЕДАКТИРОВАТЬ:

marc_s указано ниже, что вы можете использовать xs:any внутрииз xs:sequence, чтобы разрешить больше элементов, к сожалению, вы должны поддерживать порядок элементов.

В качестве альтернативы, я могу использовать xs:all, который не обеспечивает порядок элементов, но, увы, непозвольте мне поместить xs:any внутри него.

Ответы [ 5 ]

54 голосов
/ 05 августа 2010

У вашей проблемы есть решение, но оно не будет красивым. И вот почему:

Нарушение недетерминированных моделей контента

Вы затронули саму суть XML-схемы W3C. Что вы спрашиваете & mdash; переменный порядок и переменные неизвестные элементы & mdash; нарушает самый сложный, но самый основной принцип XSD, правило Non-неоднозначность , или, более формально, ограничение уникальной принадлежности частиц :

Модель контента должна быть сформирована таким что во время проверки [..] каждый элемент в последовательности может быть однозначно определяется без изучения содержание или атрибуты этого элемента, и без какой-либо информации о предметы в оставшейся части последовательность.

На обычном английском языке: когда XML проверяется и XSD-процессор обнаруживает <SurName>, он должен иметь возможность проверить его без предварительной проверки, следует ли за ним <GivenName>, т. Е. Без ожидания. В вашем сценарии это невозможно. Это правило существует, чтобы разрешить реализации через конечные автоматы, что должно сделать реализации довольно тривиальными и быстрыми.

Это одна из наиболее обсуждаемых проблем, которая является наследием SGML и DTD (модели контента должны быть детерминированными ) и XML, которые по умолчанию определяют порядок элементов важен (таким образом, попытка сделать обратное, сделать заказ неважным, труден).

Как уже предлагал Marc_s, Relax_NG является альтернативой, которая допускает недетерминированные модели контента. Но что вы можете сделать, если застряли в XML-схеме W3C?

Нерабочие полуправильные решения

Вы уже заметили, что xs:all очень ограничительно. Причина проста: применяется одно и то же недетерминированное правило, и поэтому xs:any, min/maxOccurs больше единицы, и последовательности не допускаются.

Кроме того, вы, возможно, пробовали всевозможные комбинации choice, sequence и any. Ошибка, которую выдает процессор Microsoft XSD при возникновении такой недопустимой ситуации:

Ошибка: множественное определение элемента «http://example.com/Chad:SurName' заставляет модель контента становиться неоднозначный. Модель контента должна быть сформирован так, что во время проверки последовательность элементов информации об элементе, частица содержала непосредственно, косвенно или косвенно который пытается проверить каждый элемент в последовательности в свою очередь может быть однозначно определяется без изучения содержание или атрибуты этого пункт, и без какой-либо информации о предметах в оставшейся части последовательность.

В XML-схеме О'Рейли (да, у книги есть свои недостатки), это прекрасно объяснено. К счастью, части книги доступны онлайн. Я настоятельно рекомендую вам прочитать раздел 7.4.1.3 об уникальном правиле атрибуции частиц , их объяснения и примеры гораздо яснее, чем я когда-либо смогу их получить.

Один рабочий раствор

В большинстве случаев возможно перейти от недетерминированного дизайна к детерминированному дизайну. Обычно это выглядит не очень красиво, но это решение, если вам нужно придерживаться XML-схемы W3C и / или если вам абсолютно необходимо разрешить нестрогие правила для вашего XML. Кошмар в вашей ситуации заключается в том, что вы хотите применить одну вещь (2 предопределенных элемента) и в то же время хотите, чтобы она была очень свободной (порядок не имеет значения и , все может быть между, до и после ). Если я не попытаюсь дать вам хороший совет, а просто приведу вас непосредственно к решению, оно будет выглядеть следующим образом:

<xs:element name="User">
    <xs:complexType>
        <xs:sequence>
            <xs:any minOccurs="0" processContents="lax" namespace="##other" />
            <xs:choice>
                <xs:sequence>                        
                    <xs:element name="GivenName" />
                    <xs:any minOccurs="0" processContents="lax" namespace="##other" />
                    <xs:element name="SurName" />
                </xs:sequence>
                <xs:sequence>
                    <xs:element name="SurName" />
                    <xs:any minOccurs="0" processContents="lax" namespace="##other" />
                    <xs:element name="GivenName" />
                </xs:sequence>
            </xs:choice>
            <xs:any minOccurs="0" processContents="lax" namespace="##any" />
        </xs:sequence>
        <xs:attribute name="ID" type="xs:unsignedByte" use="required" />
    </xs:complexType>
</xs:element>

Код выше на самом деле просто работает . Но есть несколько предостережений. Первый - xs:any с ##other в качестве пространства имен. Вы не можете использовать ##any, за исключением последнего, потому что это позволило бы использовать такие элементы, как GivenName, вместо этого, и это означает, что определение User становится неоднозначным.

Второе предостережение: если вы хотите использовать этот трюк с более чем двумя или тремя, вам придется записать все комбинации. Технический кошмар. Вот почему я придумываю следующее:

Предлагаемое решение, вариант контейнера с переменным содержимым

Измените свое определение. Это имеет преимущество в том, чтобы быть более понятным для ваших читателей или пользователей. Он также имеет преимущество в том, что его легче обслуживать. Целая строка решений объяснена на XFront здесь , менее читаемая ссылка, которую вы, возможно, уже видели в посте от Олега. Это отличное чтение, но в большинстве случаев не учитывается минимальное требование наличия двух элементов внутри контейнера переменного содержимого.

В настоящее время наилучшим подходом для вашей ситуации (который случается чаще, чем вы можете себе представить) является разделение ваших данных между обязательными и необязательными полями. Вы можете добавить элемент <Required> или сделать наоборот, добавить элемент <ExtendedInfo> (или назвать его Properties или OptionalData ). Это выглядит следующим образом:

<xs:element name="User2">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="GivenName" />
            <xs:element name="SurName" />
            <xs:element name="ExtendedInfo" minOccurs="0">
                <xs:complexType>
                    <xs:sequence>
                        <xs:any minOccurs="0" maxOccurs="unbounded" processContents="lax" namespace="##any" />
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
        </xs:sequence>
    </xs:complexType>
</xs:element>

В данный момент это может показаться не совсем идеальным, но пусть немного вырастет. Наличие упорядоченного набора фиксированных элементов не так уж сложно. Вы не единственный, кто будет жаловаться на очевидный недостаток XML-схемы W3C, но, как я уже говорил ранее, если вам придется использовать его, вам придется смириться с его ограничениями или принять бремя разработки вокруг этих ограничений при более высокой стоимости владения.

Альтернативное решение

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

Последнее слово

Какой бы подход вы ни выбрали, вы потеряете много проверяемых данных. Зачастую поставщикам контента разрешается добавлять типы контента, но только тогда, когда это можно проверить. Это можно сделать, переключившись с обработки lax на strict и сделав сами типы более строгими. Но быть слишком строгим также нехорошо, правильный баланс будет зависеть от вашей способности оценивать варианты использования, с которыми вы сталкиваетесь, и взвешивать их в сравнении с компромиссами определенных стратегий реализации.

6 голосов
/ 02 августа 2010

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

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

  1. http://www.xfront.com/ExtensibleContentModels.html (или http://www.xfront.com/ExtensibleContentModels.pdf) и http://www.xfront.com/VariableContentContainers.html
  2. http://www.xml.com/lpt/a/993 (или http://www.xml.com/pub/a/2002/07/03/schema_design.html)
  3. http://msdn.microsoft.com/en-us/library/ms950793.aspx
4 голосов
/ 28 июля 2010

Вы должны иметь возможность расширить схему с помощью элемента <xs:any> для расширения - подробности см. W3Schools .

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="User">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="GivenName" />
                <xs:element name="SurName" />
                <xs:any minOccurs="0" maxOccurs="unbounded" processContents="lax" />
            </xs:sequence>
            <xs:attribute name="ID" type="xs:unsignedByte" use="required" />
        </xs:complexType>
    </xs:element>
</xs:schema>

Когда вы добавляете processContents="lax", тогда.Проверка правильности NET XML на нем должна быть успешной.

Подробнее см. Документы MSDN по xs: любой .

Обновление: , если вам требуется больше гибкостии менее строгая проверка, возможно, вы захотите взглянуть на другие методы определения схем для вашего XML - что-то вроде RelaxNG .Схема XML - нарочно - довольно строгая в отношении своих правил, так что, возможно, это просто неправильный инструмент для этой работы под рукой.

1 голос
/ 12 августа 2013

RelaxNG решит эту проблему лаконично, если вы сможете ее использовать. Детерминизм не является обязательным требованием для схем. Вы можете перевести схему RNG или RNC в XSD, но в этом случае она будет приближенной. Это достаточно хорошо для вашего использования, зависит только от вас.

Схема RNC для этого случая:

start = User
User = element User {
   attribute ID { xsd:unsignedByte },
   ( element GivenName { text } &
     element SurName { text } &
     element * - (SurName | GivenName) { any })
}

any = element * { (attribute * { text } | text | any)* }

Правило any соответствует любому правильно сформированному фрагменту XML. Поэтому для этого потребуется, чтобы элемент User содержал элементы GivenName и SurName, содержащие текст в любом порядке, и разрешал использовать любые другие элементы, содержащие практически все.

1 голос
/ 04 августа 2010

Ну, вы всегда можете использовать DTD :-) за исключением того, что DTD также предписывает порядок. Проверка с помощью «неупорядоченной» грамматики ужасно дорога. Вы могли бы поиграть с xsd: choice, и мин и макс встречаются, но, вероятно, они тоже будут препятствовать. Вы также можете написать расширения XSD / производные схемы.

То, как вы поставили проблему, похоже, что вы вообще не хотите XSD. Вы можете просто загрузить его и затем проверить любой необходимый минимум с XPaths, но просто протестовать против XSD, сколько лет спустя после того, как он стал повсеместным стандартом, действительно, никуда не денется.

...