我正在尝试根据标记值将 XML 文件转换为另一个 XML 文件分组数据

I am trying to convert an XML file to another XML file grouping data according to a tag value

我想转换以下 XML 文件:

<?xml version="1.0" encoding="UTF-8"?>
<dataroot> 
  <Op>
    <Where>AIX</Where>
    <Amount>24</Amount>
  </Op>
  <Op>
    <Where>LILLE</Where>
    <Amount>10</Amount>
  </Op>
  <Op>
    <Where>LILLE</Where>
    <Amount>18</Amount>
  </Op>
  <Op>
    <Where>AIX</Where>
    <Amount>20</Amount>
  </Op>
  <Op>
    <Where>LILLE</Where>
    <Amount>12</Amount>
   </Op>
</dataroot>

<?xml version="1.0" encoding="UTF-8"?>
<Document> 
  <Recap>
   <Nop>5</Nop>
    <TotAmount>84</TotAmount>
  </Recap>
  <Location>
    <Where>AIX</Where>
    <Nop>2</Nop>
    <LocAmount>44</LocAmount>
    <Ops>
      <Amount>24</Amount>
      <Amount>20</Amount>
    </Ops>
  </Location>
  <Location> 
    <Where>LILLE</Where>
    <Nop>3</Nop>
    <LocAmount>40</LocAmount>
    <Ops>
       <Amount>10</Amount>
       <Amount>18</Amount>
       <Amount>12</Amount>
    </ops>
  </Location>
</Document>

这是将针对给定位置执行的操作分组在一起,计算它们的数量以及每个位置的操作总量。我尝试使用排序但没有成功。

我是新手,不知道该怎么做。

根据 michael.hor257k 的建议,我尝试使用以下 xsl 代码创建分组,但我仍然无法计算每个位置的操作计数 (Nop) 和 LocAmount:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

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

  <xsl:key name="Ops-by-location" match="Op" use="Where" />

  <xsl:template match="/">
    <Document> 
      <Recap>
        <Nop><xsl:value-of select="count(//Amount)"/></Nop>
        <TotAmount><xsl:value-of select="sum(//Amount)"/></TotAmount>
      </Recap>
      <xsl:apply-templates/> 
    </Document>  
  </xsl:template>

  <xsl:template match="/dataroot">
    <xsl:apply-templates select="Op[generate-id(.) = generate-id(key('Ops-by-location', Where)[1])]"/>
  </xsl:template>

  <xsl:template match="Op">
    <Where>
      <xsl:value-of select="Where" />
      <Nop>???</Nop>
      <TotAmount>???</TotAmount>
      <xsl:for-each select="key('Ops-by-location', Where)">
        <Amount> <xsl:value-of select="Amount" /></Amount>
      </xsl:for-each>
    </Where>
  </xsl:template>
</xsl:stylesheet>

试试这个

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string input =
               "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
               "<dataroot>" +
                 "<Op>" +
                   "<Where>AIX</Where>" +
                   "<Amount>24</Amount>" +
                 "</Op>" +
                 "<Op>" +
                   "<Where>LILLE</Where>" +
                   "<Amount>10</Amount>" +
                 "</Op>" +
                 "<Op>" +
                   "<Where>LILLE</Where>" +
                   "<Amount>18</Amount>" +
                 "</Op>" +
                 "<Op>" +
                   "<Where>AIX</Where>" +
                   "<Amount>20</Amount>" +
                 "</Op>" +
                 "<Op>" +
                   "<Where>LILLE</Where>" +
                   "<Amount>12</Amount>" +
                  "</Op>" +
               "</dataroot>";

            XDocument doc1 = XDocument.Parse(input);
            var groups = doc1.Descendants("Op").GroupBy(x => x.Element("Where").Value).ToList();
            int count = doc1.Descendants("Op").Count();
            int total = doc1.Descendants("Amount").Select(x => (int)x).Sum();

            XDocument doc2 = XDocument.Parse("<?xml version=\"1.0\" encoding=\"UTF-8\"?><Document></Document>");
            XElement document = doc2.Element("Document");
            XElement recap = new XElement("Recap");
            document.Add(recap);
            recap.Add(new XElement("Nop", count));
            recap.Add(new XElement("TotAmount", total));

            foreach (var opGroup in groups.AsEnumerable())
            {
                XElement location = new XElement("Location");
                document.Add(location);
                location.Add(opGroup.Elements("Where").FirstOrDefault());
                int groupCount = opGroup.Count();
                int locAmount = opGroup.Descendants("Amount").Select(x => (int)x).Sum();
                location.Add(new XElement("Nop", groupCount));
                location.Add(new XElement("LocAmount", locAmount));

                XElement ops = new XElement("Ops");
                location.Add(ops);
                foreach (var op in opGroup)
                {
                    ops.Add(op.Element("Amount"));
                }

            }


        }
    }
}
​

I am still unable to compute the operation count (Nop) and LocAmount for each locations

尝试:

        <Nop>
            <xsl:value-of select="count(key('Ops-by-location', Where))"/>
        </Nop>
        <TotAmount>
            <xsl:value-of select="sum(key('Ops-by-location', Where)/Amount)"/>
        </TotAmount>

或者,效率更高一点:

<xsl:template match="Op">
    <xsl:variable name="local-ops" select="key('Ops-by-location', Where)"/>
    <Location>
        <xsl:copy-of select="Where"/>
        <Nop>
            <xsl:value-of select="count($local-ops)"/>
        </Nop>
        <TotAmount>
            <xsl:value-of select="sum($local-ops/Amount)"/>
        </TotAmount>
        <xsl:copy-of select="$local-ops/Amount"/>
    </Location>
</xsl:template>

这是一个使用 Muenchian 方法的 XSLT 1.0 解决方案:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" omit-xml-declaration="no" />

<xsl:key name="Where" match="/dataroot/Op/Where" use="text()" />

<xsl:template match="/">
  <xsl:element name="Document">
    <xsl:element name="Recap">
      <xsl:for-each select="//dataroot">
    <Nop><xsl:value-of select="count(*)"/></Nop>
        <TotAmount><xsl:value-of select="sum(//Amount)"/></TotAmount>
      </xsl:for-each>
    </xsl:element>

   <xsl:for-each select="/dataroot/Op/Where[generate-id()
                         = generate-id(key('Where',text())[1])]">
    <xsl:element name="Location">       

      <Where><xsl:value-of select="."/></Where>             
      <Nop><xsl:value-of select="count(key('Where',text()))"/></Nop>
      <LocAmount><xsl:value-of select="sum(key('Where',text())/../Amount)"/></LocAmount>

    <xsl:element name="Ops">
       <xsl:copy-of select="key('Where',text())/../Amount"/>              
    </xsl:element>

    </xsl:element>      
   </xsl:for-each>      

  </xsl:element>  
</xsl:template>

</xsl:stylesheet>