3

我有一个源 XML,其中包含可能具有相同值的地址元素(请注意 Contact/id=1 和 Contact/id=3 具有相同的地址:

<?xml version="1.0" encoding="utf-8"?>
<Contacts xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <Contact>
        <id>1</id>
        <Address>
            <City City="Wien" />
            <Postcode Postcode="LSP-123" />
        </Address>
    </Contact>
    <Contact>
        <id>2</id>        
        <Address>
            <City City="Toronto" />
            <Postcode Postcode="LKT-947" />
        </Address>
    </Contact>
    <Contact>
        <id>3</id>        
        <Address>
            <City City="Wien" />
            <Postcode Postcode="LSP-123" />
        </Address>
    </Contact>
</Contacts> 

XSLT 1.0 所需的输出:

<?xml version="1.0" encoding="utf-8"?>
<Contacts xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <Contact>
        <id>1</id>
        <Address>SomeId_1</Address>
    </Contact>
    <Contact>
        <id>2</id>        
        <Address>SomeId_2</Address>
    </Contact>
    <Contact>
        <id>3</id>        
        <Address>SomeId_1</Address>
    </Contact>
</Contacts>

当我使用函数 generate-id(Address) 时,我在联系人 1 和联系人 3 中得到了不同的地址 ID。还有什么其他方法可以仅根据节点的值生成节点的唯一 ID?

感谢您的帮助。

4

2 回答 2

4

我建议将值的键构建为查找表,然后仅从查找表的第一个条目定位唯一编号:

t:\ftemp>type ivan.xml 
<?xml version="1.0" encoding="utf-8"?>
<Contacts xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <Contact>
        <id>1</id>
        <Address>
            <City City="Wien" />
            <Postcode Postcode="LSP-123" />
        </Address>
    </Contact>
    <Contact>
        <id>2</id>        
        <Address>
            <City City="Toronto" />
            <Postcode Postcode="LKT-947" />
        </Address>
    </Contact>
    <Contact>
        <id>3</id>        
        <Address>
            <City City="Wien" />
            <Postcode Postcode="LSP-123" />
        </Address>
    </Contact>
</Contacts> 
t:\ftemp>call xslt ivan.xml ivan.xsl 
<?xml version="1.0" encoding="utf-8"?><Contacts xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <Contact>
        <id>1</id>
        <Address>SomeId_1</Address>
    </Contact>
    <Contact>
        <id>2</id>        
        <Address>SomeId_2</Address>
    </Contact>
    <Contact>
        <id>3</id>        
        <Address>SomeId_1</Address>
    </Contact>
</Contacts>
t:\ftemp>type ivan.xsl 
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">

<xsl:key name="city-pc-pair" match="Address"
         use="concat(City/@City,'&#xd;',Postcode/@PostCode)"/>

<xsl:template match="Address">
  <xsl:for-each select="key('city-pc-pair',
                            concat(City/@City,'&#xd;',Postcode/@PostCode))[1]">
    <Address>SomeId_<xsl:number level="any"/></Address>
  </xsl:for-each>
</xsl:template>

<xsl:template match="@*|node()"><!--identity for all other nodes-->
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>
t:\ftemp>rem Done! 

至于我正在使用的连接,我告诉我的学生使用回车作为字段分隔符的技术可以将意外值冲突的可能性降低到无限小的大小,因为 XML 内容中的硬回车很少(那些作为行尾序列的一部分的回车被规范化为换行,因此不会出现在数据中)。

编辑添加了以下可能改进维护的实体技术,因为它将查找表达式集中在样式表中的单个声明中,以免在样式表的两个不同部分中意外地以不同方式编写:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet
[
<!ENTITY lookup "concat(City/@City,'&#xd;',Postcode/@PostCode)">
]>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">

<xsl:key name="city-pc-pair" match="Address" use="&lookup;"/>

<xsl:template match="Address">
  <xsl:for-each select="key('city-pc-pair',&lookup;)[1]">
    <Address>SomeId_<xsl:number level="any"/></Address>
  </xsl:for-each>
</xsl:template>

<xsl:template match="@*|node()"><!--identity for all other nodes-->
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>
于 2013-08-10T19:39:33.027 回答
1

XSLT 的 generate-id() 用于生成@xml:id,它们通常是用于唯一标识文档中节点的属性。因此,每次调用 generate-id() 时,都应该得到一个唯一值。

您要生成的标识符只是数据,与 generate-id() 的作用无关。

如果您想要一个标识符,其值基于其他数据的值,那么您应该只从该数据生成它。将这些值连接在一起,例如:

<?xml version="1.0" encoding="UTF-8"?>

<xsl:template match="*|@*">
    <xsl:copy>
        <xsl:apply-templates />
    </xsl:copy>
</xsl:template>

<xsl:template match="Address">
    <Address>
        <xsl:value-of select="concat(City/@City, '+', Postcode/@Postcode)"/>
    </Address>
</xsl:template>

将产生:

    <?xml version="1.0" encoding="UTF-8"?>
<Contacts xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <Contact>
        <id>1</id>
        <Address>Wien+LSP-123</Address>
    </Contact>
    <Contact>
        <id>2</id>        
        <Address>Toronto+LKT-947</Address>
    </Contact>
    <Contact>
        <id>3</id>        
        <Address>Wien+LSP-123</Address>
    </Contact>
</Contacts>

如果您对标识符有其他要求,那么您可以编写一个函数或使用查找表将这些键映射到其他一些标识符。

于 2013-08-10T19:52:57.953 回答