简体   繁体   English

如何使用 XSLT 合并 XML 元素,然后构造相同名称/类型的元素/属性?

[英]How do I use XSLT to merge XML elements and then structure elements/attributes of the same name/type?

Main issue主要问题

I have an XML file which has multiple similar elements with slightly different information.我有一个 XML 文件,其中包含多个相似的元素,但信息略有不同。 So, lets say I have two elements with the same X attirbute, but different Y attributes.所以,假设我有两个 X 属性相同但 Y 属性不同的元素。 Here is an example:这是一个例子:

<FILE>
  <ELEMENT>
    <ATTRIBUTEX>1</ATTRIBUTEX>
    <ATTRIBUTEY>A</ATTRIBUTEY>
  </ELEMENT>
  <ELEMENT>
    <ATTRIBUTEX>1</ATTRIBUTEX>
    <ATTRIBUTEY>B</ATTRIBUTEY>
  </ELEMENT>
</FILE>

What I want to do is--based on the fact that both elements have the same X attribute--merge them into a single element with one attribute X and multiple child elements (of the same type)--each of which contain each of the different attributes Y. So, for example, I want my file to end up like this:我想要做的是——基于两个元素具有相同的 X 属性这一事实——将它们合并为具有一个属性 X 和多个子元素(相同类型)的单个元素——每个元素都包含不同的属性 Y。因此,例如,我希望我的文件最终如下所示:

<FILE>
  <ELEMENT>
    <ATTRIBUTEX>1</ATTRIBUTEX>
    <NEWELEMENT>
      <ATTRIBUTEY>A</ATTRIBUTEY>
    </NEWELEMENT>
    <NEWELEMENT>
      <ATTRIBUTEY>B</ATTRIBUTEY>
    </NEWELEMENT>
  </ELEMENT>
</FILE>

Possible solution?可能的解决方案?

One possible solution I can think of, but lack the knowledge to execute, is made up of two steps:我能想到的一种可能的解决方案,但缺乏执行知识,由两个步骤组成:

  1. Firstly, I could merge all elements which contain the X attribute so that the information is, at the least, in one place.首先,我可以合并所有包含 X 属性的元素,以便信息至少在一个地方。 I don't know quite how to do this.我不知道该怎么做。
  2. Secdonly, once this is done, this opens me up--in theory--to use my knowledge of XSLT to re-structure the element.其次,一旦完成,这让我打开了大门——理论上——使用我对 XSLT 的知识来重新构造元素。

Problem with the possible solution可能解决方案的问题

However, once all elements that containin the X attribute are merged together ( first step ), I will have a file in which there will be attributes with the same name (specifically, the Y attribute).但是,一旦包含 X 属性的所有元素都合并在一起(第一步),我将有一个文件,其中将有同名的属性(特别是 Y 属性)。 Here is an example:这是一个例子:

<FILE>
  <ELEMENT>
    <ATTRIBUTEX>1</ATTRIBUTEX>
    <ATTRIBUTEY>A</ATTRIBUTEY>
    <ATTRIBUTEY>B</ATTRIBUTEY>
  </ELEMENT>
</FILE>

What this means is that--at least with my knowledge--when I execute an XSLT file on the above XML ( second step ), it cannot distinguish between the two Y elements when sorting them into the two new child elements ( NEWELEMENT ).这意味着 - 至少据我所知 - 当我在上述 XML (第二步)上执行 XSLT 文件时,在将两个 Y 元素排序到两个新的子元素( NEWELEMENT )时,它无法区分它们。

So, let's say I execute the following XSLT on the above XML:所以,假设我在上面的 XML 上执行了下面的 XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="FILE">
  <Record>
    <xsl:for-each select = "ELEMENT">
      <ELEMENT>
        <xsl:copy-of select="ATTRIBUTEX"/>
      
        <NEWELEMENT>
          <xsl:copy-of select="ATTRIBUTEY"/>
        </NEWELEMENT>
        
        <NEWELEMENT>
          <xsl:copy-of select="ATTRIBUTEY"/>
        </NEWELEMENT>

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

The output is this: output 是这样的:

<Record>
    <ELEMENT>
        <ATTRIBUTEX>1</ATTRIBUTEX>
        <NEWELEMENT>
            <ATTRIBUTEY>A</ATTRIBUTEY>
            <ATTRIBUTEY>B</ATTRIBUTEY>
        </NEWELEMENT>
        <NEWELEMENT>
            <ATTRIBUTEY>A</ATTRIBUTEY>
            <ATTRIBUTEY>B</ATTRIBUTEY>
        </NEWELEMENT>
    </ELEMENT>
</Record>

As you can see, the XSLT has taken all and any ATTRIBUTEY elements and put them into each NEWELEMENT rather than distinguishing between them and placing one ATTRIBUTEY in the first NEWELEMENT , and the second ATTRIBUTEY in the second NEWELEMENT .如您所见, NEWELEMENT已将所有和任何ATTRIBUTEY元素放入每个新元素中,而不是区分它们并将一个ATTRIBUTEY放在第一个NEWELEMENT中,将第二个ATTRIBUTEY放在第二个NEWELEMENT中。 I am in need of an XSLT file which does just that, and, as stated further up, produces an XML that looks like this:我需要一个 XSLT 文件来执行此操作,并且如前所述,生成一个看起来像这样的 XML :

<FILE>
  <ELEMENT>
    <ATTRIBUTEX>1</ATTRIBUTEX>
    <NEWELEMENT>
      <ATTRIBUTEY>A</ATTRIBUTEY>
    </NEWELEMENT>
    <NEWELEMENT>
      <ATTRIBUTEY>B</ATTRIBUTEY>
    </NEWELEMENT>
  </ELEMENT>
</FILE>

Would anyone be able to help me with this?有人能帮我解决这个问题吗? Any solutions which generate the desired output (above) from the very first example file would be much appreciated.任何从第一个示例文件生成所需 output(上图)的解决方案都将不胜感激。 It's a bonus if that solution follows steps one and two.如果该解决方案遵循第一步和第二步,那将是一个奖励。 Thanks!谢谢!

I'm getting your requested output from the XSLT 3.0 below.我正在从下面的 XSLT 3.0 中获得您请求的 output。

Just a couple of notes beforehand:只是事先说明几点:

  1. You mention ATTRIBUTEs but, in fact, everything is an element in your XML document.您提到了属性,但实际上,一切都是您的 XML 文档中的一个元素。 There are no attributes in the XML sense. XML 意义上没有属性。
  2. I really don't see the need to create the additional subelements.. you can already have a list of elements of the same name.我真的不认为需要创建额外的子元素。你已经可以拥有一个同名元素的列表。

Nonetheless, as an exercise in style(sheets), here is a stylesheet that produces your output from your given input:尽管如此,作为样式(表)的练习,这里有一个样式表,它从您的给定输入生成您的 output:

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

  <xsl:mode on-no-match="shallow-copy"/>
  
  <xsl:output method="xml" indent="yes" />

  <xsl:template match="/FILE" >
    <FILE>
      <xsl:for-each-group select="*" group-by="name(.)" >
        <xsl:variable name="parentName" as="xs:string" select="current-grouping-key()" /> 
        
        <xsl:variable name="childNamesSameValues" as="xs:string*" >
          <xsl:for-each-group select="current-group()/*" group-by="name(.)" >
            <xsl:if test="count(distinct-values(current-group()/text())) eq 1">
              <xsl:sequence select="current-grouping-key()" />
            </xsl:if>
          </xsl:for-each-group>
        </xsl:variable>
            
        <xsl:element name="{$parentName}" >
          <xsl:for-each-group select="current-group()/*" group-by="name(.)" >
            <xsl:choose>
              <xsl:when test="current-grouping-key() = $childNamesSameValues">
                <xsl:copy-of select="current-group()[1]" />
              </xsl:when>
              <xsl:otherwise >
                <xsl:for-each select="current-group()" >
                  <xsl:element name="NEW{$parentName}" >
                    <xsl:copy-of select="." />
                  </xsl:element>
                </xsl:for-each>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:for-each-group>
        </xsl:element>
      </xsl:for-each-group>
    </FILE>
  </xsl:template>
  
</xsl:stylesheet>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM