A tough table transforming into XML - c#

I have a DataTable which I select from database (Well, these data cross several tables, after the query and putting into a DataTable, it shows at below)
ColumnA ColumnB
a 11
b 33
b 44
a 22
b 55
but I want to transform it into an XML like this
<root>
<header name ='a'>
<item name='11' />
<item name='22' />
</header>
<header name ='b'>
<item name='33' />
<item name='44' />
<item name='55' />
</header>
</root>
Is there an easy way to implement it by C#?

Why bother with C# ?? You can do it in T-SQL directly (SQL Server 2005 and up):
SELECT
ColumnA AS '#name',
(SELECT ColumnB AS '#name'
FROM YourTable t
WHERE t.ColumnA = YourTable.ColumnA
FOR XML PATH('item'), TYPE)
FROM
YourTable
GROUP BY
ColumnA
FOR XML PATH('header'), ROOT('root')
Gives you:
<root>
<header name="a">
<item name="11" />
<item name="22" />
</header>
<header name="b">
<item name="33" />
<item name="44" />
<item name="55" />
</header>
</root>
You can execute this SQL query using standard ADO.NET SqlCommand and get back the XML nicely formatted already.
Marc

OK, second approach after learning that the data is available in a DataTable to begin with.
The code is a bit more involved, since based on a DataTable, you can't really do much in terms of grouping etc. I am building up the XmlDocument (since you're on .NET 2.0) while scanning through the rows of data. I need to keep track of the <header> elements in a dictionary, in order to add a second, third entry with the same "ColumnA" value to that already existing XmlElement in the document - it's a bit involved, but if you study it carefully, I hope you see it's really no trickery or anything - just a bit of bookkeeping along the way of building the XmlDocument:
// create the XmlDocument and add <root> node
XmlDocument doc = new XmlDocument();
doc.AppendChild(doc.CreateElement("root"));
// dictionary to keep track of <header> nodes
Dictionary<string, XmlNode> nodesPerColumnA = new Dictionary<string, XmlNode>();
// Loop through data rows
foreach (DataRow row in tbl.Rows)
{
// extract values for ColumnA and ColumnB as strings
string columnAValue = row["ColumnA"].ToString();
string columnBValue = row["ColumnB"].ToString();
// create a new <item> XmlNode and fill its attribute #Name
XmlElement newNode = doc.CreateElement("item");
XmlAttribute newNodeAttribute = doc.CreateAttribute("name");
newNodeAttribute.InnerText = columnBValue;
newNode.Attributes.Append(newNodeAttribute);
// check if we already have a <header> node for that "ColumnA" value
if(nodesPerColumnA.ContainsKey(columnAValue))
{
// if so - just add <item> below that <header>
XmlNode parent = nodesPerColumnA[columnAValue];
parent.AppendChild(newNode);
}
else
{
// if not - create appropriate <header> node and its #name attribute
XmlElement header = doc.CreateElement("header");
XmlAttribute headerAttr = doc.CreateAttribute("name");
headerAttr.InnerText = columnAValue;
header.Attributes.Append(headerAttr);
header.AppendChild(newNode);
doc.DocumentElement.AppendChild(header);
// store that <header> xmlnode into the dictionary for future use
nodesPerColumnA.Add(columnAValue, header);
}
}
// check the contents of the XmlDocument at the end
string xmlContents = doc.InnerXml;

With LINQ:-
var qry = from row in Table
group row by row.ColumnA into header
select header;
var elem = new XElement("root");
foreach (var header in qry)
{
var elemHead = new XElement("header", new XAttribute("name", header.Key));
elem.Add(elemHead);
foreach (var item in header)
elemHead.Add(new XElement("item", new XAttribute("name", item.ColumnB)));
}
// the variable elem contains the result.

This will do it using .NET 3.5 and the XDocument
XDocument yourDocument = new XDocument(new XElement("root",
new XElement("header",
new XAttribute("name", "a"),
new XElement("item",
new XAttribute("name", "11")),
new XElement("item",
new XAttribute("name", "22"))),
new XElement("header",
new XAttribute("name", "b"),
new XElement("item",
new XAttribute("name", "33")),
new XElement("item",
new XAttribute("name", "44")),
new XElement("item",
new XAttribute("name", "55")))));

Related

how to add attribute with tag in xml using C# [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I am updating my xml grammar file from C#. I am successfully inserted child node within node. I want to insert child node with attribute. I have done this but format is wrong.I want to add new node named as and its attribute names ad having value like this out = "Karaa chee";.****
<?xml version="1.0" encoding="utf-8"?>
<grammar xml:lang="en-US" root="top" mode="voice" tag-format="semantics/1.0"
sapi:alphabet="x-microsoft-ups"
version="1.0" xmlns="http://www.w3.org/2001/06/grammar"
xmlns:sapi="http://schemas.microsoft.com/Speech/2002/06/SRGSExtensions">
<rule id="top" scope="public">
<item>
<ruleref uri="#CityName"/>
<tag> out = "src=" + rules.latest(); </tag>
</item>
to
<item>
<ruleref uri="#CityName"/>
<tag> out += "^dst=" + rules.latest(); </tag>
</item>
</rule>
<rule id="CityName" scope="private">
<one-of>
<item>
Abbottabad
<tag>out = "Abbott aabaad";</tag>
</item>
<item>
Karachi
<tag>out = "Karaa chee";</tag>
</item>
<item>
New Item here
<tag>out = "new item pronunciation here";</tag>
</item>
</one-of>
</rule>
</grammar>
Here C# code that i used to add node and its attribute
XmlElement eleItem = xmlDoc.CreateElement("item", ns);
eleItem.InnerText = item;
eleItem.SetAttribute("tag", val);
foundNode.AppendChild(eleItem);
With LINQ to XML, you can do this :
XNamespace ns = "http://www.w3.org/2001/06/grammar";
XDocument xmlDoc = XDocument.Load("xmlfile.xml");
XElement newitem = new XElement(ns +"Item", new Object[] {"New York",
new XElement(ns + "tag", new Object[] {new XAttribute("out", "Neww Yarke")})});
XElement parentNode = xmlDoc.Descendants(ns + "one-of").First();
parentNode.Add(newitem);
xmlDoc.Save("xmlfile.xml");
If you hesitate between XmlDocument or XDocument : topic
The XML given in the question shows that the item element can contain both text and further tag elements. Note that the tag as shown in the example XML is not an attribute which is what your code is trying to create, which would be written like this:
<item tag="out = "Abbott aabaad";">Abbottabad</item>
What you are creating is child elements. Also intermixed with the elements are chunks of text. To create a mixed text and element content you need to use Text Nodes and Element Nodes like this:
public static void Main()
{
XmlDocument doc = new XmlDocument();
var root = doc.CreateElement("grammar");
doc.AppendChild(root);
var item = doc.CreateElement("item");
var text = doc.CreateTextNode("Abbottabad");
item.AppendChild(text);
var tag = doc.CreateElement("tag");
tag.InnerText = "out = \"Abbott aabaad\";";
item.AppendChild(tag);
root.AppendChild(item);
Console.WriteLine(doc.OuterXml);
}
Which produces something like this:
<grammar>
<item>
Abbottabad
<tag>out = "Abbott aabaad";</tag>
</item>
</grammar>
Like Panagiotis already commented:
your desired output is not valid
<item>
New York
<tag>out = "Neww Yarke";</tag>
</item>
your desired output should be like the following:
<item>
New York
<tag out="Neww Yarke"/>
</item>
you can get the above by doing the following using linq to XML
var document = new XDocument();
var itemelement = new XElement("item",
"New York",
new Xelement("tag", new XAttribute("out", "Neww Yarke")));

Get custom attribute value using LINQ to XML

I'm writing to an XML file from which I'm going to retrieve data later.
Here's how I'm writing to the file.
XNamespace testNM = "urn:lst-emp:emp";
XDocument xDoc;
string path = "project_data.xml";
if (!File.Exists(path))
{
xDoc = new XDocument(
new XDeclaration("1.0", "UTF-16", null),
new XElement(testNM + "Test")
);
}
else
{
xDoc = XDocument.Load(path);
}
var element = new XElement("key",
new XAttribute("name", key),
new XElement("Type", type),
new XElement("Value", value));
xDoc.Element(testNM + "Test").Add(element);
// Save to Disk
xDoc.Save(path);
And this is what my XML file looks like after data is written to it.
<?xml version="1.0" encoding="utf-16"?>
<Test xmlns="urn:lst-emp:emp">
<key name="key2" xmlns="">
<Type>int</Type>
<Value>12312</Value>
</key>
<key name="key3" xmlns="">
<Type>String</Type>
<Value>asdfasd</Value>
</key>
</Test>
Now what would be the simplest way to get the name attribute value (key2 and key3 in this case) along with the Type and Value attribute values.
Load the document;
XDocument doc = XDocument.Load(#"doc.xml");
Loop the key nodes reading what you need;
foreach (var keyNode in doc.Root.Elements("key"))
{
var name = keyNode.Attribute("name");
var type = (string)keyNode.Element("Type"); // or .value to throw if there is no node
...
}

XML Name Space Not Formating Correctly

Building an XML XDocument to push to web service and the root element needs namespace values this is what the XML form should look like....
<shipment-feed xmlns="http://seller.marketplace.sears.com/oms/v5"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://seller.marketplace.sears.com/oms/v5 asn.xsd ">
<shipment>
<header>
<asn-number>00601780002</asn-number>
<po-number>0060180</po-number>
<po-date>2009-09-26</po-date>
</header>
<detail>
<tracking-number>UPS1XXX</tracking-number>
<ship-date>2001-01-01</ship-date>
<shipping-carrier>UPS</shipping-carrier>
<shipping-method>GROUND</shipping-method>
<package-detail>
<line-number>1</line-number>
<item-id>AB12345678912345456789123456789CD</item-id>
<quantity>1</quantity>
</package-detail>
</detail>
</shipment>
</shipment-feed>
This is the xml that I'm getting....
<?xml version="1.0" encoding="utf-8"?>
<shipment-feed xmlns="http://seller.marketplace.sears.com/oms/v5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsischemalocation="http://seller.marketplace.sears.com/oms/v5 asn.xsd">
<shipment xmlns="">
<header>
<asn-number>2824565201311</asn-number>
<po-number>2824565</po-number>
<po-date>2013-11-14</po-date>
</header>
<details>
<tracking-number>579040914892</tracking-number>
<ship-date>2013-11-14</ship-date>
<shipping-carrier>FEDEX</shipping-carrier>
<shipping-method>Ground</shipping-method>
<package-details>
<line-number>1</line-number>
<item-id>LTH7XB1MW-EA</item-id>
<quantity>3</quantity>
</package-details>
</details>
</shipment>
<shipment xmlns="">
<header>
<asn-number>2821596201311</asn-number>
<po-number>2821596</po-number>
<po-date>2013-11-13</po-date>
</header>
<details>
<tracking-number>9405515901119923380663</tracking-number>
<ship-date>2013-11-14</ship-date>
<shipping-carrier>USPS</shipping-carrier>
<shipping-method>Priority Mail</shipping-method>
<package-details>
<line-number>1</line-number>
<item-id>CWD93151-EA</item-id>
<quantity>6</quantity>
</package-details>
<package-details>
<line-number>2</line-number>
<item-id>CWD93901-EA</item-id>
<quantity>4</quantity>
</package-details>
</details>
</shipment>
</shipment-feed>
This is the C# code I created....
XNamespace ns1 = "http://seller.marketplace.sears.com/oms/v5";
XNamespace ns2 = "http://www.w3.org/2001/XMLSchema-instance";
XNamespace ns3 = "http://seller.marketplace.sears.com/oms/v5 asn.xsd";
XDocument doc = new XDocument();
XElement root = new XElement(ns1 + "shipment-feed",
new XAttribute("xmlns" , "http://seller.marketplace.sears.com/oms/v5"),
new XAttribute(XNamespace.Xmlns + "xsi", "http://www.w3.org/2001/XMLSchema-instance"),
new XAttribute("xsi" + "schemalocation", "http://seller.marketplace.sears.com/oms/v5 asn.xsd"));
doc.Add(root);
int x = 1;
foreach (SearsOrder s in SearsList)
{
XElement shipment = new XElement("shipment",
new XElement("header",
new XElement("asn-number", s.asnnumber),
new XElement("po-number", s.ponumber),
new XElement("po-date", s.podate)),
new XElement("details",
new XElement("tracking-number", s.trackingnum),
new XElement("ship-date", s.shipdate),
new XElement("shipping-carrier", s.carrier),
new XElement("shipping-method", s.method),
s.orderitems.Select(i => new XElement("package-details",
new XElement("line-number", x++),
new XElement("item-id", i.itemid),
new XElement("quantity", i.quantity)))
));
doc.Root.Add(shipment);
x = 1;
}
The fist problem is the first child node I'm not seeing where that is coming from because that node is not even declared until the foreach loop. I was under the impression that I was only adding attributes to the root element.
and the other problem is removing the xml declaration
This sort of the thing is the problem:
new XElement("shipment", ...)
You want the shipment elements to be in the "http://seller.marketplace.sears.com/oms/v5" namespace - so you need to make that explicit. That won't show up in the resulting XML directly, because they'll inherit that as the default namespace specified in the root. Basically, wherever you're creating an element, you probably want to use ns1. So:
new XElement(ns1 + "shipment", new XElement(ns1 + "header", ...), ...)
To understand more about why this is required, you should read up on namespace defaulting in the XML Namespaces specification.
and the other problem is removing the xml declaration
So add an XDeclaration to the XDocument:
XDocument doc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"))

Bind XML data to a Dropdownlist c#

I am trying to bind XML data to Dropdownlist
XElement xDoc = XElement.Parse(QContent.OuterXml);
Here is what my xDoc contains
<root xmlns="">
<item value="-1" text="Select" />
<item value="1" text="$30,000" />
<item value="2" text="$50,000" />
</root>
Query to extract the data into a Listitem :
var query = from xEle in xDoc.Descendants("root")
select new ListItem(xEle.Attribute("value").Value , xEle.Attribute("text").Value);
This yields no results. Please advice.
Thanks in advance
BB
Put your XML file inside App_Data or anywhere in program and in MapPath.
Assign their path and also assign the name of your first XML column in the last line.
Here I'm using "code" because it is my first XML column and country is the name of the column I want to show in my dropdownlist.
private void BindCountry()
{
XmlDocument doc = new XmlDocument();
doc.Load(Server.MapPath("~//App_Data//countries.xml"));
foreach (XmlNode node in doc.SelectNodes("//country"))
{
ddlcountry.Items.Add(new ListItem(node.InnerText, node.Attributes["code"].InnerText));
}
}
You can change your LINQ query to the following which will return all the nodes under root and return new items with the value/text pairs that you are trying to bind to.
var query = from xEle in xDoc.Descendants()
select new {value = xEle.Attribute("value").Value , text = xEle.Attribute("text").Value};
Then set up your bindings as follows including collapsing the query to a list.
ddlList.DataValueField = "value";
ddlList.DataTextField = "text";
ddlList.DataSource = query.ToList();
ddlList.DataBind();

How to return sibling XElements in a LINQ to XML statement?

From the following XML input:
<root>
<node name="one" value="1"/>
<node name="two" value="2"/>
<node name="three" value="3"/>
<node name="four" value="4"/>
</root>";
I need to use LINQ to XML to produce the following:
<root>
<name content="one"/>
<value content="1"/>
<name content="two"/>
<value content="2"/>
<name content="three"/>
<value content="3"/>
<name content="four"/>
<value content="4"/>
</root>
This code produces the name elements, but not the value elements.
var input = #"
<root>
<node name=""one"" value=""1""/>
<node name=""two"" value=""2""/>
<node name=""three"" value=""3""/>
<node name=""four"" value=""4""/>
</root>";
var xml = XElement.Parse(input);
var query = new XElement("root",
from p in xml.Elements("node")
select new XElement("name",
new XAttribute("content", p.Attribute("name").Value) /*,
new XElement("value", new XAttribute("content", p.Attribute("value").Value)) */
)
);
If I include the value XElement (commented out above) inside the last parenthesis then it is a child of the name element, but outside the closing parenthesis it no longer has access to q (it is outside the query).
It feels like I need to concatenate two XElements together or somehow include them in another collection that doesn't produce any XML.
You could flatten the attributes using the Enumerable.SelectMany method. In query format this is equivalent to two from clauses:
var query = new XElement("root",
from p in xml.Elements("node")
from a in p.Attributes()
select new XElement(a.Name,
new XAttribute("content", a.Value)
)
);
To contrast, using the actual SelectMany method and writing it fluently would look like this:
var query = new XElement("root",
xml.Elements("node")
.SelectMany(n => n.Attributes())
.Select(a => new XElement(a.Name,
new XAttribute("content", a.Value))));
However, I tend to find the query syntax to be clearer in most of SelectMany usages and I tend to stick to one format or another, although it's perfectly fine to mix both.
Using your code as starting point. Wrap the pair in an item element and then replace it with its children.
var xml = XElement.Parse(input);
var result = new XElement("root",
from p in xml.Elements("node")
select new XElement("item",
new XElement("name", new XAttribute("content", p.Attribute("name").Value)),
new XElement("value", new XAttribute("content", p.Attribute("value").Value))));
result.Descendants("item").ToList().ForEach(n => n.ReplaceWith(n.Elements()));

Categories