如何基于XSD架构中的父属性或位置来限制子元素的属性

时间:2014-04-25 01:43:01

标签: xml xsd xslt-1.0

尝试为此示例XML设计XSD架构。从外部API,所以无法改变它。我遇到的问题是如何限制行的属性。

<?xml version='1.0' encoding='UTF-8'?>
<eveapi version="2">
  <currentTime>2014-04-21 06:07:26</currentTime>
  <result>
    <characterID>123</characterID>
    <name>Me</name>
    <rowset name="skills" key="typeID" columns="typeID,skillpoints,level,published">
      <row typeID="3424" skillpoints="16000" level="3" published="1" />
      <row typeID="3318" skillpoints="2829" level="2" published="1" />
      <row typeID="3425" skillpoints="2829" level="2" published="1" />
      <row typeID="3451" skillpoints="500" level="1" published="1" />
    </rowset>
    <rowset name="certificates" key="certificateID" columns="certificateID">
        <row certificateID="1"/>
    </rowset>
    <rowset name="corporationRoles" key="roleID" columns="roleID,roleName">
      <row roleID="1" roleName="roleDirector" />
    </rowset>
    <rowset name="corporationRolesAtHQ" key="roleID" columns="roleID,roleName">
      <row roleID="1" roleName="roleDirector" />
    </rowset>
    <rowset name="corporationRolesAtBase" key="roleID" columns="roleID,roleName">
      <row roleID="1" roleName="roleDirector" />
    </rowset>
    <rowset name="corporationRolesAtOther" key="roleID" columns="roleID,roleName">
      <row roleID="1" roleName="roleDirector" />
    </rowset>
    <rowset name="corporationTitles" key="titleID" columns="titleID,titleName">
      <row titleID="1" titleName="Member" />
      <row titleID="2" titleName="Member Senior" />
      <row titleID="8192" titleName="Research Director" />
    </rowset>
  </result>
  <cachedUntil>2014-04-21 12:04:26</cachedUntil>
</eveapi>

我从基于样本的IDE生成中得到了这个。

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="eveapi" type="eveapiType"/>
  <xs:complexType name="rowType">
    <xs:simpleContent>
      <xs:extension base="xs:string">
        <xs:attribute type="xs:string" name="typeID" use="optional"/>
        <xs:attribute type="xs:string" name="skillpoints" use="optional"/>
        <xs:attribute type="xs:string" name="level" use="optional"/>
        <xs:attribute type="xs:string" name="published" use="optional"/>
        <xs:attribute type="xs:string" name="certificateID" use="optional"/>
        <xs:attribute type="xs:string" name="roleID" use="optional"/>
        <xs:attribute type="xs:string" name="roleName" use="optional"/>
        <xs:attribute type="xs:string" name="titleID" use="optional"/>
        <xs:attribute type="xs:string" name="titleName" use="optional"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
  <xs:complexType name="rowsetType">
    <xs:sequence>
      <xs:element type="rowType" name="row" maxOccurs="unbounded" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute type="xs:string" name="name" use="optional"/>
    <xs:attribute type="xs:string" name="key" use="optional"/>
    <xs:attribute type="xs:string" name="columns" use="optional"/>
  </xs:complexType>
  <xs:complexType name="eveapiType">
    <xs:sequence>
      <xs:element type="xs:string" name="currentTime"/>
      <xs:element type="resultType" name="result"/>
      <xs:element type="xs:string" name="cachedUntil"/>
    </xs:sequence>
    <xs:attribute type="xs:string" name="version"/>
  </xs:complexType>
  <xs:complexType name="resultType">
    <xs:sequence>
      <xs:element type="xs:string" name="characterID"/>
      <xs:element type="xs:string" name="name"/>
      <xs:element type="rowsetType" name="rowset" maxOccurs="unbounded" minOccurs="0"/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>

但它只是合并属性,因此<row>允许任何或所有属性,无论<rowset>

任何帮助都会很棒,因为我已经尝试了所有我能想到的东西,并且大多数都在XSD验证器中给出了关于使用抽象错误或在某处不确定的错误。

我仍然对仅使用XSD的解决方案感兴趣,但是为了防止其他人遇到类似的问题而发现这个问题,我将使用XSL样式表/转换和XSD来解决这个问题。

#两步解决方案#

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml"
        version="1.0"
        encoding="utf-8"
        omit-xml-declaration="no"
        standalone="no"
        indent="yes"/>
    <xsl:template match="rowset">
        <xsl:choose>
            <xsl:when test="@name">
                <xsl:element name="{@name}">
                    <xsl:copy-of select="@key"/>
                    <xsl:copy-of select="@columns"/>
                    <xsl:apply-templates/>
                </xsl:element>
            </xsl:when>
            <xsl:otherwise>
                <xsl:copy-of select="."/>
                <xsl:apply-templates/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

结果最终看起来像这样:

<?xml version='1.0' encoding='UTF-8'?>
<eveapi version="2">
  <currentTime>2014-04-21 06:07:26</currentTime>
  <result>
    <characterID>123</characterID>
    <name>Me</name>
    <skills key="typeID" columns="typeID,skillpoints,level,published">
      <row typeID="3424" skillpoints="16000" level="3" published="1" />
      <row typeID="3318" skillpoints="2829" level="2" published="1" />
      <row typeID="3425" skillpoints="2829" level="2" published="1" />
      <row typeID="3451" skillpoints="500" level="1" published="1" />
    </skills>
    <certificates key="certificateID" columns="certificateID">
        <row certificateID="1"/>
    </certificates>
    <corporationRoles key="roleID" columns="roleID,roleName">
      <row roleID="1" roleName="roleDirector" />
    </corporationRoles>
    <corporationRolesAtHQ key="roleID" columns="roleID,roleName">
      <row roleID="1" roleName="roleDirector" />
    </corporationRolesAtHQ>
    <corporationRolesAtBase key="roleID" columns="roleID,roleName">
      <row roleID="1" roleName="roleDirector" />
    </corporationRolesAtBase>
    <corporationRolesAtOther key="roleID" columns="roleID,roleName">
      <row roleID="1" roleName="roleDirector" />
    </corporationRolesAtOther>
    <corporationTitles key="titleID" columns="titleID,titleName">
      <row titleID="1" titleName="Member" />
      <row titleID="2" titleName="Member Senior" />
      <row titleID="8192" titleName="Research Director" />
    </corporationTitles>
  </result>
  <cachedUntil>2014-04-21 12:04:26</cachedUntil>
</eveapi>

快速解释它的作用是替换任何具有rowset属性且元素等于属性值的name元素,并将剩余的属性复制到新元素。它将与我需要的递归rowset一起使用,有些可能不包含子元素上的名称,因此在这种情况下只需使用常规元素副本就可以处理它。其余的是简单的标准树拷贝。现在XML已经转换,XSD很容易制作,在我的情况下,我的IDE甚至可以为我制作它们; - )

1 个答案:

答案 0 :(得分:0)

转换使其变得更加容易,并且由于您现在拥有不同的类型,因此可以使用XSD 1.0解决问题。

您可以生成XSD,但稍后手动编辑它是有用的,这样您就可以决定所有文件的实际规则是什么,因为该代可以生成可能过于通用的XSD或太具体了(因为它只使用一个文件作为来源)。

这是一个经过编辑的XSD,它将验证您发布的第二个XML:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
    <xs:element name="eveapi" type="eveapiType"/>
    <xs:complexType name="eveapiType">
        <xs:sequence>
            <xs:element type="xs:string" name="currentTime"/>
            <xs:element type="ResultType" name="result"/>
            <xs:element type="xs:string" name="cachedUntil"/>
        </xs:sequence>
        <xs:attribute type="xs:string" name="version"/>
    </xs:complexType>

    <xs:complexType name="ResultType">
        <xs:sequence>
            <xs:element type="xs:string" name="characterID"/>
            <xs:element type="xs:string" name="name"/>
            <xs:element type="SkillsType" name="skills"/>
            <xs:element type="CertificatesType" name="certificates"/>
            <xs:element type="CorporationRolesType" name="corporationRoles"/>
            <xs:element type="CorporationRolesType" name="corporationRolesAtHQ"/>
            <xs:element type="CorporationRolesType" name="corporationRolesAtBase"/>
            <xs:element type="CorporationRolesType" name="corporationRolesAtOther"/>
            <xs:element type="CorporationTitlesType" name="corporationTitles"/>
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="RowSetType">
        <xs:attribute name="key" type="xs:NMTOKEN"/>
        <xs:attribute name="columns" type="xs:string" />
    </xs:complexType>

    <xs:complexType name="SkillsType">
        <xs:complexContent>
            <xs:extension base="RowSetType">
                <xs:sequence>
                    <xs:element name="row" minOccurs="1" maxOccurs="unbounded">
                        <xs:complexType>
                            <xs:attribute name="typeID" type="xs:string"/>
                            <xs:attribute name="skillpoints" type="xs:integer"/>
                            <xs:attribute name="level" type="xs:integer"/>
                            <xs:attribute name="published" type="xs:integer"/>
                        </xs:complexType>
                    </xs:element>
                </xs:sequence>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

    <xs:complexType name="CertificatesType">
        <xs:complexContent>
            <xs:extension base="RowSetType">
                <xs:sequence>
                    <xs:element name="row" minOccurs="1" maxOccurs="unbounded">
                        <xs:complexType>
                            <xs:attribute name="certificateID" type="xs:string"/>
                        </xs:complexType>
                    </xs:element>
                </xs:sequence>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

    <xs:complexType name="CorporationRolesType">
        <xs:complexContent>
            <xs:extension base="RowSetType">
                <xs:sequence>
                    <xs:element name="row" minOccurs="1" maxOccurs="unbounded">
                        <xs:complexType>
                            <xs:attribute name="roleID" type="xs:string"/>
                            <xs:attribute name="roleName" type="xs:string"/>
                        </xs:complexType>
                    </xs:element>
                </xs:sequence>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

    <xs:complexType name="CorporationTitlesType">
        <xs:complexContent>
            <xs:extension base="RowSetType">
                <xs:sequence>
                    <xs:element name="row" minOccurs="1" maxOccurs="unbounded">
                        <xs:complexType>
                            <xs:attribute name="titleID" type="xs:string"/>
                            <xs:attribute name="titleName" type="xs:string"/>
                        </xs:complexType>
                    </xs:element>
                </xs:sequence>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

</xs:schema>

如果您正在设计此XSD,您可以改进许多内容。使用可以按上下文区分的idname等常用名称,而不是roleIDroleName,可以让您更多地重复使用类型。您还可以使用标准符号表示日期/时间,例如

<currentTime>2014-04-21T06:07:26</currentTime>

而不是

<currentTime>2014-04-21 06:07:26</currentTime>

然后您也可以使用xs:date作为该元素的类型。