参数化允许的数字范围或嵌入类型名称?
Parameterize allowed numeric range or embed in type name?
据我目前所见,我的问题可能无法在给定的限制条件下解决,但我会在这里给它一个机会。
我正在尝试编写一个 XSD(1.1) 文件来匹配一个非常简单的 XML。问题是 XML 标签中存在属性,这迫使我使用 xs:extension。元素示例:
<xs:element name="Index">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="int1TO10">
<xs:attribute name="comment" fixed="Attribute in XML Tag"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
而我创建的名为 "int1TO10" 的类型看起来像这样:
<xs:simpleType name="int1TO10">
<xs:restriction base="xs:integer">
<xs:minInclusive value="1"/>
<xs:maxInclusive value="10"/>
</xs:restriction>
</xs:simpleType>
这确实有效,问题是它需要大量编写,如果您想更改某些内容,则需要更改引用名称以保持一致以及定义类型中的值。如果这是一个小文件,这是可行的;但是这里有很多元素,它们几乎可以是最小整数和最大整数的任意组合。因此,对于每个新元素,必须有人检查一个类型是否已经存在,或者必须创建它。这一切都让人觉得非常多余。
我正在寻找的是一个简单的解决方案:
<xs:element>...
<xs:extension base="xs:integer" minInclusive="1" maxInclusive="10">
<xs:attribute name="comment" fixed="Attribute in XML Tag"/>
</xs:extension>...
</xs:element>
尽管据我发现,这似乎是不可能的。例如,在 this Whosebug post 中,接受的答案表明我目前正在做的事情是要走的路;虽然 post 已经有点老了,所以也许我错过了最近的一些突破?
我能想到的唯一其他选择是从(当前)
重组 XML 文件本身
<Index comment="Attribute in XML Tag">(some value between 1 and 10)</Index>
至
<Index>
<value>(some value between 1 and 10)</value>
<comment>Attribute now in its own tag</comment>
</Index>
这也需要相当多的工作和一些与 XML 一起工作的其他东西的改变(脚本等),所以我宁愿避免这种情况。
总结一下:有没有办法不为我的元素可以具有的每个新整数组合编写新的简单类型?欢迎提出任何建议,也许还有完全不同的方法或 XSD-结构可能吗?
我认为你所介绍的是要走的路。
但是,您可以...
为范围类型使用一致的命名方案,如 int_<lower_bound>_to_<upper_bound>
,并使用独立于 [=45= 的文本处理工具从模板自动生成匹配的 xsd 类型声明] 模式。
转换您提出的语法(<xs:extension base="xs:integer" minInclusive="1" maxInclusive="10">
使用 xslt。
PoC
生成 (perl)
写入包含 xml 片段的文件,该片段由系统生成的类型定义组成。
use IO::File;
use warnings;
use strict;
my ($dict, $fh_out, $fpath, $instance);
my $tmpl =<<EOTMPL;
<xs:simpleType name="int${lb}TO${ub}">
<xs:restriction base="xs:integer">
<xs:minInclusive value="${lb}"/>
<xs:maxInclusive value="${ub}"/>
</xs:restriction>
</xs:simpleType>
EOTMPL
my %dict = ( 'lb' => 0, 'ub' => 0 );
$fpath = "<wherever>";
$fh_out = new IO::File ( ">" . $fpath );
for ($dict{lb} = 1; $dict{lb} <= 4; $dict{lb}++ ) {
for ($dict{ub} = $dict{lb}+1; $dict{ub} <= 4; $dict{ub}++ ) {
($instance = $tmpl) =~ s/$\{([a-z]+)\}/$dict{}/g;
print $fh_out "$instance\n";
}
}
$fh_out->close;
exit(0);
转换 (xslt)
将源文件中的所有伪类型规范转换为完整的 int<m>TO<n>
类型声明。不检查重复定义。
<?xml-stylesheet version="2.0" encoding="utf-8"?>
<!--
t.xslt
XSD typespec generation
-->
<xsl:stylesheet xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<!--
Specifying the output
-->
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:apply-templates select="@*|node()"/>
</xsl:template>
<!--
Toplevel template:
-->
<xsl:template match="/">
<xsl:message select="string('main template: start;')"/>
<mytypes xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:apply-templates />
</mytypes>
<xsl:message select="string('main template: end;')"/>
</xsl:template>
<!--
Type subst
-->
<xsl:template match="xs:element/xs:complexType/xs:simpleContent/xs:extension[@base='xs:integer'][@minInclusive][@maxInclusive]">
<xs:simpleType>
<xsl:attribute name='name' select="concat('int', string(@minInclusive), 'TO', string(@maxInclusive))"/>
<xs:restriction base="xs:integer">
<xs:minInclusive><xsl:attribute name='value' select="number(@minInclusive)"/></xs:minInclusive>
<xs:maxInclusive><xsl:attribute name='value' select="number(@maxInclusive)"/></xs:maxInclusive>
</xs:restriction>
</xs:simpleType>
</xsl:template>
</xsl:stylesheet>
基于域的名称
第一个建议:使用对您的域有意义的名称,而不是基于允许范围的名称。例如,使用 Score
而不是 int1TO10
; Degrees
而不是 int1TO360
; LindenRathanNumber
而不是 int323TO9043
.
除了遵循使用基于域名的最佳实践之外,这种方法还有一个好处 属性 将范围视为实现细节。如果将来 LindenRathan Number 最大值从 9043 更改为 9044,它可以在定义中更改一次,而不是在名称本身出现的所有地方。
完全参数化的端点
备选建议:如果出于某种无法解释的原因,在您希望公开特定允许范围的地方创建标记很重要,您可以一路走下去,并为端点提供明确的 XML 属性:
<n min="0" max="10">9</n>
然后,使用 XSD 1.1 断言,您可以将元素值限制在给定端点的范围内:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
vc:minVersion="1.1">
<xs:element name="n" type="IntRangeType"/>
<xs:complexType name="IntRangeType">
<xs:simpleContent>
<xs:extension base="xs:int">
<xs:attribute name="min" type="xs:int"/>
<xs:attribute name="max" type="xs:int"/>
<xs:assert test="@max >= @min"/>
<xs:assert test=". >= @min and . <= @max"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:schema>
据我目前所见,我的问题可能无法在给定的限制条件下解决,但我会在这里给它一个机会。
我正在尝试编写一个 XSD(1.1) 文件来匹配一个非常简单的 XML。问题是 XML 标签中存在属性,这迫使我使用 xs:extension。元素示例:
<xs:element name="Index">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="int1TO10">
<xs:attribute name="comment" fixed="Attribute in XML Tag"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
而我创建的名为 "int1TO10" 的类型看起来像这样:
<xs:simpleType name="int1TO10">
<xs:restriction base="xs:integer">
<xs:minInclusive value="1"/>
<xs:maxInclusive value="10"/>
</xs:restriction>
</xs:simpleType>
这确实有效,问题是它需要大量编写,如果您想更改某些内容,则需要更改引用名称以保持一致以及定义类型中的值。如果这是一个小文件,这是可行的;但是这里有很多元素,它们几乎可以是最小整数和最大整数的任意组合。因此,对于每个新元素,必须有人检查一个类型是否已经存在,或者必须创建它。这一切都让人觉得非常多余。
我正在寻找的是一个简单的解决方案:
<xs:element>...
<xs:extension base="xs:integer" minInclusive="1" maxInclusive="10">
<xs:attribute name="comment" fixed="Attribute in XML Tag"/>
</xs:extension>...
</xs:element>
尽管据我发现,这似乎是不可能的。例如,在 this Whosebug post 中,接受的答案表明我目前正在做的事情是要走的路;虽然 post 已经有点老了,所以也许我错过了最近的一些突破?
我能想到的唯一其他选择是从(当前)
重组 XML 文件本身<Index comment="Attribute in XML Tag">(some value between 1 and 10)</Index>
至
<Index>
<value>(some value between 1 and 10)</value>
<comment>Attribute now in its own tag</comment>
</Index>
这也需要相当多的工作和一些与 XML 一起工作的其他东西的改变(脚本等),所以我宁愿避免这种情况。
总结一下:有没有办法不为我的元素可以具有的每个新整数组合编写新的简单类型?欢迎提出任何建议,也许还有完全不同的方法或 XSD-结构可能吗?
我认为你所介绍的是要走的路。
但是,您可以...
为范围类型使用一致的命名方案,如
int_<lower_bound>_to_<upper_bound>
,并使用独立于 [=45= 的文本处理工具从模板自动生成匹配的 xsd 类型声明] 模式。转换您提出的语法(
<xs:extension base="xs:integer" minInclusive="1" maxInclusive="10">
使用 xslt。
PoC
生成 (perl)
写入包含 xml 片段的文件,该片段由系统生成的类型定义组成。
use IO::File;
use warnings;
use strict;
my ($dict, $fh_out, $fpath, $instance);
my $tmpl =<<EOTMPL;
<xs:simpleType name="int${lb}TO${ub}">
<xs:restriction base="xs:integer">
<xs:minInclusive value="${lb}"/>
<xs:maxInclusive value="${ub}"/>
</xs:restriction>
</xs:simpleType>
EOTMPL
my %dict = ( 'lb' => 0, 'ub' => 0 );
$fpath = "<wherever>";
$fh_out = new IO::File ( ">" . $fpath );
for ($dict{lb} = 1; $dict{lb} <= 4; $dict{lb}++ ) {
for ($dict{ub} = $dict{lb}+1; $dict{ub} <= 4; $dict{ub}++ ) {
($instance = $tmpl) =~ s/$\{([a-z]+)\}/$dict{}/g;
print $fh_out "$instance\n";
}
}
$fh_out->close;
exit(0);
转换 (xslt)
将源文件中的所有伪类型规范转换为完整的 int<m>TO<n>
类型声明。不检查重复定义。
<?xml-stylesheet version="2.0" encoding="utf-8"?>
<!--
t.xslt
XSD typespec generation
-->
<xsl:stylesheet xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<!--
Specifying the output
-->
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:apply-templates select="@*|node()"/>
</xsl:template>
<!--
Toplevel template:
-->
<xsl:template match="/">
<xsl:message select="string('main template: start;')"/>
<mytypes xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:apply-templates />
</mytypes>
<xsl:message select="string('main template: end;')"/>
</xsl:template>
<!--
Type subst
-->
<xsl:template match="xs:element/xs:complexType/xs:simpleContent/xs:extension[@base='xs:integer'][@minInclusive][@maxInclusive]">
<xs:simpleType>
<xsl:attribute name='name' select="concat('int', string(@minInclusive), 'TO', string(@maxInclusive))"/>
<xs:restriction base="xs:integer">
<xs:minInclusive><xsl:attribute name='value' select="number(@minInclusive)"/></xs:minInclusive>
<xs:maxInclusive><xsl:attribute name='value' select="number(@maxInclusive)"/></xs:maxInclusive>
</xs:restriction>
</xs:simpleType>
</xsl:template>
</xsl:stylesheet>
基于域的名称
第一个建议:使用对您的域有意义的名称,而不是基于允许范围的名称。例如,使用 Score
而不是 int1TO10
; Degrees
而不是 int1TO360
; LindenRathanNumber
而不是 int323TO9043
.
除了遵循使用基于域名的最佳实践之外,这种方法还有一个好处 属性 将范围视为实现细节。如果将来 LindenRathan Number 最大值从 9043 更改为 9044,它可以在定义中更改一次,而不是在名称本身出现的所有地方。
完全参数化的端点
备选建议:如果出于某种无法解释的原因,在您希望公开特定允许范围的地方创建标记很重要,您可以一路走下去,并为端点提供明确的 XML 属性:
<n min="0" max="10">9</n>
然后,使用 XSD 1.1 断言,您可以将元素值限制在给定端点的范围内:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
vc:minVersion="1.1">
<xs:element name="n" type="IntRangeType"/>
<xs:complexType name="IntRangeType">
<xs:simpleContent>
<xs:extension base="xs:int">
<xs:attribute name="min" type="xs:int"/>
<xs:attribute name="max" type="xs:int"/>
<xs:assert test="@max >= @min"/>
<xs:assert test=". >= @min and . <= @max"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:schema>