【问题标题】:XSL to remove duplicate records considering the tag and value考虑标签和值的 XSL 删除重复记录
【发布时间】:2021-04-08 18:03:28
【问题描述】:

考虑到标签/值,我想删除重复记录。

这是输入:

<Details>
    <block order="1" title="Circle A" id="Circle">
        <block id="square" title="Square 1" order="1">
            <block id="dot" title="test" order="1">
                <field order="1" title="A" id="ID a">500</field>
                <field order="2" title="B" id="ID b">Street xpto</field>
                <field order="3" title="C" id="ID c">Sun</field>
                <field order="4" title="D" id="ID d">45-34-YT</field>
                <field order="5" title="E" id="ID e">500</field>
                <field order="6" title="F" id="ID f">ABC</field>
                <field order="7" title="G" id="ID g">Street xpto</field>
                <field order="8" title="H" id="ID h">39RT</field>
                <field order="9" title="I" id="ID i">Working Closet</field>
            </block>
            <block id="dot" title="test" order="1">
                <field order="1" title="A" id="ID a">500</field>
                <field order="2" title="B" id="ID b">Street xpto</field>
                <field order="3" title="C" id="ID c">Sun</field>
                <field order="4" title="D" id="ID d">45-34-YT</field>
                <field order="5" title="E" id="ID e">500</field>
                <field order="6" title="F" id="ID f">ABC</field>
                <field order="7" title="G" id="ID g">Street xpto</field>
                <field order="8" title="H" id="ID h">39RT</field>
                <field order="9" title="I" id="ID i">Working Closet</field>
            </block>
            <block id="dot" title="test" order="1">
                <field order="1" title="A" id="ID a">500</field>
                <field order="2" title="B" id="ID b">Street xpto</field>
                <field order="3" title="C" id="ID c">Sun</field>
                <field order="4" title="D" id="ID d">45-34-YT</field>
                <field order="5" title="E" id="ID e">500</field>
                <field order="6" title="F" id="ID f">ABC</field>
                <field order="7" title="G" id="ID g">Street xpto</field>
                <field order="8" title="H" id="ID h">39RT</field>
                <field order="9" title="I" id="ID i">Working Closet</field>
            </block>            
        </block>
    </block>
</Details>

这是愿望输出:

<Details>
    <block order="1" title="Circle A" id="Circle">
        <block id="square" title="Square 1" order="1">
            <block id="dot" title="test" order="1">
                <field order="1" title="A" id="ID a">500</field>
                <field order="2" title="B" id="ID b">Street xpto</field>
                <field order="3" title="C" id="ID c">Sun</field>
                <field order="4" title="D" id="ID d">45-34-YT</field>
                <field order="5" title="E" id="ID e">500</field>
                <field order="6" title="F" id="ID f">ABC</field>
                <field order="7" title="G" id="ID g">Street xpto</field>
                <field order="8" title="H" id="ID h">39RT</field>
                <field order="9" title="I" id="ID i">Working Closet</field>
            </block>        
        </block>
    </block>
</Details>

我已经尝试过使用这个 xslt:

<?xml version="1.0" encoding="UTF-8"?>
<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:template match="@*|node()">
        <xsl:if test="not(preceding-sibling::node()[.=string(current())])">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

但正在删除我需要保留的这些行:

<field order="5" title="E" id="ID e">500</field>
<field order="7" title="G" id="ID g">Street xpto</field>

因为我的 xslt 只考虑标签值。 我不知道如何考虑所有行,而不仅仅是值。 有人可以帮忙吗?

谢谢。 何塞

更精确的样本。 示例 1: 输入:

<Details>
    <block order="1" title="Circle A" id="Circle">
        <block id="square" title="Square 1" order="1">
            <block id="dot" title="test" order="1">
                <field order="1" title="A" id="ID a">500</field>
                <field order="2" title="B" id="ID b">Street xpto</field>
                <field order="3" title="C" id="ID c">500</field>               
            </block>
            <block id="dot" title="test" order="1">
                <field order="1" title="A" id="ID a">500</field>
                <field order="2" title="B" id="ID b">Street xpto</field>
                <field order="3" title="C" id="ID c">500</field>
            </block>
            <block id="dot" title="test" order="1">
                <field order="1" title="A" id="ID a">500</field>
                <field order="2" title="B" id="ID b">Street xpto</field>
                <field order="3" title="C" id="ID c">500</field>
            </block>            
        </block>
    </block>
</Details>

输出:

<Details>
    <block order="1" title="Circle A" id="Circle">
        <block id="square" title="Square 1" order="1">
            <block id="dot" title="test" order="1">
                <field order="1" title="A" id="ID a">500</field>
                <field order="2" title="B" id="ID b">Street xpto</field>
                <field order="3" title="C" id="ID c">500</field>               
            </block>
        </block>
    </block>
</Details>

结果:一个不同的块 id“点”

示例 2: 第 3 个街区不同,因为第 2 场现在是“A 街” 输入:

<Details>
    <block order="1" title="Circle A" id="Circle">
        <block id="square" title="Square 1" order="1">
            <block id="dot" title="test" order="1">
                <field order="1" title="A" id="ID a">500</field>
                <field order="2" title="B" id="ID b">Street xpto</field>
                <field order="3" title="C" id="ID c">500</field>               
            </block>
            <block id="dot" title="test" order="1">
                <field order="1" title="A" id="ID a">500</field>
                <field order="2" title="B" id="ID b">Street xpto</field>
                <field order="3" title="C" id="ID c">500</field>
            </block>
            <block id="dot" title="test" order="1">
                <field order="1" title="A" id="ID a">500</field>
                <field order="2" title="B" id="ID b">Street A</field>
                <field order="3" title="C" id="ID c">500</field>
            </block>            
        </block>
    </block>
</Details>

输出:

<Details>
    <block order="1" title="Circle A" id="Circle">
        <block id="square" title="Square 1" order="1">
            <block id="dot" title="test" order="1">
                <field order="1" title="A" id="ID a">500</field>
                <field order="2" title="B" id="ID b">Street xpto</field>
                <field order="3" title="C" id="ID c">Sun</field>               
            </block>
            <block id="dot" title="test" order="1">
                <field order="1" title="A" id="ID a">500</field>
                <field order="2" title="B" id="ID b">Street A</field>
                <field order="3" title="C" id="ID c">Sun</field>               
            </block>        
        </block>
    </block>
</Details>

结果:两个不同的块 id "dot"

我希望这些新样本有助于澄清我所假装的内容。 提前感谢您的所有回复和回答。

【问题讨论】:

  • 块之间的字段是否可能以任何方式有所不同?如果是这样,你能举个例子吗?
  • 如果您仅限于 XSLT 1.0,则使用 Muenchian grouping 而不是您现在使用的低效方法。这也将允许您使用 key 来连接您要考虑的所有值(您的问题不清楚哪些值)。
  • @SiebeJongebloed,字段没有区别
  • @michael.hor257k,我希望始终拥有独特的&lt;block id="dot" 内容。在这种情况下,我有 3 个相同的块,输出应该是不同的,仅显示 1 个块。如果其中一个块中的某个值与其他块不同,则该块应出现在输出中。总之,我想使用“不同”功能来输出独特的块。我希望我说清楚了。
  • @JoseMartins,哪个 XSLT 处理器,您使用或可以使用哪个版本? unique contents 到底是什么意思? XPath 2 和更高版本的函数 deep-equal w3.org/TR/xpath-functions/#func-deep-equal 会表达这种要求吗?

标签: xslt duplicates


【解决方案1】:

在 XSLT 2 或 3 中,您的要求可能可以用 deep-equal 来表达(当然还有身份转换):

<xsl:template match="block/block/block[@id = 'dot'][some $b in preceding-sibling::block[@id = 'dot'] satisfies deep-equal(., $b)]"/>

另一方面,请注意该方法对于较大的数据表现不佳,一般建议是为节点使用或编写一些指纹函数,然后将block/block/block[@id = 'dot']节点按指纹值分组并取每个节点中的第一个节点分组以消除重复。

该已知输入结构的指纹可能是可计算的,如https://xsltfiddle.liberty-development.net/93nwMpk/1 所示:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="#all" version="3.0">

    <xsl:function name="mf:sort-attributes" as="attribute()*">
        <xsl:param name="input" as="attribute()*"/>
        <xsl:perform-sort select="$input">
            <xsl:sort select="namespace-uri()"/>
            <xsl:sort select="local-name()"/>
        </xsl:perform-sort>
    </xsl:function>

    <xsl:function name="mf:fingerprint" as="xs:string">
        <xsl:param name="element" as="element()"/>
        <xsl:sequence select="
                string-join(
                (for $att in mf:sort-attributes($element/@*)
                return
                    concat(node-name($att), '|', $att),
                for $child in $element/*
                return
                    string-join((for $att2 in mf:sort-attributes($child/@*)
                    return
                        concat(node-name($att2), '|', $att2), $child), '|')), '|')"/>
    </xsl:function>

    <xsl:mode on-no-match="shallow-copy"/>

    <xsl:key name="block" match="block[@id = 'dot']" use="mf:fingerprint(.)"/>

    <xsl:template match="block[@id = 'dot'][not(. is key('block', mf:fingerprint(.))[1])]"/>

</xsl:stylesheet>

我希望我在那里使用的唯一 XSLT 3 是 &lt;xsl:mode on-no-match="shallow-copy"/&gt; 应该被模板替换

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

对于 XSLT 1,我希望以下两个键的使用就足够了:

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

  <xsl:key name="block" match="block[@id = 'dot']" use="concat(@title, '|', @order)"/>
  
  <xsl:key name="field" match="block[@id = 'dot']/field" use="concat(../@title, '|', ../@order, '|', @order, '|', @title, '|', @id, '|', .)"/>

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template 
    match="block[@id = 'dot'][not(generate-id() = generate-id(key('block', concat(@title, '|', @order))[1]))][not(field[generate-id() = generate-id(key('field', concat(../@title, '|', ../@order, '|', @order, '|', @title, '|', @id, '|', .))[1])])]"/>

</xsl:stylesheet>

但我还没有彻底测试过,我不确定方法是否遗漏了某些方面,例如子元素的顺序。

【讨论】:

【解决方案2】:

这可能是您需要的吗? 它使用 block[field] 的 @id 来查看它是否是唯一具有该 @id 的 block[field]。

<?xml version="1.0" encoding="UTF-8"?>
<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:template match="@*|node()">
      <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
  </xsl:template>
  
  <xsl:template match="block[field]">
    <xsl:variable name="id" select="@id"/>
    <xsl:if test="not( preceding-sibling::block[@id=$id])">
      <xsl:copy-of select="."/>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

10-04-2021:在 OP 发布更多示例之后,我认为这应该可以完成工作:

<?xml version="1.0" encoding="UTF-8"?>
<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:template match="@*|node()">
      <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
  </xsl:template>
  
  <xsl:template match="block[block[field]]">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:apply-templates select="block[1]"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="block[field]">
    <xsl:variable name="fields" select="normalize-space(.)"/>
    <xsl:if test="not(following-sibling::block[normalize-space(.)=$fields])">
      <xsl:copy>
        <xsl:apply-templates select="@*"/>
        <xsl:copy-of select="field"/> 
      </xsl:copy>
    </xsl:if>
    <xsl:apply-templates select="following-sibling::block[1]"/>
  </xsl:template>

</xsl:stylesheet>

【讨论】:

  • 谢谢。快到了。但是这个解决方案不考虑字段标签值的变化,请检查我对这个问题提出的新样本。
  • @Jose Martins:请再次尝试更新 xslt 1.0 版。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-02-16
  • 1970-01-01
  • 1970-01-01
  • 2012-02-20
  • 1970-01-01
  • 1970-01-01
  • 2021-03-17
相关资源
最近更新 更多