XSL:删除具有重复子节点的父节点

时间:2017-05-16 07:18:38

标签: xml xslt xslt-1.0 xslt-2.0

我有一个关于根据子节点删除父节点的问题。

XML文件具有以下结构:

<PlmXmlData xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:plm="http://www.plmxml.org/Schemas/PLMXMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:an="">
  <ItemList>
    <Item>
      <ID>1</ID>
      <Group>Group1</Group>
      <Projekt>Projekt1</Projekt>
      <DatasetList>
        <Dataset>
          <Name>Name1</Name>
          <Type>TXT</Type>
          <Template>None</Template>
          <RelativeFilePath>FilePath1</RelativeFilePath>
          <PropertyList>
            <Property>
              <Title>item_name</Title>
              <Value>ITEM_Name</Value>
            </Property>
            <Property>
              <Title>item_name</Title>
              <Value>ITEM_Name</Value>
            </Property>
          </PropertyList>
        </Dataset>
        <Dataset>
          <Name>Name1</Name>
          <Type>PDF</Type>
          <Template>Template1</Template>
          <RelativeFilePath>FilePath1/Name1.pdf</RelativeFilePath>
          <PropertyList>
            <Property>
              <Title>item_name</Title>
              <Value>CAR1</Value>
            </Property>
            <Property>
              <Title>item_name</Title>
              <Value>CAR1</Value>
            </Property>
            <Property>
              <Title>item_name2</Title>
              <Value>CAR2</Value>
            </Property>
            <Property>
              <Title>item_name2</Title>
              <Value>CAR2</Value>
            </Property>
          </PropertyList>
        </Dataset>
      </DatasetList>
    </Item>
  </ItemList>
</PlmXmlData>

正如您所看到的,此示例TXT和PDF中有不同的<Type>个节点。 在此节点中,有节点<Property>和子节点<Title><Value>

我希望为每个<Property>中的每个重复条目删除整个<Title>节点及其子节点<Value><Type>

所需的输出应该是这样的:

<PlmXmlData xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:plm="http://www.plmxml.org/Schemas/PLMXMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:an="">
  <ItemList>
    <Item>
      <ID>1</ID>
      <Group>Group1</Group>
      <Projekt>Projekt1</Projekt>
      <DatasetList>
        <Dataset>
          <Name>Name1</Name>
          <Type>TXT</Type>
          <Template>None</Template>
          <RelativeFilePath>FilePath1</RelativeFilePath>
          <PropertyList>
            <Property>
              <Title>item_name</Title>
              <Value>ITEM_Name</Value>
            </Property>
          </PropertyList>
        </Dataset>
        <Dataset>
          <Name>Name1</Name>
          <Type>PDF</Type>
          <Template>Template1</Template>
          <RelativeFilePath>FilePath1/Name1.pdf</RelativeFilePath>
          <PropertyList>
            <Property>
              <Title>item_name</Title>
              <Value>CAR1</Value>
            </Property>
            <Property>
              <Title>item_name2</Title>
              <Value>CAR2</Value>
            </Property>
          </PropertyList>
        </Dataset>
      </DatasetList>
    </Item>
  </ItemList>

我搜索了论坛,但无法找到合适的解决方案。感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

这是XSLT 1.0中的解决方案。

<?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"/>

    <xsl:key name="property" match="Property" use="concat(generate-id(parent::*), Title, '|', Value)" />

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

    <xsl:template match="PropertyList">
        <xsl:copy>
            <xsl:apply-templates select="@*" />
            <xsl:apply-templates select="Property[generate-id(.) = generate-id(key('property', concat(generate-id(parent::*), Title, '|', Value))[1])]" />
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

它使用一种称为&#34; Muenchian分组&#34;的技术,它使用key函数在特定条件下查找XMl上的匹配,然后检查当前上下文是否与第一个相同匹配,忽略重复。等式检查使用generate-id函数;每个XML元素都有一个id属性,在运行时显式或隐式生成,唯一标识它。

在我们的例子中,密钥匹配<Property>个元素并对它们编制索引。因为我们要删除<PropertyList>内的重复项而不是整个XML中的重复项,所以密钥使用&#34;索引&#34;:父项<PropertyList>的(生成的)if的串联, <Title>元素值,|符号和<Value>元素值。这就是这一部分的作用:

<xsl:key name="property" match="Property" use="concat(generate-id(parent::*), Title, '|', Value)" />

匹配<PropertyList>的模板然后以递归方式应用模板,但仅适用于此谓词所在的<Property>元素:[generate-id(.) = generate-id(key('property', concat(generate-id(parent::*), Title, '|', Value))[1])]

让我们打破这种局面。 generate-id(.)为当前节点(Property元素)生成一个id。然后检查该id是否与为此生成的id相同:key('property', concat(generate-id(parent::*), Title, '|', Value))[1]

对于父级(我们的PropertyList),Title,|和Value&#34的生成id的串联的匹配,它说&#34;密钥命名属性;然后只获取匹配节点集的第一个元素。

请注意,你这样说:

  

如您所见,此示例TXT和PDF中有不同的节点。在这个节点内有节点和子节点。

我假设每种类型(TXT,PDF,...)在给定的<DatasetList>中只出现一次,并且您想删除PropertyList本地的重复项。如果类型可以重复并且您想要删除整个类型的重复项,则必须将Muenchian分组转到另一个级别(包括类型)。但我认为上述解决方案正是您的目标。

此外,如果Title和Value元素可能包含|个符号,则可能会失败。在这种情况下,您可以选择不同的分隔符。

如果你可以使用XSLT 2,这会变得更加简单,因为它内置了分组。在这种情况下,请在你的问题中指定,我可以做出新的答案。