XSLT:如何仅基于值为节点生成唯一ID

时间:2013-08-10 19:11:10

标签: xslt xslt-1.0

我的源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(地址)时,我在联系人1和联系人3中获得了不同的地址ID。根据其值仅为节点生成唯一ID的其他方法是什么?

感谢您的帮助。

2 个答案:

答案 0 :(得分: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>

答案 1 :(得分: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>

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