【问题标题】:XSLT: Ignore duplicates similar valuesXSLT:忽略重复的相似值
【发布时间】:2017-07-06 17:19:02
【问题描述】:

我正在研究 XSLT,它具有使用 (MySegment/*[not(.=preceding::*)]). 忽略重复项的逻辑

输入:

<MySegment>
  <Field1>ABCD</Field1>
  <FIeld2>1</Field2>
</MySegment>
<MySegment>
  <Field1>ABCD123</Field1>
  <FIeld2>1</Field2>
</MySegment>

这里我们有两个不同的值,但是由于 ABCD123 也包含 ABCD,因此它被视为重复条目。谁能给点建议。

这是我的 XSLT 的样子:

<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="/RecordsInp">

    <xsl:for-each select="ParentSegment">

      <xsl:for-each select="./MySegment/*[not(.=preceding::*)]">

        <A>
          <Field1><xsl:value-of select ="name(.)"/></Field1>
          <Field2><xsl:value-of select="."/></Field2>
        </A>

      </xsl:for-each>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

我的输入是:

<RecordsInp>
  <ParentSegment>
    <MySegment>
      <Field1>ABC</Field1>
      <Field2>A1</Field2>
    </MySegment>

    <MySegment>
      <Field1>ABC</Field1>
      <Field2>A1</Field2>
    </MySegment>

    <MySegment>
      <Field1>ABCDEF</Field1>
      <Field2>ABC</Field2>
    </MySegment>
  </ParentSegment>
</RecordsInp>

我得到的是:

 <?xml version="1.0" encoding="UTF-8"?>
    <A>
      <Field1>Field1</Field1>
      <Field2>ABC</Field2>
    </A><A>
      <Field1>Field2</Field1>
      <Field2>A1</Field2>
    </A><A>
      <Field1>Field1</Field1>
      <Field2>ABCDEF</Field2>
    </A>

请注意,我在最后一次出现时没有得到 Field2=ABC。由于 Field2 以前从未出现过 ABC,因此我需要以下 XML:

<?xml version="1.0" encoding="UTF-8"?>
<A>
  <Field1>Field1</Field1>
  <Field2>ABC</Field2>
</A><A>
  <Field1>Field2</Field1>
  <Field2>A1</Field2>
</A><A>
  <Field1>Field1</Field1>
  <Field2>ABCDEF</Field2>
</A>
<A>
  <Field1>Field2</Field1>
  <Field2>ABC</Field2>
</A>

【问题讨论】:

  • “因为 ABCD123 也包含 ABCD,所以它被视为重复项” - 不,不是您显示的 XPath。
  • 能否请您告知如何更正此问题。我的目标是遍历该段的所有字段并将它们填充在标题中而没有任何重复
  • 如果您没有清楚地解释您的代码应该做什么,并且至少提供一个展示错误行为的典型示例,我们无法就如何更正您的代码向您提供建议。连同示例输入以及预期和观察到的输出,我们称之为minimal reproducible example,这是我们希望您在寻求帮助修复代码的问题中提供的内容。
  • 嗨,我在这里详细添加了我的问题。请看一下

标签: xml xslt


【解决方案1】:

根据您的第二个输入示例,您的样式表未按您预期的方式拉动&lt;Field2&gt;ABC&lt;/Field2&gt; 的原因是因为ABC 作为一个值存在于前面的Field1 中。

也可以根据谓词中的当前 (current()) 名称测试名称,但也许您应该尝试使用 xsl:key (Muenchian grouping) 而不是 preceding::轴...

XML 输入

<RecordsInp>
    <ParentSegment>
        <MySegment>
            <Field1>ABC</Field1>
            <Field2>A1</Field2>
        </MySegment>
        <MySegment>
            <Field1>ABC</Field1>
            <Field2>A1</Field2>
        </MySegment>
        <MySegment>
            <Field1>ABCDEF</Field1>
            <Field2>ABC</Field2>
        </MySegment>
    </ParentSegment>
</RecordsInp>

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:key name="fields" match="MySegment/*" 
    use="concat(name(),'|',normalize-space())"/>

  <xsl:template match="MySegment">
    <xsl:for-each 
      select="*[count(.|key('fields',concat(name(),'|',normalize-space()))[1])=1]">
      <A>
        <Field1><xsl:value-of select="name()"/></Field1>
        <Field2><xsl:value-of select="."/></Field2>
      </A>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

输出

<A>
   <Field1>Field1</Field1>
   <Field2>ABC</Field2>
</A>
<A>
   <Field1>Field2</Field1>
   <Field2>A1</Field2>
</A>
<A>
   <Field1>Field1</Field1>
   <Field2>ABCDEF</Field2>
</A>
<A>
   <Field1>Field2</Field1>
   <Field2>ABC</Field2>
</A>

【讨论】:

  • 当然,将 Meunchian 分组应用于此类问题的诀窍是构造明确的键值。当您需要通过连接多个单独的值来形成一个键时,总是存在被数据中出现的所选分隔符弄乱的风险。显然,此样式表和 OP 的示例数据不会发生这种情况,但重要的是要记住这种可能性。
  • @JohnBollinger - 非常正确。还需要注意的是,您可以使用任何字符串来代替单个字符分隔符。例如,我可以使用 ~StackOverflowIsAwesome~ 而不是 |
【解决方案2】:

您的 XPath 表达式 ./MySegment/*[not(.=preceding::*)] 选择每个上下文节点的 &lt;MySegment&gt; 子元素的字符串值与文档顺序中它们前面的 any 元素的字符串值不匹配的那些子元素。因此,&lt;Field2&gt;ABC&lt;/Field2&gt; 不会反映在结果中,因为前面有 &lt;Field1&gt;ABC&lt;/Field1&gt; 元素。

显然,这比您预期的要广泛;据我了解,您只想与具有相同名称的前面元素进行比较。用纯 XPath 表达有点棘手;使用样式表重构会更容易处理。无偿使用xsl:for-each 无论如何都建议进行这样的重构。这是一个合理的方法:

<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:strip-space elements="*"/>

  <xsl:template match="/RecordsInp/ParentSegment/MySegment/*">
    <!-- bind the context node's name and value to variables: -->
    <xsl:variable name="fname" select="name()"/>
    <xsl:variable name="fvalue" select="string(.)"/>

    <!-- here's the test you seem really to want: -->
    <xsl:if test="not(preceding::*[name() = $fname and string() = $fvalue])">
      <A>
        <Field1><xsl:value-of select ="$fname"/></Field1>
        <Field2><xsl:value-of select="$fvalue"/></Field2>
      </A>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-24
    • 2013-10-22
    • 2020-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多