I have a datatable with category/subcategory records in the format below:
hierarchy id1 cat1 id2 cat2 id3 cat3
4 3105 Mens 3195 Shorts 3130 Shorts
4 3105 Mens 3195 Shorts 3196 Swim Shorts
4 3105 Mens 3177 Knitwear 3118 Jumpers
4 3105 Mens 3177 Knitwear 3178 Cardigans
4 3105 Mens 3177 Knitwear 3814 V-Neck Knitwear
I'm trying to convert it to xml in a format like this:
<CATEGORY NAME="mens">
<CATEGORIES NAME="Shorts" />
<CATEGORIES NAME="Shorts" />
<CATEGORIES NAME="SwimShorts" />
<CATEGORIES NAME="Knitwear" />
<CATEGORIES NAME="Jumpers" />
<CATEGORIES NAME="Cardigans" />
<CATEGORIES NAME="V-Neck Knitwear" />
But the best I can get is this:
<CATEGORY NAME="Mens">
<CATEGORIES NAME="Knits" />
<CATEGORIES NAME="Crew Neck Knitwear" />
</CATEGORY>
<CATEGORY NAME="Mens">
<CATEGORIES NAME="Knits" />
<CATEGORIES NAME="Cardigans" />
As you can see there are duplicates which I don't want. I know I need to merge or de-duplicate somehow.
I'm returning the data as AsEnumerable() and then doing a foreach and creating an XElement for the top-level category and child XElements for the the subcategories:
var e = new XElement("CATEGORY", new XAttribute("ID", item["did1"]), new XAttribute("NAME", item["name1"]),
item["did2"].ToString() != "" ? new XElement("CATEGORIES", new XAttribute("ID", item["did2"]), new XAttribute("NAME", item["name2"])) : null,
item["did3"].ToString() != "" ? new XElement("CATEGORIES", new XAttribute("ID", item["did3"]), new XAttribute("NAME", item["name3"])) : null,
item["did4"].ToString() != "" ? new XElement("CATEGORIES", new XAttribute("ID", item["did4"]), new XAttribute("NAME", item["name4"])) : null
);
I'm not fussy about what technique I used to product the results.
How about something like this
var X = from item in Data
group item by item.cat1 into g
select new XElement(
"CATEGORY",
new XAttribute("NAME", g.Key),
from it in g
group it by it.cat2 into k
select new XElement("CATEGORIES2", new XAttribute("NAME", k.Key),
from i in k.Select(x=>x.cat3).Distinct()
select new XElement("CATEGORIES3", new XAttribute("NAME",i))
)
);
Related
I have the following C# code to generate an XML document. The problem is that I need to swap the order of the attributes in the tag "CState". Therefore I have already tried several things. Unfortunately it does not work or I get an exception.
XNamespace ns = "http:dev.test.com/Job/Con";
XNamespace nsi = "http:www.w3.org/2001/XMLSchema-instance";
XElement doc = new XElement(ns + "CState", new XAttribute(XNamespace.Xmlns + "xsi", nsi),
new XElement(ns + "Page",
new XElement(ns + "Field", new XAttribute("dName", "MembershipNumber"), new XAttribute("type", "Text"), new XAttribute("value", "123456")),
new XElement(ns + "Field", new XAttribute("dName", "FirstName"), new XAttribute("type", "Text"), new XAttribute("value", "Michael")),
new XElement(ns + "Field", new XAttribute("dName", "LastName"), new XAttribute("type", "Text"), new XAttribute("value", "Hendly"))
));
This is the output in the XML file.
<?xml version="1.0" encoding="utf-8"?>
<CState xmlns:xsi="http:www.w3.org/2001/XMLSchema-instance" xmlns="http:dev.test.com/Job/Con">
<Page>
<Field dName="MembershipNumber" type="Text" value="123456" />
<Field dName="FirstName" type="Text" value="Michael" />
<Field dName="LastName" type="Text" value="Hendly" />
</Page>
</ControlStatements>
However, the attributes should appear in a different order.
This is how the second line in the XML file is created (incorrect):
<CState xmlns:xsi="http:www.w3.org/2001/XMLSchema-instance" xmlns="http:dev.test.com/Job/Con">
This is how the second line should be created in the XML file (correct):
<CState xmlns="http:dev.test.com/Job/Con" xmlns:xsi="http:www.w3.org/2001/XMLSchema-instance">
How can I achieve this as easily as possible?
I am trying to create a generic class structure to save lists with various subitems in an xml file as XML attributes. Each subitem has Name (string) and a Value (double).
An example:
The XML of two Lists - frame 1 and frame 2 - should look like this shown above. The frame1 is a List of the class rec. The class rec always contains multiple pairs of parameters and their values. In frame1 the class rec contains the parameters car, factory and height. And in frame2 book and page.
<frame1>
<rec car="0" factory="1" height="2" />
<rec car="1" factory="4" height="2" />
<rec car="2" factory="4" height="3" />
<rec car="3" factory="5" height="2" />
</frame1>
<frame2>
<rec book="0" page="1" />
<rec book="1" page="4" />
<rec book="2" page="4" />
<rec book="3" page="5" />
</frame2>
This is only an example. I do not want to create different classes with different properites for each "frame" List. I want to solve the problem with one class "rec" as the subitems are always changing. Is there a way to create this?
Thank you in advance
I think you can use a code like this: lists is an object[][], so not any generic types needed.
using System.Xml.Linq;
...
var lists = new[]
{
new object[]
{
new ClassA("0", "1", "2"), new ClassA("1", "4", "2"), new ClassA("2", "4", "3"),
new ClassA("3", "5", "2"),
},
new object[]
{
new ClassB("0", "1"), new ClassB("1", "4"), new ClassB("2", "4"),
new ClassB("3", "5"),
}
};
var xml = new XDocument(new XElement("Root"));
for (var i = 0; i < lists.Length; i++)
{
var eFrame = new XElement($"frame{i}");
var list = lists[i];
foreach (var obj in list)
{
var eRec = new XElement("rec");
var props = obj.GetType().GetProperties();
foreach (var prop in props)
{
eRec.SetAttributeValue(prop.Name, prop.GetValue(obj).ToString());
}
eFrame.Add(eRec);
}
xml.Root.Add(eFrame);
}
Result will be something like this as xml object:
<Root>
<frame0>
<rec car="0" factory="1" height="2" />
<rec car="1" factory="4" height="2" />
<rec car="2" factory="4" height="3" />
<rec car="3" factory="5" height="2" />
</frame0>
<frame1>
<rec book="0" page="1" />
<rec book="1" page="4" />
<rec book="2" page="4" />
<rec book="3" page="5" />
</frame1>
</Root>
I have the following xml node
<group>
<level id="1" name="Level 1" />
<level id="2" name="Level 2" />
</group>
How can insert new element with it's attributes to xml file
var doc = XDocument.Load("yourxmlfile.xml");
var x = new XElement("level",
new XAttribute("id", 3),
new XAttribute("name", "Level 3"));
doc.Element("group").Add(x);
doc.Save("yourxmlfile.xml");
This code doesn't work for me and I can't understand why.
xmlDoc.Element("results").Add(
new XElement("user",
new XAttribute("id", user.Id),
new XAttribute("facebookid", user.FacebookId),
new XAttribute("email", user.Email),
new XAttribute("totalpoints", totalpoints)
).Add(
user.Answers.Select(value => new XElement("question",
new XAttribute("id", value.QuestionId),
new XAttribute("answer_id", value.AnswerId),
new XAttribute("points", value.Points)))
)
);
I'm trying to create this
<results>
<user id="2323" facebookId="3254954795743957" email="david#gmail" totalPoints="">
<question id="1" answer_id="3" points="0" />
<question id="2" answer_id="1" points="1" />
</user>
</results>
xmlDoc.Element("results").Add(
new XElement("user",
new XAttribute("id", user.Id),
new XAttribute("facebookid", user.FacebookId),
new XAttribute("email", user.Email),
new XAttribute("totalpoints", totalpoints),
user.Answers.Select(value => new XElement(
"question",
new XAttribute("id", value.QuestionId),
new XAttribute("answer_id", value.AnswerId),
new XAttribute("points", value.Points)
))
)
);
The problem was that the Add method returns void so you can't continue with another Add over the void result. Also, for attributes you need to use XAttribute, not XElement.
I know I can do this iteratively, but it would be cool to do it in a single LINQ statement.
I have some XML that looks like this:
<parent name="george">
<child name="steve" age="10" />
<child name="sue" age="3" />
<pet type="dog" />
<child name="jill" age="7" />
</parent>
<!-- ... -->
and I want to write a LINQ to XML statement to turn it into
<node type="parent" label="george">
<node type="child" label="steve" years="10 />
<node type="child" label="sue" years="3" />
<node type="child" label="jill" years="7" />
<!-- no pets! -->
</parent>
<!-- ... -->
Is that possible in a single LINQ to XML statement?
I've included two from statements in a LINQ statement before, but not a second select, which seems to be what this would require.
You'll need to query the desired elements and create new elements and attributes using the queried items. Something like this should work:
var input = #"<root>
<parent name=""george"">
<child name=""steve"" age=""10"" />
<child name=""sue"" age=""3"" />
<pet type=""dog"" />
<child name=""jill"" age=""7"" />
</parent>
</root>";
var xml = XElement.Parse(input);
var query = from p in xml.Elements("parent")
select new XElement("node",
new XAttribute("type", p.Name),
new XAttribute("label", p.Attribute("name").Value),
from c in p.Elements("child")
select new XElement("node",
new XAttribute("type", c.Name),
new XAttribute("label", c.Attribute("name").Value),
new XAttribute("years", c.Attribute("age").Value)));
Quick and dirty:
doc.Elements("parent")
.Select(p =>
new XElement("node",
new XAttribute("type", p.Name),
new XAttribute("label", p.Attribute("name") != null ? p.Attribute("name").Value : ""),
p.Elements("child")
.Select(c =>
new XElement("node",
new XAttribute("type", c.Name),
new XAttribute("label", c.Attribute("name") != null ? c.Attribute("name").Value : ""),
new XAttribute("years", c.Attribute("age") != null ? c.Attribute("age").Value : ""))
)
)
);