Reputation: 53
I'm new to LINQ and C# but I have an xml file generated from a database table. Within the XML document is a element called "group", I would like to wrap all group elements with the element called "groups".
An extract of the XML document is:
<members>
- <user>
<fullname>John Smith</fullname>
<username>SmithA</username>
<email>[email protected]/email>
<distinguishedName>xxx</distinguishedName>
<group>London</group>
</user>
- <user>
<fullname>Sue Jones</fullname>
<username>JonesS</username>
<email>[email protected]/email>
<distinguishedName>xxx</distinguishedName>
<group>London</group>
</user>
</members>
The end result I struggling to code in C# ASP.NET is:
<members>
- <user>
<fullname>John Smith</fullname>
<username>SmithA</username>
<email>[email protected]/email>
<distinguishedName>xxx</distinguishedName>
<groups>
<group>London</group>
<groups>
</user>
- <user>
<fullname>Sue Jones</fullname>
<username>JonesS</username>
<email>[email protected]/email>
<distinguishedName>xxx</distinguishedName>
<groups>
<group>London</group>
<groups>
</user>
</members>
Any help will be appreciated.
Upvotes: 0
Views: 816
Reputation: 96477
One way to do this is to:
user
elementgroups
elementgroup
to the new groups
elementgroup
elementThis approach would be similar to this, provided xml
is an XElement
:
foreach (var user in xml.Elements("user"))
{
user.Add(new XElement("groups", user.Element("group")));
user.Element("group").Remove();
}
If you're using an XDocument
you could use xml.Root.Elements("user")
instead.
foreach (var user in xml.Elements("user"))
{
var groups = user.Elements()
.Where(e => e.Name.LocalName.StartsWith("group"))
.ToArray();
// rename groups
foreach (var group in groups)
group.Name = "group";
user.Add(new XElement("groups", groups));
groups.Remove();
}
Notice that I used ToArray()
to prevent the Remove
call from reevaluating the expression, which would incorrectly remove the newly added groups
element since it too matches the condition of starting with "group." Another way around this would be to change the Where
predicate to also check that the name ends with a digit. It's extra work to prevent accidentally selecting any other element that may start with "group" but it's up to you based on your knowledge of the XML structure.
Upvotes: 1
Reputation: 1499770
I think this is what you want - it seems to work for me:
using System;
using System.Linq;
using System.Xml.Linq;
class Program
{
static void Main(string[] args)
{
XDocument doc = XDocument.Load("test.xml");
var groupedGroups = doc.Descendants("group")
.GroupBy(x => x.Parent);
foreach (var groupedGroup in groupedGroups)
{
// Create the new element (copies each <group>)
groupedGroup.Key.Add(new XElement("groups", groupedGroup));
// Remove all the old ones
foreach (var element in groupedGroup)
{
element.Remove();
}
}
Console.WriteLine(doc);
}
}
Apologies for the name - it's kinda hard to come up with names when every concept is "group" :)
Upvotes: 3