基于查找 Table 中的值的 XSLT 属性在多个 XML 输入文件的 XSLT 中动态创建

XSLT attribute based on value in Lookup Table created dynamically in XSLT over multiple XML input files

我需要在查找 table 中获取由位置动态创建的值,并在遍历多个文件后将其分配给在同一转换中创建的元素。我有一个 "manifest" xml 文件,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="test.xsl"?>
<files>
    <file>file1.xml</file>
    <file>file2.xml</file>
</files>

文件1和文件2如下:

文件 1:

<?xml version="1.0" encoding="UTF-8"?>
<ProductList>
    <Product>
        <Name>Schwinn Bicycle</Name>
        <Type>Bicycle</Type>
        <Quantity>2</Quantity>
        <Store>
            <Name>Some Store</Name>
            <Location>Somewhere</Location>
            <Contact>Phone Number</Contact>
        </Store>
   </Product>
    <Product>
        <Name>Huffy Bicycle</Name>
        <Type>Bicycle</Type>
        <Quantity>10</Quantity>
        <Store>
            <Name>Some Other Store</Name>
            <Location>Somewhere Else</Location>
            <Contact>Another Phone Number</Contact>
        </Store>
   </Product>
</ProductList>

文件 2:

<?xml version="1.0" encoding="UTF-8"?>
<ProductList>
    <Product>
        <Name>Expensive Bicycle Brand</Name>
        <Type>Bicycle</Type>
        <Quantity>2</Quantity>
        <Store>
            <Name>Yet Another Store</Name>
            <Location>Whole New Place</Location>
            <Contact>Completely Different Phone Number</Contact>
        </Store>
   </Product>
    <Product>
        <Name>Harley Davidson</Name>
        <Type>Motorcycle</Type>
        <Quantity>1</Quantity>
        <Store>
            <Name>Some Other Store</Name>
            <Location>Somewhere Else</Location>
            <Contact>Another Phone Number</Contact>
        </Store>
   </Product>
</ProductList>

现在我需要的是文件 XML 输出如下:

<?xml version="1.0" encoding="UTF-8"?>
<ProductList>
    <Stores>
        <Store id="1" Name="Some Store">Phone Number</Store>
        <Store id="2" Name="Some Other Store">Another Phone Number</Store>
        <Store id="3" Name="Yet Another Store">Completely Different Phone Number</Store>
    </Stores>
    <Products>
        <Product>
            <Name>Huffy Bicycle</Name>
            <StoreContact contactId="2"/>
        </Product>
        <Product>
            <Name>Schwinn Bicycle</Name>
            <StoreContact contactId="1"/>
        </Product>
        <Product>
            <Name>Expensive Bicycle Brand</Name>
            <StoreContact contactId="3"/>
        </Product>
        <Product>
            <Name>Harly Davidson</Name>
            <StoreContact contactId="2"/>
        </Product>
    </Products>

所以,总而言之,我正在浏览文件并获取联系信息,并使用它们的位置在顶部进行查找 table 以获得结果 XML。然后,我为输出创建了产品元素,并且需要以某种方式获取在我创建查找 table 时分配的 ID。我需要使用纯 XSLT 1.0。

编辑:查找中的 Id table 必须是整数值。

这是我的:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" indent="yes" omit-xml-declaration="no" encoding="UTF-8"/>

    <xsl:variable name="input-docs" select="document(files/file)"/>
    <xsl:variable name="store_contacts" select="document(files/file)/ProductList/Product/Store/Contact"/>

    <xsl:template name="ProductList">
        <xsl:for-each select="$input-docs">
            <xsl:apply-templates select="Product"/>
        </xsl:for-each>
    </xsl:template>

    <xsl:template name="Contacts">
        <xsl:element name="contacts">
            <xsl:for-each select="$store_contacts">
                <xsl:if test="generate-id()=generate-id($store_contacts[normalize-space(Contact)=normalize-space(current()/Contact)][1])">
                    <xsl:element name="Store">
                        <xsl:attribute name="id"><xsl:value-of select="position()"/></xsl:attribute>
                        <xsl:value-of select="normalize-space(Contact)"/>
                    </xsl:element>
                </xsl:if>
            </xsl:for-each>
        </xsl:element>
    </xsl:template>

    <xsl:template match="/">
        <xsl:element name="ProductList">
            <xsl:element name="Stores">
                <xsl:call-template name="Contacts"/>
            </xsl:element>
            <xsl:element name="Products">
                <xsl:call-template name="ProductList"/>
            </xsl:element>
        </xsl:element>
    </xsl:template>

    <xsl:template match="Product">
        <xsl:element name="StoreContact">
            <xsl:attribute name="Name">
                <xsl:value-of select="Name"/>
            </xsl:attribute>
            <xsl:attribute name="contactId">1
                <!-- lookup value from the table created from all of the files, using the value 1 until I can figure out how to set the correct value -->
            </xsl:attribute>
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

如果这是一个重复的问题,我深表歉意,我试图找到类似的东西,但当操作员在遍历多个文件时尝试执行该功能时,没有发现任何关于该主题的问题。

我从你的 了解到你不能使用 node-set() 扩展功能 - 这使得这非常困难。

我建议您使用 generate-id() 函数生成的唯一 ID 来 link 产品到他们的商店。

以下样式表使用 the method proposed by G. Ken Holman 生成从多个文件收集的不同商店的列表。然后我们使用相同的原理来检索每个产品的商店的唯一id:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:variable name="input-docs" select="document(files/file)"/>
<xsl:variable name="stores" select="$input-docs/ProductList/Product/Store"/>

<xsl:template match="/">
    <ProductList>
        <Stores>
            <xsl:for-each select="$stores">
                <xsl:if test="generate-id()=generate-id($stores[Contact=current()/Contact][1])">
                    <Store id="{generate-id()}" Name="{Name}">
                        <xsl:value-of select="Contact"/>
                    </Store>
                </xsl:if>
            </xsl:for-each>     
        </Stores>
        <Products>
            <xsl:for-each select="$input-docs/ProductList/Product">
                <Product>
                    <xsl:copy-of select="Name"/>
                    <StoreContact contactId="{generate-id($stores[Contact=current()/Store/Contact][1])}"/>
                </Product>          
            </xsl:for-each>     
        </Products>
    </ProductList>      
</xsl:template>

</xsl:stylesheet>

实际结果可能因处理器而略有不同 - 以下是 Saxon 6.5 生成的示例:

<?xml version="1.0" encoding="UTF-8"?>
<ProductList>
   <Stores>
      <Store id="d1e14" Name="Some Store">Phone Number</Store>
      <Store id="d1e38" Name="Some Other Store">Another Phone Number</Store>
      <Store id="d2e14" Name="Yet Another Store">Completely Different Phone Number</Store>
   </Stores>
   <Products>
      <Product>
         <Name>Schwinn Bicycle</Name>
         <StoreContact contactId="d1e14"/>
      </Product>
      <Product>
         <Name>Huffy Bicycle</Name>
         <StoreContact contactId="d1e38"/>
      </Product>
      <Product>
         <Name>Expensive Bicycle Brand</Name>
         <StoreContact contactId="d2e14"/>
      </Product>
      <Product>
         <Name>Harley Davidson</Name>
         <StoreContact contactId="d1e38"/>
      </Product>
   </Products>
</ProductList>

这是一个完整的解决方案,可以生成有意义的整数 ID:

<xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:variable name="vAllDocs" select="document(/*/*)"/>

 <xsl:variable name="vAllStores" select="$vAllDocs/*/*/Store"/>

 <xsl:variable name="vPositionsOfDistinct">
   <xsl:for-each select="$vAllStores">
     <xsl:variable name="vCur" select="."/>
     <xsl:if test=
      "generate-id() 
     = generate-id($vAllStores[concat(Name,'|',Location,'|',Contact) 
                             = concat($vCur/Name,'|',$vCur/Location,'|',$vCur/Contact)]
                                [1])">
       <xsl:value-of select="concat('|', position())"/>
     </xsl:if>
   </xsl:for-each>
   <xsl:text>|</xsl:text>
 </xsl:variable>

 <xsl:variable name="vDistinctStores" select=
 "$vAllStores[contains($vPositionsOfDistinct, concat('|',position(),'|'))]"/>

  <xsl:template match="/">
    <ProductList>
      <Stores>
        <xsl:apply-templates select="$vAllStores" mode="distinct"/>
      </Stores>
      <Products><xsl:apply-templates select="$vAllDocs/*/Product"/></Products>
    </ProductList>
  </xsl:template>

  <xsl:template match="Store" mode="distinct">
    <xsl:if test="contains($vPositionsOfDistinct, concat('|',position(),'|'))">
      <Store id="{position()}" Name="{Name}"><xsl:value-of select="Contact"/></Store>
    </xsl:if>
  </xsl:template>

  <xsl:template match="Product">
     <xsl:variable name="vCur" select="Store"/>
     <xsl:variable name="vPos">
       <xsl:for-each select="$vAllStores">
             <xsl:if test=
                "contains($vPositionsOfDistinct, concat('|',position(),'|'))
               and
                 concat(Name,'|',Location,'|',Contact) 
               = concat($vCur/Name,'|', $vCur/Location,'|', $vCur/Contact)
                ">
               <xsl:value-of select="position()"/>
             </xsl:if>
       </xsl:for-each>
     </xsl:variable>

   <Product>
     <xsl:copy-of select="Name"/>
     <StoreContact contactId="{$vPos}"/>
   </Product>
  </xsl:template>
</xsl:stylesheet>

应用于所提供的 XML 文档时:

<files>
    <file>afile1.xml</file>
    <file>afile2.xml</file>
</files>

和提供的两个 XML 文件(两个产品重新排序以显示更有趣的情况,当生成的商店 ID 不是连续整数时):

afile1.xml:

<ProductList>
    <Product>
        <Name>Schwinn Bicycle</Name>
        <Type>Bicycle</Type>
        <Quantity>2</Quantity>
        <Store>
            <Name>Some Store</Name>
            <Location>Somewhere</Location>
            <Contact>Phone Number</Contact>
        </Store>
    </Product>
    <Product>
        <Name>Huffy Bicycle</Name>
        <Type>Bicycle</Type>
        <Quantity>10</Quantity>
        <Store>
            <Name>Some Other Store</Name>
            <Location>Somewhere Else</Location>
            <Contact>Another Phone Number</Contact>
        </Store>
    </Product>
</ProductList>

afile2.xml:

<ProductList>
    <Product>
        <Name>Expensive Bicycle Brand</Name>
        <Type>Bicycle</Type>
        <Quantity>2</Quantity>
        <Store>
            <Name>Some Other Store</Name>
            <Location>Somewhere Else</Location>
            <Contact>Another Phone Number</Contact>
        </Store>
    </Product>
    <Product>
        <Name>Harley Davidson</Name>
        <Type>Motorcycle</Type>
        <Quantity>1</Quantity>
        <Store>
            <Name>Yet Another Store</Name>
            <Location>Whole New Place</Location>
            <Contact>Completely Different Phone Number</Contact>
        </Store>
    </Product>
</ProductList>

然后产生了想要的正确结果:

<ProductList>
   <Stores>
      <Store id="1" Name="Some Store">Phone Number</Store>
      <Store id="2" Name="Some Other Store">Another Phone Number</Store>
      <Store id="4" Name="Yet Another Store">Completely Different Phone Number</Store>
   </Stores>
   <Products>
      <Product>
         <Name>Schwinn Bicycle</Name>
         <StoreContact contactId="1"/>
      </Product>
      <Product>
         <Name>Huffy Bicycle</Name>
         <StoreContact contactId="2"/>
      </Product>
      <Product>
         <Name>Expensive Bicycle Brand</Name>
         <StoreContact contactId="2"/>
      </Product>
      <Product>
         <Name>Harley Davidson</Name>
         <StoreContact contactId="4"/>
      </Product>
   </Products>
</ProductList>