将XML节点合并为一个逗号分隔的节点

时间:2017-11-16 12:33:56

标签: xml xslt

我有一个XML结构,我想用XSLT转换它。但是,尽可能使其尽可能动态非常重要。我相信可以制作Feed的副本,然后只需选择一个特定的部分并对其进行转换。将新节点添加到原始XML时,在XSLT中不需要更改,以使此新节点包含在XSLT的输出中。

原始XML示例:

<?xml version="1.0" encoding="UTF-8"?>
<catalog week_id="629" generated_at="15.11.2017 23:53" version="2">
  <item>
    <lot>
      <id>2982641</id>
      <title_local><![CDATA[Title]]></title_local>
      <sub_title_local>Subtitle</sub_title_local>
      <promo>false</promo>
    </lot>
    <lot_specifics>
      <case_material>
        <name_local>Materiaal kast</name_local>
        <name_en>Case material</name_en>
        <slug>s-10-materiaal-kast</slug>
        <option>
          <value_local>Verguld</value_local>
          <value_en>Gold-plated</value_en>
          <slug_value>1750-verguld</slug_value>
        </option>
        <option>
          <value_local>Zilver</value_local>
          <value_en>Silver</value_en>
          <slug_value>1751-silver</slug_value>
        </option>
      </case_material>
    </lot_specifics>
    <associations>
      <category_id>1</category_id>
      <auction_id>2</auction_id>
    </associations>
  </item>
</catalog>

期望的输出:

<?xml version="1.0" encoding="UTF-8"?>
<catalog week_id="629" generated_at="15.11.2017 23:53" version="2">
  <item>
    <lot>
      <id>2982641</id>
      <title_local><![CDATA[Title]]></title_local>
      <sub_title_local>Subtitle</sub_title_local>
      <promo>false</promo>
    </lot>
    <lot_specifics>
      <case_material>
        <name_local>Materiaal kast</name_local>
        <name_en>Case material</name_en>
        <slug>s-10-materiaal-kast</slug>
        <value_local>Verguld,Silver</value_local>
        <value_en>Gold-plated,Silver</value_en>
      </case_material>
    </lot_specifics>
    <associations>
      <category_id>1</category_id>
      <auction_id>2</auction_id>
    </associations>
  </item>
</catalog>

我遇到了SO question that pretty much does what I'm aiming for,但我需要在此XSLT中定义完整的XML结构。由于我们可能会在以后的阶段添加其他XML节点,并且此XSLT将在各个地方使用,我希望尽可能降低维护。

我认为this SO question shows a low maintenance version但坦率地说,我无法理解第二个xsl:template是如何运作的。

非常感谢所有帮助。如果提供了XSLT,如果您可以包含一些您正在做什么的评论,我将非常感激。

2 个答案:

答案 0 :(得分:2)

由于您没有指定 XSLT 版本,我使用 2.0 , 这更强大,更适合这样的任务。

您的脚本应包含:

  • 身份模板
  • case_material元素的模板。

此模板应:

  • 复制option以外的所有元素。
  • 提供option元素的特殊处理。

此特殊处理应包括option的所有子元素的分组 按元素名称。对于每个这样的小组,你应该:

  • 使用源元素的名称创建一个元素(分组键)。
  • 打印出以逗号分隔的所有群组成员的内容。

我注意到你想&#34;排除&#34;某些元素(实际上只是 slug_value),因此循环包含排除它们的条件。

所以整个脚本如下所示:

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

  <xsl:template match="case_material">
    <xsl:copy>
      <xsl:apply-templates select="*[name() != 'option']|@*"/>
      <xsl:for-each-group select="option/*" group-by="name()">
        <xsl:if test="current-grouping-key() != 'slug_value'">
         <xsl:element name="{current-grouping-key()}">
           <xsl:value-of select="current-group()" separator=","/>
         </xsl:element>
        </xsl:if>
      </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="node()|@*">
    <xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy>
  </xsl:template>
</xsl:stylesheet>

可以在 XSLT 1.0 中编写这样的脚本,但是你会这样做 必须编写更多代码,因为版本1.0不支持例如分组。

答案 1 :(得分:1)

我会尝试复制除option元素之外的所有内容。迭代选项元素并对需要合并的项目进行分组。例如:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xsl:param name="pGroupElems" as="xs:string *" select="'value_local', 'value_en'" />

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

    <xsl:template match="case_material">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
            <xsl:for-each-group select="option/*" group-by="local-name()[ . = $pGroupElems]">
                <xsl:copy>
                    <xsl:sequence select="string-join(current-group(), ',')" />
                </xsl:copy>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="option" />

</xsl:stylesheet>