跳转到内容

XML - 数据交换管理/多对多关系

来自维基教科书,开放世界中的开放书籍



上一章 下一章
一对一关系 递归关系



学习目标
  • 了解使用 XML 表示多对多关系的不同方法
  • 使用“消除”和“ID/IDREF”方法创建 XML 架构,以根据多对多关系构建内容
  • 为“消除”和“ID/IDREF”方法创建相应的 XML 文档
  • 了解如何在 XML 样式表中实现 key 功能,以格式化使用“ID/IDREF”方法建模的数据
  • 创建包含 key 功能的基本 XML 样式表

简介

[edit | edit source]

在前面的章节中,您学习了如何使用 XML 根据一对一和一对多关系来构建和格式化数据。由于 XML 提供了使用分层父子关系对数据建模的方法,因此一对一和一对多关系在 XML 中相对容易表示。然而,这种分层父子结构难以用于对多对多关系进行建模,多对多关系是许多情况下实体之间常见的关联关系。

在本章中,我们将探讨几种用于在 XML 中对多对多关系进行建模的方法的优缺点;这些方法在克服在将这种关系应用于 XML 时出现的难题方面做出了折衷。具体来说,我们将看到如何使用两种不同的方法“消除”和“ID/IDREF”对多对多关系进行建模的示例。此外,在 XML 样式表中,我们将学习如何实现 key 功能来显示使用“ID/IDREF”方法建模的数据。

问题:多对多关系

[edit | edit source]

在 XML 中,父子关系最常用于表示关联关系。这可以很容易地应用于一对一或一对多关系。XML 不直接支持多对多关系;父子关系将无法正常工作,因为每个元素只能有一个父元素。为了解决这个问题,有几种可能的解决方案。

解决方案:多对多关系

[edit | edit source]

消除

[edit | edit source]

创建消除多对多关系需求的 XML 文档
通过限制传递的信息范围,您可以避免多对多关系的需求。与其尝试让一个 XML 文档包含所有信息,不如将信息分开,其中一个文档仅描述参与多对多关系的实体之一。以我们的 tourGuide 关系为例,一种方法是为每个酒店创建单独的 XML 文档。与 amenity 的关系最终将变成一对多关系。此方法更适合将数据交换范围限制为数据集子集的情况。但是,在使用此方法进行范围更广的数据交换时,您可能会多次重复数据,尤其是如果存在许多属性时。为了避免这种冗余,请使用 ID/IDREF 方法。

ID/IDREF

[edit | edit source]

使用唯一标识符表示多对多关系
虽然这不是处理此问题的最友好的方法,但一种解决多对多关系的方法是创建键,这些键将唯一标识每个实体。为此,必须在 XML 架构中指定具有 ID 或 IDREF 属性类型的元素。要使用数据建模类比,ID 类似于主键,而 IDREF 类似于外键。

多对多关系数据模型

[edit | edit source]

图示 1:m:m 关系的数据模型
Data Model for a m:m relationship

此关系表示为“一家酒店可以拥有许多便利设施,而一个便利设施可以在许多酒店存在”。

如您所见,为了表示多对多关系,添加了两个实体。中间实体对于数据模型来说是必要的,因为它存储了关于酒店和便利设施之间关系的数据。使用我们的旅游指南示例,添加了“Amenity”来表示酒店可以拥有的可能便利设施列表。

以下示例说明了在 XML 中表示多对多关系的方法。

消除:示例解决方案

[edit | edit source]

在此示例中,多对多关系已转换为一对多关系。

XML 架构

[edit | edit source]

图示 2:“消除”方法的 XML 架构

<?xml version="1.0" encoding="UTF-8" ?>
<!--
     Document   : amenity1.xsd
     Created on : February 4, 2006
     Author     : Dr. Rick Watson
-->
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="unqualified">
    <xsd:element name="hotelGuide">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element name="hotel" type="hotelDetails" minOccurs="1" maxOccurs="unbounded"/>
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>
    <xsd:simpleType name="emailAddressType">
        <xsd:restriction base="xsd:string">
            <xsd:pattern value="\w+\W*\w*@{1}\w+\W*\w+.\w+.*\w*"/>
        </xsd:restriction>
    </xsd:simpleType>
    <xsd:complexType name="hotelDetails">
        <xsd:sequence>
            <xsd:element name="hotelPicture"/>
            <xsd:element name="hotelName" type="xsd:string"/>
            <xsd:element name="streetAddress" type="xsd:string"/>
            <xsd:element name="postalCode" type="xsd:string" minOccurs="0"/>
            <xsd:element name="telephoneNumber" type="xsd:string"/>
            <xsd:element name="emailAddress" type="emailAddressType" minOccurs="0"/>
            <xsd:element name="websiteURL" type="xsd:anyURI" minOccurs="0"/>
            <xsd:element name="hotelRating" type="xsd:integer" default="0"/>
            <xsd:element name="lowerPrice" type="xsd:positiveInteger"/>
            <xsd:element name="upperPrice" type="xsd:positiveInteger"/>
            <xsd:element name="amenity" type="amenityValue" minOccurs="0" maxOccurs="unbounded"/>
        </xsd:sequence>
    </xsd:complexType>
    <xsd:complexType name="amenityValue">
        <xsd:sequence>
            <xsd:element name="amenityType" type="xsd:string"/>
            <xsd:element name="amenityOpenHour" type="xsd:time"/>
            <xsd:element name="amenityCloseHour" type="xsd:time"/>
        </xsd:sequence>
    </xsd:complexType>
</xsd:schema>

XML 文档

[edit | edit source]

图示 3:“消除”方法的 XML 文档

<?xml version="1.0" encoding="UTF-8"?>
<!--
     Document   : amenity1.xml
     Created on : February 4, 2006
     Author     : Dr. Rick Watson
-->
<hotelGuide xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="amenity1.xsd">
    <hotel>
        <hotelPicture/>
        <hotelName>Narembeen Hotel</hotelName>
        <streetAddress>Churchill Street</streetAddress>
        <telephoneNumber>+61 (08) 9064 7272</telephoneNumber>
        <emailAddress>[email protected]</emailAddress>
        <hotelRating>1</hotelRating>
        <lowerPrice>50</lowerPrice>
        <upperPrice>100</upperPrice>
        <amenity>
            <amenityType>Restaurant</amenityType>
            <amenityOpenHour>06:00:00</amenityOpenHour>
            <amenityCloseHour>22:00:00 </amenityCloseHour>
        </amenity>
        <amenity>
            <amenityType>Pool</amenityType>
            <amenityOpenHour>06:00:00</amenityOpenHour>
            <amenityCloseHour>18:00:00 </amenityCloseHour>
        </amenity>
        <amenity>
            <amenityType>Complimentary Breakfast</amenityType>
            <amenityOpenHour>07:00:00</amenityOpenHour>
            <amenityCloseHour>10:00:00 </amenityCloseHour>
        </amenity>
    </hotel>
    <hotel>
        <hotelPicture/>
        <hotelName>Narembeen Caravan Park</hotelName>
        <streetAddress>Currall Street</streetAddress>
        <telephoneNumber>+61 (08) 9064 7308</telephoneNumber>
        <emailAddress>[email protected]</emailAddress>
        <hotelRating>1</hotelRating>
        <lowerPrice>20</lowerPrice>
        <upperPrice>30</upperPrice>
        <amenity>
            <amenityType>Pool</amenityType>
            <amenityOpenHour>10:00:00</amenityOpenHour>
            <amenityCloseHour>22:00:00 </amenityCloseHour>
        </amenity>
    </hotel>
</hotelGuide>

ID/IDREF:示例解决方案

[edit | edit source]

为了避免冗余,我们创建了一个单独的元素“amenity”,它与“hotel”一起包含在架构的顶部。请记住,数据类型 ID 和 IDREF 分别与主键和外键同义。对于每个外键(IDREF),都必须有一个匹配的主键(ID)。请注意,IDREF 数据类型必须是字母数字字符串。

以下示例说明了 ID/IDREF 方法。请注意,便利设施泳池的 ID 定义为“k1”,每个拥有泳池作为便利设施的酒店都使用 IDREF 引用“k1”。如果 IDREF 与任何 ID 不匹配,则文档将无法验证。

XML 架构

[edit | edit source]

图示 4:“ID/IDREF”方法的 XML 架构

<?xml version="1.0" encoding="UTF-8" ?>
<!--
     Document   : amenity2.xsd
     Created on : February 4, 2006
     Author     : Dr. Rick Watson
-->
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="unqualified">
    <xsd:element name="hotelGuide">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element name="hotel" type="hotelDetails" minOccurs="1" maxOccurs="unbounded"/>
                <xsd:element name="amenity" type="amenityList" minOccurs="1" maxOccurs="unbounded"/>
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>
    <xsd:simpleType name="emailAddressType">
        <xsd:restriction base="xsd:string">
            <xsd:pattern value="\w+\W*\w*@{1}\w+\W*\w+.\w+.*\w*"/>
        </xsd:restriction>
    </xsd:simpleType>
    <xsd:complexType name="hotelDetails">
        <xsd:sequence>
            <xsd:element name="hotelPicture"/>
            <xsd:element name="hotelName" type="xsd:string"/>
            <xsd:element name="streetAddress" type="xsd:string"/>
            <xsd:element name="postalCode" type="xsd:string" minOccurs="0"/>
            <xsd:element name="telephoneNumber" type="xsd:string"/>
            <xsd:element name="emailAddress" type="emailAddressType" minOccurs="0"/>
            <xsd:element name="websiteURL" type="xsd:anyURI" minOccurs="0"/>
            <xsd:element name="hotelRating" type="xsd:integer" default="0"/>
            <xsd:element name="lowerPrice" type="xsd:positiveInteger"/>
            <xsd:element name="upperPrice" type="xsd:positiveInteger"/>
            <xsd:element name="amenities" type="amenityDesc" minOccurs="0" maxOccurs="unbounded"/>
        </xsd:sequence>
    </xsd:complexType>
    <xsd:complexType name="amenityDesc">
        <xsd:sequence>
            <xsd:element name="amenityIDREF" type="xsd:IDREF"/>
            <xsd:element name="amenityOpenHour" type="xsd:time"/>
            <xsd:element name="amenityCloseHour" type="xsd:time"/>
        </xsd:sequence>
    </xsd:complexType>
    <xsd:complexType name="amenityList">
        <xsd:sequence>
            <xsd:element name="amenityID" type="xsd:ID"/>
            <xsd:element name="amenityType" type="xsd:string"/>
        </xsd:sequence>
    </xsd:complexType>
</xsd:schema>

XML 文档

[edit | edit source]

图示 5:“ID/IDREF”方法的 XML 文档

<?xml version="1.0" encoding="UTF-8"?>
<!--
     Document   : amenity2.xml
     Created on : February 4, 2006
     Author     : Dr. Rick Watson
-->
<?xml-stylesheet href="amenity2.xsl" type="text/xsl" media="screen"?>
<hotelGuide xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="amenity2.xsd">
    <hotel>
        <hotelPicture/>
        <hotelName>Narembeen Hotel</hotelName>
        <streetAddress>Churchill Street</streetAddress>
        <telephoneNumber>+61 (08) 9064 7272</telephoneNumber>
        <emailAddress>[email protected]</emailAddress>
        <hotelRating>1</hotelRating>
        <lowerPrice>50</lowerPrice>
        <upperPrice>100</upperPrice>
        <amenities>
            <amenityIDREF>k2</amenityIDREF>
            <amenityOpenHour>06:00:00</amenityOpenHour>
            <amenityCloseHour>22:00:00 </amenityCloseHour>
        </amenities>
        <amenities>
            <amenityIDREF>k1</amenityIDREF>
            <amenityOpenHour>06:00:00</amenityOpenHour>
            <amenityCloseHour>18:00:00 </amenityCloseHour>
        </amenities>
        <amenities>
            <amenityIDREF>k5</amenityIDREF>
            <amenityOpenHour>07:00:00</amenityOpenHour>
            <amenityCloseHour>10:00:00 </amenityCloseHour>
        </amenities>
    </hotel>
    <hotel>
        <hotelPicture/>
        <hotelName>Narembeen Caravan Park</hotelName>
        <streetAddress>Currall Street</streetAddress>
        <telephoneNumber>+61 (08) 9064 7308</telephoneNumber>
        <emailAddress>[email protected]</emailAddress>
        <hotelRating>1</hotelRating>
        <lowerPrice>20</lowerPrice>
        <upperPrice>30</upperPrice>
        <amenities>
            <amenityIDREF>k1</amenityIDREF>
            <amenityOpenHour>10:00:00</amenityOpenHour>
            <amenityCloseHour>22:00:00 </amenityCloseHour>
        </amenities>
    </hotel>
    <amenity>
        <amenityID>k1</amenityID>
        <amenityType>Pool</amenityType>
    </amenity>
    <amenity>
        <amenityID>k2</amenityID>
        <amenityType>Restaurant</amenityType>
    </amenity>
    <amenity>
        <amenityID>k3</amenityID>
        <amenityType>Fitness room</amenityType>
    </amenity>
    <amenity>
        <amenityID>k4</amenityID>
        <amenityType>Complimentary breakfast</amenityType>
    </amenity>
    <amenity>
        <amenityID>k5</amenityID>
        <amenityType>in-room data port</amenityType>
    </amenity>
    <amenity>
        <amenityID>k6</amenityID>
        <amenityType>Water slide</amenityType>
    </amenity>
</hotelGuide>

关键功能:XML 样式表

[编辑 | 编辑源代码]

为了使用 ID/IDREF 方法为多对多关系设置 XML 样式表,应该使用关键功能。在样式表中,<xsl:key> 元素指定索引,该索引用于从 XML 文档中返回节点集。

一个键包含以下部分:

1. 包含键的节点
2. 键的名称
3. 键的值

以下 XML 样式表说明了如何使用键功能来呈现以多对多关系结构化的内容。

XML 样式表

[编辑 | 编辑源代码]

图 6: 用于“ID/IDREF” 方法的 XML 样式表

<?xml version="1.0" encoding="UTF-8"?>
<!--
     Document   : amenity2.xsl
     Created on : February 4, 2006
     Author     : Dr. Rick Watson
-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="amList" match="amenity" use="amenityID"/>
    <xsl:output method="html"/>
    <xsl:template match="/">
        <html>
            <head>
                <title>Hotel Guide</title>
            </head>
            <body>
                <h2>Hotels</h2>
                <xsl:apply-templates select="hotelGuide"/>
            </body>
        </html>
    </xsl:template>
    <xsl:template match="hotelGuide">
        <xsl:for-each select="hotel">
            <xsl:value-of select="hotelName"/>
            <br/>
            <xsl:for-each select="amenities">
                <xsl:value-of select="key('amList',amenityIDREF)/amenityType"/>
                <xsl:text>   </xsl:text>
                <xsl:value-of select="amenityOpenHour"/> - 
                <xsl:value-of select="amenityCloseHour"/>
                <BR/>
            </xsl:for-each>
            <br/>
            <br/>
        </xsl:for-each>
        <br/>
    </xsl:template>
</xsl:stylesheet>

Expedia.de: XML 和联盟营销

[编辑 | 编辑源代码]

Expedia.de 是 Expedia.com 的德国子公司,Expedia.com 是一家总部位于美国华盛顿州贝尔维尤的网络旅游代理商。它通过其网站和电话为客户提供机票预订、租车、度假套餐以及各种其他景点和服务。其网站每月吸引超过 7000 万游客。目前,Expedia.com 拥有 4600 名员工,为美国、加拿大、英国、法国、德国、意大利和澳大利亚的客户提供服务。

出于营销目的,Expedia.de 建立了一个联盟营销计划。联盟营销是一种在不承担任何财务风险的情况下接触潜在客户的方式,这种方式针对的是想要做广告的公司(商家)。商家为网站所有者(称为联盟伙伴)提供了指向商家页面的推荐机会,并提供基于佣金的货币奖励作为激励。在 Expedia.de 的案例中,每当用户从其网站预订 Expedia.de 的行程时,联盟伙伴就会获得佣金。因此,联盟伙伴可以专注于销售,而商家则负责处理交易。

为了简化联盟伙伴的业务 - 当然也为了使该计划更具吸引力 - Expedia.de 为其伙伴提供了一项名为 xmlAdEd 的服务。xmlAdEd 是一项提供当前产品信息的基于 XML 的服务。使用该服务的联盟伙伴可以通过 HTTP 请求以 XML 格式获取超过 800 万种旅行产品的请求。数据每天更新多次。在 HTTP 请求中,您可以设置某些参数,例如地点、价格、机场代码等。

在这种情况下使用 XML 为联盟伙伴带来了许多优势:
- 由于结构、内容和样式的分离,可以高效灵活地处理数据。
- 平台独立的数据处理。
- 无损转换为其他文件格式。
- 易于集成到其网站中。
- 可以创建具有个性化设计的专属网络商店

通过为其联盟伙伴提供 XML 格式的产品信息,Expedia.de 不仅简化了其合作伙伴的业务,而且确保客户能够获得其服务的一致、最新的信息。

在 XML 中描述多对多关系时,设计师可以使用几种解决方案。在选择如何表示多对多关系时,设计师不仅要考虑以最有效的方式表示信息,还要考虑文档的目标受众以及文档的使用方式。

参考文献

[编辑 | 编辑源代码]

http://www-128.ibm.com/developerworks/xml/library/x-xdm2m.html

http://www.w3.org/TR/xslt#key

华夏公益教科书