参数化允许的数值范围或嵌入类型名称?

时间:2015-07-07 09:39:56

标签: xml xsd xsd-1.1

从我看到的到现在为止,我的问题可能无法在给定的约束条件下解决,但我会在这里给它一个机会。

我正在尝试编写一个与非常简单的XML匹配的XSD(1.1)文件。问题是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>

而我创建的类型称为&#34; int1TO10&#34;看起来像这样:

<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 Stackoverflow 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一起工作的东西(脚本等),所以我宁愿避免这种情况。

所以要把它包起来:有没有办法不为我的元素可以拥有的每个新的Integer组合写一个新的simpleType?欢迎任何建议,也许还有一种完全不同的方法或XSD结构可能吗?

2 个答案:

答案 0 :(得分:1)

基于域的名称

第一个建议:使用对您的域有意义的名称,而不是基于允许范围的名称。例如,使用Score而不是int1TO10; Degrees而不是int1TO360; LindenRathanNumber而不是int323TO9043

除了遵循使用基于域名的最佳实践之外,此方法还具有将范围视为实现细节的良好属性。如果将来LindenRathan数字的最大值从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 . &lt;= @max"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
</xs:schema>

答案 1 :(得分:0)

我认为你提出的是要走的路。

但是,你可以......

  • 对范围类型使用类似int_<lower_bound>_to_<upper_bound>的一致命名方案,并使用独立于xml架构的文本处理工具从模板自动生成匹配的xsd类型声明。

  • 使用xslt转换您提出的语法(<xs:extension base="xs:integer" minInclusive="1" maxInclusive="10">

<强>的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{$1}/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>