xml node hierarchy transform
I'm wondering if there is a way to flip the hierarchy of node using linq to XML or xslt.
I'm want to transform
<hardware>
<software/>
<software/>
</hardware>
<hardware>
<software/>
<software/>
</hardware>
Where I use the existing parent - child relationship and just flip and rebuild the stru开发者_StackOverflow中文版cture to look like this.
<software>
<hardware/>
<hardware/>
</software>
<software>
<hardware/>
<hardware/>
</software>
I'm wondering if there is a way to flip the hierarchy of node using linq to XML or xslt.
. . . . . . .
Where I use the existing parent - child relationship and just flip and rebuild the structure.
Yes. This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kSoftByName" match="software"
use="@name"/>
<xsl:template match=
"software[generate-id()
=
generate-id(key('kSoftByName',@name)[1])
]
">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates mode="copy"
select="key('kSoftByName',@name)/.."/>
</xsl:copy>
</xsl:template>
<xsl:template match="hardware" mode="copy">
<xsl:copy>
<xsl:copy-of select="@*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<inventory>
<hardware name="PC">
<software name="Office"/>
<software name="Safari"/>
<software name="Windows"/>
</hardware>
<hardware name="Mac">
<software name="Safari"/>
<software name="Office"/>
<software name="Leopard"/>
</hardware>
</inventory>
produces the wanted, correct result:
<software name="Office">
<hardware name="PC"/>
<hardware name="Mac"/>
</software>
<software name="Safari">
<hardware name="PC"/>
<hardware name="Mac"/>
</software>
<software name="Windows">
<hardware name="PC"/>
</software>
<software name="Leopard">
<hardware name="Mac"/>
</software>
Explanation: Muenchian method for grouping.
Here's the LINQ version.
Given xml like this:
var xml = @"
<inventory>
<hardware name=""PC"">
<software name=""Office""/>
<software name=""Safari""/>
<software name=""Windows""/>
</hardware>
<hardware name=""Mac"">
<software name=""Safari""/>
<software name=""Office""/>
<software name=""Leopard""/>
</hardware>
</inventory>";
Here's the query to pull it apart:
var xd = XDocument.Parse(xml);
var query =
from h in xd.Root.Elements()
from s in h.Elements()
select new
{
hardware = h.Attribute("name").Value,
software = s.Attribute("name").Value
};
var lookup = query.ToLookup(q => q.software, q => q.hardware);
And here's the query to put it back together again, flipped as desired:
var result =
new XDocument
(
new XElement
(
"inventory",
from s in query.Select(q => q.software).Distinct()
select new XElement
(
"software",
new XAttribute("name", s),
from h in lookup[s].Distinct()
select new XElement
(
"hardware",
new XAttribute("name", h)
)
)
)
);
/*
<inventory>
<software name="Office">
<hardware name="PC" />
<hardware name="Mac" />
</software>
<software name="Safari">
<hardware name="PC" />
<hardware name="Mac" />
</software>
<software name="Windows">
<hardware name="PC" />
</software>
<software name="Leopard">
<hardware name="Mac" />
</software>
</inventory>
*/
精彩评论