1

我有想要转移到另一个结构中的 XML 文档。文档示例如下所示:

<application>
    <contactPerson>
        <name>Dominik</name>
        <countryCode>DE</countryCode>
    </contactPerson>
    <contactPerson>
        <name>Dorothea</name>
        <countryCode>DE</countryCode>
    </contactPerson>
    <contactPerson>
        <name>Fiona</name>
        <countryCode>FR</countryCode>
    </contactPerson>
    <contactPerson>
        <name>Fabian</name>
        <countryCode>FR</countryCode>
    </contactPerson>
    <contactPerson>
        <name>Florian</name>
        <countryCode>FR</countryCode>
    </contactPerson>
    <contactPerson>
        <name>Gabi</name>
        <countryCode>GB</countryCode>
    </contactPerson>
    <contactPerson>
        <name>Gert</name>
        <countryCode>GB</countryCode>
    </contactPerson>
</application>

现在我要做的是按国家/地区对元素进行分组,这意味着结果应该如下所示:

<application>
    <memberState>
        <countryCode>De</countryCode>
        <contactPerson>
            <name>Dominik</name>
        </contactPerson>
        <contactPerson>
            <name>Dorothea</name>
        </contactPerson>
    </memberState>
    <memberState>
        <countryCode>FR</countryCode>
        <contactPerson>
            <name>Fiona</name>
        </contactPerson>
        <contactPerson>
            <name>Fabian</name>
        </contactPerson>
        <contactPerson>
            <name>Florian<name>
        </contactPerson>
    </memberState>
    <memberState>
        <countryCode>GB</countryCode>
        <contactPerson>
            <name>Gabi</name>
        </contactPerson>
        <contactPerson>
            <name>Gert</name>
        </contactPerson>
    </memberState>
</application>

我正在使用 XPath for-each 模式来选择所有国家,但它并没有做它应该做的事情。我的模式如下所示:

  <xsl:template match="/">
    <application>
      <xsl:for-each select="/application/contactPerson/countryCode[not(.=preceding-sibling::*/application/contactPerson/countryCode)]">
        <memberState>
          <countryCode>
            <xsl:value-of select="."/>
          </countryCode>
          <contactPerson>
            <name>
              <xsl:value-of select="../name"/>
            </name>
          </contactPerson>
        </memberState>
      </xsl:for-each>
    </application>
  </xsl:template>

错误可能出现在无法编译的 XPath 表达式中。我将其更改为以下

<xsl:for-each select="/application/contactPerson/countryCode[not(.=preceding-sibling::*)]">

因为我认为我已经在我的树的正确位置。该解决方案可以编译,但它并没有按照我的意图通过使用“preceding-sibling”给我一个唯一的国家列表,而是完整的列表。

除了我需要解决我的问题之外,我会特别感谢一些帮助以了解这里实际发生的事情。

  1. 是否可以像我的第二个解决方案一样提供相对路径,还是每次都必须提供完整路径?
  2. 我是否在制作唯一的国家代码列表的正确轨道上,或者通常会以不同的方式实施?
  3. 我正在寻找的解决方案是否可以通过 XSLT 实现?

我非常感谢您的帮助。

4

3 回答 3

2

您可以使用key标签通过 countryCode 访问每个元素,如下所示:

<xsl:key name="groups" match="/application/contactPerson" use="countryCode" />

然后您可以迭代该键的内容。请参阅遍历/循环通过 XSL 键:如何?

因此,如果您坚持使用 XSLT 1.0,那将是一个很好的起点。如果您可以使用 XSLT 2.0,您应该看看group-by.

于 2013-01-09T12:15:05.643 回答
2

这是使用Muenchian 分组的 XSLT 1.0 解决方案:

<xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output indent="yes"/>

<xsl:key name="country" match="contactPerson" use="countryCode"/>

<xsl:template match="application">
  <xsl:copy>
    <xsl:apply-templates select="contactPerson[generate-id() = generate-id(key('country', countryCode)[1])]" mode="group"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="contactPerson" mode="group">
  <memberState>
    <xsl:copy-of select="countryCode"/>
    <xsl:apply-templates select="key('country', countryCode)"/>
  </memberState>
</xsl:template>

<xsl:template match="contactPerson">
  <xsl:copy>
    <xsl:copy-of select="*[not(self::countryCode)]"/>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>
于 2013-01-09T12:37:59.167 回答
1

您的 XPath 表达式几乎是正确的:

<xsl:for-each 
    select="/application/contactPerson/countryCode[not(.=preceding-sibling::*)]"> 

它不起作用的原因是 countryCode 没有任何兄弟姐妹。要查找以前的国家代码,您需要上移一级然后再下移:

<xsl:for-each 
    select="/application/contactPerson/countryCode[not(. = ../preceding-sibling::*/countryCode)]"> 

我相信应该成功地遍历不同的国家代码。

对模板的这种修改应该可以工作。您需要另一个循环来遍历每个国家/地区的所有人:

<xsl:template match="/">
    <application>
      <xsl:for-each select="/application/contactPerson/countryCode[not(.=../preceding-sibling::*/countryCode)]">
        <memberState>
          <countryCode>
            <xsl:value-of select="."/>
          </countryCode>
          <xsl:for-each select="/application/contactPerson[countryCode = current()]">
          <contactPerson>
            <name>
              <xsl:value-of select="name"/>
            </name>
          </contactPerson>
          </xsl:for-each>
        </memberState>
      </xsl:for-each>
    </application>
  </xsl:template>
于 2013-01-09T12:50:24.963 回答