我正在尝试根据标记值将 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>
我想转换以下 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>