【问题标题】:Remove non-identical duplicate nodes (considering default values when tags aren't present)删除不相同的重复节点(在标签不存在时考虑默认值)
【发布时间】:2019-05-23 05:00:16
【问题描述】:

我对之前的帖子有一个后续问题: How to normalize XML on reverse domain name sorting and custom filtering

那里有数百个重复标签删除问题。我试图通过遍历它们来根据逻辑删除重复的节点,但它似乎不起作用:

<?xml version='1.0' encoding='UTF-8' ?>
<?tapia chrome-version='2.0' ?>
<mapGeo>
  <a>blah</a>
  <b>blah</b>
  <maps>
    <mapIndividual>
      <src>
        <scheme>https</scheme>
        <domain>photos.yahoo.com</domain>
        <path>somepath</path>
        <query>blah</query>
      </src>
      <loc>C:\var\tmp</loc>
      <x>blah</x>
      <y>blah</y>
    </mapIndividual>
    <mapIndividual>
      <src>
        <domain>photos.yahoo.com</domain>
        <path>somepath</path>
        <query>blah</query>
      </src>
      <loc>C:\var\tmp</loc>
      <x>blah</x>
      <y>blah</y>
    </mapIndividual>
    <mapIndividual>
      <src>
        <scheme>tcp</scheme>
        <domain>map.google.com</domain>
        <port>80</port>
        <path>/value</path>
        <query>blah</query>
      </src>
      <tgt>
        <scheme>https</scheme>
        <domain>map.google.com</domain>
        <port>443</port>
        <path>/value</path>
        <query>blah</query>
      </tgt>
      <loc>C:\var\tmp2</loc>
      <x>blah</x>
      <y>blah</y>
    </mapIndividual>
    <mapIndividual>
      <src>
        <scheme>tcp</scheme>
        <domain>map.google.com</domain>
        <path>/value</path>
        <query>blah</query>
      </src>
      <tgt>
        <domain>map.google.com</domain>
        <path>/value</path>
        <query>blah</query>
      </tgt>
      <loc>C:\var\tmp2</loc>
      <x>blah</x>
      <y>blah</y>
    </mapIndividual>
    <mapIndividual>
      <src>
        <scheme>http</scheme>
        <domain>*.c.b.a</domain>
        <path>somepath</path>
        <port>8085</port>
        <query>blah</query>
      </src>
      <tgt>
        <domain>r.q.p</domain>
        <path>somepath</path>
        <query>blah</query>
      </tgt>
      <x>blah</x>
    </mapIndividual>
    <mapIndividual>
      <src>
        <scheme>http</scheme>
        <domain>d.c.b.a</domain>
        <path>somepath</path>
        <port>8085</port>
        <query>blah</query>
      </src>
      <tgt>
        <domain>r.q.p</domain>
        <path>somepath</path>
        <query>blah</query>
      </tgt>
      <y>blah</y>
    </mapIndividual>
  <maps>
</mapGeo>

我尝试了多种方式,例如 XSLT 1.0、XSLT 2.0,但我知道我犯了一些错误,无法正常工作:

我尝试的方法:

<xsl:key name="kPropertyByName" match="domain" use="text()" />
...
<xsl:template match="domain[not(generate-id() = generate-id(key('kPropertyByName', text())[1]))]"/>
<xsl:key name="property" match="mapIndividual" use="concat(generate-id(parent::*), scheme, '|', domain, '|', port, '|', path, '|', query)" />
...
<xsl:apply-templates select="mapIndividual/src[generate-id(.) = generate-id(key('property', concat(generate-id(parent::*), scheme, '|', domain, '|', port, '|', path, '|', query))[1])]" />
<xsl:for-each-group select="mapIndividual" group-by="domain">
    <xsl:sequence select="."/>
</xsl:for-each-group>

我还有其他代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xdt="http://www.w3.org/2005/xpath-datatypes"
    version="2.0">

    <xsl:output method="xml" encoding="utf-8" indent="yes" />
    <xsl:strip-space elements="*" />

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

<!-- not working -->
<!--
    <xsl:key name="kPropertyByName" match="domain" use="text()" />
    <xsl:key name="property" match="src" use="concat(generate-id(parent::*), schema, '|', domain, '|', port, '|', path, '|', query)" />
-->

    <xsl:template match="maps">
        <xsl:copy>
            <xsl:apply-templates select="*">
                <xsl:sort select="src/domain" />
                <xsl:sort select="src/port" />
                <xsl:sort select="src/path" />
                <xsl:sort select="src/query" />
            </xsl:apply-templates>
        </xsl:copy>
        <!-- not working -->
        <!--
        <xsl:apply-templates select="mapIndividual/src[generate-id(.) = generate-id(key('property', concat(generate-id(parent::*), schema, '|', domain, '|', port, '|', path, '|', query))[1])]" />
        -->
    </xsl:template>

<!-- not working -->
<!--
    <xsl:template
    match="domain[
             not(
               generate-id() =
               generate-id(key('kPropertyByName', text())[1])
             )
           ]"/>
-->

    <xsl:template match="schema[text() = '' or text() = 'http' or text() = 'https']" />
    <xsl:template match="port[text() = '80' or text() = '443']" />

    <xsl:template match="*[not(@*|*|comment()|processing-instruction()) and normalize-space()='']" />

</xsl:stylesheet>

需要考虑以下项目:

  • 我有其他转换逻辑,我正在尝试同时添加重复删除,并且不想应用于第一个转换的输出。
  • 标签本质上是可选的,因此它可能存在也可能不存在。在上面的 XML 中,前 2 个 &lt;mapIndividual&gt; 节点是重复的,尽管 &lt;scheme&gt;https&lt;/scheme&gt; 只出现在 &lt;domain&gt;photos.yahoo.com&lt;/domain&gt; 的一个位置。同样,&lt;mapIndividual&gt;&lt;domain&gt;map.google.com&lt;/domain&gt; 是重复的,尽管 &lt;scheme&gt;https&lt;/scheme&gt;&lt;port&gt;443&lt;/port&gt; 可能存在也可能不存在。
  • 当标签不存在时,它应该考虑键/分组逻辑的默认值,比如&lt;scheme&gt;可以是空标签、空字符串、http或https,&lt;port&gt;标签可以是空标签、空字符串,80 或 443。

请帮助并提前感谢您!

【问题讨论】:

  • 我建议编写一个转换步骤来填充默认值,然后第二个消除重复项。您可以在单个样式表中使用多个转换步骤,例如使用模式。
  • @MartinHonnen 谢谢你的提示。我宁愿删除&lt;scheme&gt;&lt;port&gt; 等可能包含默认值的节点,而不是填写,这将减少要处理的有效负载。我之前看到过这个关于模式使用的帖子,但没有完全理解:stackoverflow.com/questions/10863491/…
  • 我看不出你想如何根据不存在的数据直接分组和/或排序

标签: xslt xslt-1.0 xslt-2.0 xslt-grouping


【解决方案1】:

这是一种通过第一种模式运行输入的方法,以根据您的问题描述的默认值剥离 schemeport,然后使用 XSLT 3 的复合分组来消除重复:

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

  <xsl:strip-space elements="*"/>
  <xsl:output indent="yes"/>

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

  <xsl:template match="scheme[normalize-space() = ('http', 'https', '')]" mode="default"/>

  <xsl:template match="port[normalize-space() = ('', 80, 443)]" mode="default"/>

  <xsl:variable name="defaults-stripped">
      <xsl:apply-templates mode="default"/>
  </xsl:variable>

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

  <xsl:template match="/">
      <xsl:apply-templates select="$defaults-stripped/node()"/>
  </xsl:template>

  <xsl:template match="maps">
      <xsl:copy>
          <xsl:for-each-group 
            select="mapIndividual" composite="yes"
            group-by="src ! (domain, string(scheme), string(path), string(port))">
              <xsl:sequence select="."/>
          </xsl:for-each-group>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/94rmq6E/1

这是一个如何处理它的示例,我不确定我是否已经掌握了关于剥离默认值或使用各种子元素或后代元素来识别重复项的确切要求。

【讨论】:

  • 感谢您的方法。是否可以改用 XSLT 2.0 解决方案?
  • 删除 xsl:mode 声明并在 XSLT 2 中拼出身份模板并删除 composite 属性并使用例如group-by="string-join(for $src in src return ($src/domain, string($src/scheme), string($src/path), string($src/port)), '|')".
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-11
  • 2012-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-13
相关资源
最近更新 更多