6

我对使用 XML 模式相当陌生,所以如果这比我自己认为的更琐碎,请原谅我的无能。

我正在尝试创建一个必需的属性,该属性必须包含列表中的 1 个或多个空格分隔的字符串值。该列表是 4 种典型的 HTTP 请求方法;get, post,putdelete.

所以有效的元素包括:

<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 getorpost post post)和没有空格(例如getpostorpostputdelete


编辑

在玩了一会儿之后,我想出了一个主意:枚举所有可能的序列。谢天谢地,这个列表(暂时)固定在四种常用的运输方式上,getpostputdelete,所以我想:

<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>

任何人都可以看到这不是一个好主意的原因吗?

4

5 回答 5

12

基本问题也可以通过枚举来解决:

<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将被拒绝)。

于 2012-01-01T07:04:56.647 回答
3

您可以使用正则表达式作为对 simpleType 的限制: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>
于 2011-12-31T22:58:46.403 回答
3

在定期搞砸这个之后,我想出了这个庞大的模式;首先在 PCRE 漂亮打印中:

^
(
  (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这将成功匹配and的任何排列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));

它省略了最外层(),因为我认为没有必要。

于 2012-03-15T10:11:43.617 回答
1

你可以像这样处理空格:

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

它与 getpost 不匹配。

于 2012-01-01T12:47:25.353 回答
0

我需要与您想要的类似的东西,但我不希望强制执行任何命令,并且我不希望模式随着更多可能值的添加而呈指数增长。

以您的枚举为例,我想出的模式是这样的:

(?: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来确保属于其他选项的选项不会引起问题。一个例子是,如果您有 options foo, barand foobar,您不希望该选项foobar阻止fooandbar选项合法。

请记住,由于这将进入 XML,因此当您将其放入模式时,您必须将<其替换为。&lt;

此外,最后警告,并非所有正则表达式处理器都支持后向功能。

于 2015-03-27T10:35:13.520 回答