XML-схема; кратный из списка допустимых значений атрибута - PullRequest
4 голосов
/ 31 декабря 2011

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

Я пытаюсь создать обязательный атрибут, который должен содержать 1 или более строковых значений, разделенных пробелами, из списка. Список представляет собой 4 типичных метода HTTP-запроса; get, post, put и delete.

Итак, допустимые элементы будут включать:

<rule methods="get" />
<rule methods="get post" />
<rule methods="post put delete" />

Принимая во внимание, что недопустимые элементы будут включать:

<rule methods="get get" />
<rule methods="foobar post" />
<rule methods="get;post;put" />

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


Вот где я сейчас, благодаря @tdrury:

<xs:attribute name="methods" use="required">
    <xs:simpleType>
        <xs:restriction base="xs:string">
            <xs:whiteSpace value="collapse" />
            <xs:pattern value="(?:(?:get|post|put|delete)\s?){1,4}" />
        </xs:restriction>
    </xs:simpleType>
</xs:attribute>

Что работает, за исключением повторения (, например get get или post post post) и отсутствующих пробелов (, например getpost или postputdelete)


Редактировать

Немного поиграв с этим, у меня возникла идея: перечисление всех возможных последовательностей. К счастью, этот список ( на данный момент ) привязан к четырем обычным транспортным методам, get, post, put и delete, поэтому я решил:

<xs:restriction base="xs:string">
    <xs:whiteSpace value="collapse" />
    <xs:enumeration value="delete" />
    <xs:enumeration value="put" />
    <xs:enumeration value="put delete" />
    <xs:enumeration value="post" />
    <xs:enumeration value="post delete" />
    <xs:enumeration value="post put" />
    <xs:enumeration value="post put delete" />
    <xs:enumeration value="get" />
    <xs:enumeration value="get delete" />
    <xs:enumeration value="get put" />
    <xs:enumeration value="get put delete" />
    <xs:enumeration value="get post" />
    <xs:enumeration value="get post delete" />
    <xs:enumeration value="get post put" />
    <xs:enumeration value="get post put delete" />
</xs:restriction>

Может кто-нибудь увидеть причину, по которой это не будет хорошей идеей?

Ответы [ 5 ]

10 голосов
/ 01 января 2012

Основная проблема может быть решена также с помощью перечислений:

<xs:attribute name="methods" use="required">
    <xs:simpleType>
        <xs:restriction>
            <xs:simpleType>
                <xs:list>
                    <xs:simpleType>
                        <xs:restriction base="xs:token">
                            <xs:enumeration value="get"/>
                            <xs:enumeration value="post"/>
                            <xs:enumeration value="put"/>
                            <xs:enumeration value="delete"/>
                        </xs:restriction>
                    </xs:simpleType>
                </xs:list>
            </xs:simpleType>
            <xs:minLength value="1"/>
        </xs:restriction>
    </xs:simpleType>
</xs:attribute>

Это, к сожалению, имеет то же ограничение, что и решение <xs:pattern>, и не может подтвердить, что каждый токен в списке уникален.Однако он решает проблему пробелов (getpost будет отклонено).

3 голосов
/ 15 марта 2012

После периодического шумихи с этим я придумал эту тусклую схему;первый в PCRE pretty-print:

^
(
  (get     (\s post)?    (\s put)?     (\s delete)?  (\s head)?    (\s options)?)
| (post    (\s put)?     (\s delete)?  (\s head)?    (\s options)?)
| (put     (\s delete)?  (\s head)?    (\s options)?)
| (delete  (\s head)?    (\s options)?)
| (head    (\s options)?)
| (options)
)
$

и совместимый с XML:

((get(\spost)?(\sput)?(\sdelete)?(\shead)?(\soptions)?)|(post(\sput)?(\sdelete)?(\shead)?(\soptions)?)|(put(\sdelete)?(\shead)?(\soptions)?)|(delete(\shead)?(\soptions)?)|(head(\soptions)?)|(options))

Это будет успешно соответствовать любой перестановке get post put delete head и options, дополнительно требуя, чтобы они были правильно упорядочены (, что тоже неплохо )

В любом случае, в итоге:

"get post put delete head options" // match

"get put delete options"           // match

"get get post put"                 // fail; double get

"get foo post put"                 // fail; invalid token, foo

"post delete"                      // match

"options get"                      // fail; ordering

Этот шаблон не 'Масштабирование наибольшее, так как каждый новый «токен» должен быть включен в каждую группу, но, поскольку проблемной областью являются методы HTTP, изменение непредвиденно, и я полагаю, что оно должно работать просто отлично.


ТакжеВот быстрый скрипт (PHP) для генерации шаблона:

$tokens = ['get', 'post', 'put', 'delete', 'head', 'options'];

echo implode('|', array_map(function ($token) use (&$tokens) {
    return sprintf('(%s%s)', array_shift($tokens),
        implode(null, array_map(function ($token) {
            return sprintf('(\s%s)?', $token);
        }, $tokens)));
}, $tokens));

Он пропускает самый внешний (), потому что я не думаю, что это необходимо.

3 голосов
/ 01 января 2012

Вы можете использовать регулярные выражения в качестве ограничения для простого типа: http://www.w3.org/TR/xmlschema-2/#dt-pattern

Я не эксперт по регулярным выражениям, но это будет примерно так:

<xs:attribute name="methods" use="required">
   <xs:simpleType>
      <xs:restriction base="xs:string">
         <xs:pattern value='((get|post|put|delete)[/s]*){4}'/>
      </xs:restriction>
   </xs:simpleType>
</xs:attribute>
1 голос
/ 01 января 2012

Вы можете иметь дело с такими пробелами, как это:

(get | post | put | delete) (\ sget | \ spost | \ sput | \ sdelete) {0,3}

Это не будет соответствовать getpost.

0 голосов
/ 27 марта 2015

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

Используя ваше перечисление в качестве примера, шаблон, который я придумал, выглядит следующим образом:

(?:get|post|put|delete|head|options)(?:\s(?:(?<!.*\bget\b.*)get|
(?<!.*\bpost\b.*)post|(?<!.*\bput\b.*)put|(?<!.*\bdelete\b.*)delete|
(?<!.*\bhead\b.*)head|(?<!.*\boptions\b.*)options))*

Эта часть

(?:[values])

просто требует, чтобы был выбран хотя бы один из вариантов. Если значение также не допускается, окружите все выражение следующим образом: (?:[...])?

Остаток

(?:\s(?:[values-with-restraints]))*

допускает комбинации ноль или больше пробелов плюс значение. Значения даны в этом формате

(?<!.*\b[value]\b.*)[value]

, который использует отрицательный взгляд (?<![...]), чтобы убедиться, что он не существует ранее в тексте. Я использую маркеры границы слова \b, чтобы убедиться, что опции, являющиеся частью других, не вызывают проблем. Например, если у вас есть опции foo, bar и foobar, вы не хотите, чтобы опция foobar не позволяла опциям foo и bar быть легальными.

Просто имейте в виду, что, поскольку речь идет о XML, вам придется заменить символ < на &lt;, когда вы добавите его в свою схему.

Кроме того, последнее предупреждение: не все процессоры регулярных выражений поддерживают функцию просмотра назад.

...