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



尝试为此示例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>

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

转换使它变得更容易,而且由于您现在有不同的类型,您可以使用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作为该元素的类型。

相关内容

  • 没有找到相关文章

最新更新