Parse XML into DataTable - c#

I have the following XML:
<RECORDS>
<RECORD>
<PROPERTY NAME="FNAME" TYPE="string"></PROPERTY>
<PROPERTY NAME="LNAME" TYPE="string">
<VALUE>SMITH</VALUE>
</PROPERTY>
<PROPERTY NAME="ZIP" TYPE="string">
<VALUE></VALUE>
</PROPERTY>
<PROPERTY NAME="PHONE" TYPE="string">
<VALUE>5551212</VALUE>
</PROPERTY>
</RECORD>
<RECORD>
....
</RECORD>
<RECORD>
....
</RECORD>
<RECORD>
....
</RECORD>
</RECORDS>
There may be 1000 "RECORD" nodes under "RECORDS". I am trying to simply dump each record into a data row (or the entire thing into a datatable). I'd be happy to just get all the "VALUE"s into each row's column. Is there an efficient way to do this in C#? Doing a foreach() and accessing each property one at a time seems awfully slow.
I imagine it would involve Row.ItemArray or maybe XMLArray or something along those lines.
Interested to see a good solution to this!

Try this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string xml =
"<RECORDS>" +
"<RECORD>" +
"<PROPERTY NAME=\"FNAME\" TYPE=\"string\"></PROPERTY>" +
"<PROPERTY NAME=\"LNAME\" TYPE=\"string\">" +
"<VALUE>SMITH</VALUE>" +
"</PROPERTY>" +
"<PROPERTY NAME=\"ZIP\" TYPE=\"string\">" +
"<VALUE></VALUE>" +
"</PROPERTY>" +
"<PROPERTY NAME=\"PHONE\" TYPE=\"string\">" +
"<VALUE>5551212</VALUE>" +
"</PROPERTY>" +
"</RECORD>" +
"</RECORDS>";
XDocument doc = XDocument.Parse(xml);
DataTable dt = new DataTable();
List<XElement> properties = doc.Descendants("PROPERTY").ToList();
//add columns to table
foreach (XElement property in properties)
{
string name = property.Attribute("NAME").Value;
string _type = property.Attribute("TYPE").Value;
if(!dt.Columns.Contains(name))
{
dt.Columns.Add(name, Type.GetType("System." + _type, false, true));
}
}
//add rows to table
foreach(XElement record in doc.Descendants("RECORDS").ToList())
{
DataRow newRow = dt.Rows.Add();
foreach (XElement property in properties)
{
string name = property.Attribute("NAME").Value;
Type _type = Type.GetType("System." + property.Attribute("TYPE").Value, false, true);
string value = property.Value;
switch (_type.ToString())
{
case "System.String" :
newRow[name] = (string)value;
break;
case "System.Int" :
newRow[name] = int.Parse(value);
break;
}
}
}
}
}
}

Related

C# - Convert XML elements to key value pair in a concatenated string

I Have below XML as an input
<Details>
<PAY>
<Mode>xyz</Mode>
<REMARKS>no</REMARKS>
</PAY>
<data>
<Customer>
<Participant>
<Participant1FirstName>aaaa</Participant1FirstName>
<Participant1LastName>zzzz</Participant1LastName>
</Participant>
<Participant>
<Participant2FirstName>bbbb</Participant2FirstName>
<Participant2LastName>yyyy</Participant2LastName>
</Participant>
<Participant>
<Participant3FirstName>cccc</Participant3FirstName>
<Participant3LastName>xxxx</Participant3LastName>
</Participant>
</Customer>
</data>
</Details>
Desired output :
String strOutput = "|Participant1FirstName = aaaa|Participant1LastName = zzzz|; |Participant2FirstName = bbbb|Participant2LastName = yyyy|; |Participant3FirstName = cccc|Participant3LastName = xxxx|"
SO far, I have tried parsing XML string into XDocument, and thinking of using Linq to get the desired output, but don't know the way forward.
Input :
string strXML = "<Details> <PAY> <Mode>xyz</Mode> <REMARKS>no</REMARKS> </PAY> <data> <Customer> <Participant> <Participant1FirstName>aaaa</Participant1FirstName> <Participant1LastName>zzzz</Participant1LastName> </Participant> <Participant> <Participant2FirstName>bbbb</Participant2FirstName> <Participant2LastName>yyyy</Participant2LastName> </Participant> <Participant> <Participant3FirstName>cccc</Participant3FirstName> <Participant3LastName>xxxx</Participant3LastName> </Participant> </Customer> </data></Details>"
XDocument xdoc = new XDocument();
xdoc = XDocument.Parse(strXML);
Edit
Any way I can get desired output from below XML ?
<Details>
<PAY>
<Mode>xyz</Mode>
</PAY>
<data>
<Customer>
<Participant>
<Participant1>
<Participant1FirstName>aaaa</Participant1FirstName>
<Participant1LastName>zzzz</Participant1LastName>
</Participant1>
<Participant2>
<Participant2FirstName>bbbb</Participant2FirstName>
<Participant2LastName>yyyy</Participant2LastName>
</Participant2>
<Participant>
</Customer>
</data>
</Details>
You could do it like this
XDocument xdoc = new XDocument();
xdoc = XDocument.Parse(strXML);
var values = xdoc.Descendants("Participant").Elements()
.Select(x => x.Name + " = " + x.Value).ToList();
string result = string.Join("|", values);
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
List<Particapant> participants = doc.Descendants("Participant").Select(x => new Particapant()
{
firstName = (string)x.Elements().FirstOrDefault(),
lastName = (string)x.Elements().LastOrDefault()
}).ToList();
string output = string.Join(";", participants
.Select(x => string.Format("|ParticipantFirstName = {0}|ParticipantLastName = {1}|", x.firstName, x.lastName)));
}
}
public class Particapant
{
public string firstName { get; set; }
public string lastName { get; set; }
}
}

Reading XML with namespace and repeating nodes

I have the following XML:
<resource>
<description>TTT</description>
<title>TEST</title>
<entity xmlns="TdmBLRuPlUz.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="TdmBLRuPlUz.xsd TdmBLRuPlUz.xsd">
<UzdProd>
<row>
<F_DAUDZ>50</F_DAUDZ>
<BR_DAUDZ/>
<DAUDZ>50</DAUDZ>
<U_DAUDZ/>
<NKODS>ST2.0_014_023</NKODS>
</row>
</UzdProd>
<UzdMat>
<row>
<NKODS>SAG 2.0_014_150</NKODS>
<NNOSAUK>Sagatave 2.0mm*0.14*150m</NNOSAUK>
<PK_VIEN>1</PK_VIEN>
<DAUDZ>0.077</DAUDZ>
<F_DAUDZ>0.077</F_DAUDZ>
</row>
</UzdMat>
</entity>
</resource>
And this is my C# code:
XNamespace ns = "TdmBLRuPlUz.xsd";
XDocument doc = XDocument.Parse(xml);
foreach (XElement element in doc.Descendants(ns + "row"))
{
Console.WriteLine(element.Element(ns + "NKODS").Value);
string NKODS = element.Element(ns + "NKODS").Value;
string F_DAUDZ = element.Element(ns + "F_DAUDZ").Value;
string DAUDZ = element.Element(ns + "DAUDZ").Value;
}
What I need is to read values from the XML nodes NKODS, F_DAUDZ and DAUDZ.
The problem is that there are repeating nodes with those names and with this code it gives me the last ones which are under UzdMat node. What would be the way to get the values for these nodes under UzdProd?
I tried to change row to UzdProd, but that didn't work.
You need to read the specific row you want rather than looping through all of them. For example:
var prodRow = doc.Descendants(ns + "UzdProd").Elements(ns + "row").Single();
var matRow = doc.Descendants(ns + "UzdMat").Elements(ns + "row").Single();
var prodNkods = (string) prodRow.Element(ns + "NKODS");
var matNkods = (string) matRow.Element(ns + "NKODS");
See this fiddle for a working demo.
See if this works :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication25
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement entity = doc.Descendants().Where(x => x.Name.LocalName == "entity").FirstOrDefault();
XNamespace ns = entity.GetDefaultNamespace();
var results = entity.Elements().Select(x => new {
uzd = x.Name.LocalName,
dict = x.Descendants(ns + "row").Elements().GroupBy(y => y.Name.LocalName, z => (string)z)
.ToDictionary(y => y.Key, z => z.FirstOrDefault())
}).ToList();
}
}
}

How to group XML nodes based on their values in C#

I have my XML as:
<root>
    <element>
        <id>1</id>
        <group>first</group>
    </element>
    <element>
        <id>2</id>
        <group>second</group>
    </element>
    <element>
        <id>3</id>
        <group>first</group>
    </element>
</root>
Is there anyway we can group the nodes with same values like this:
<root>
    <groups name="first">
<element>
        <id>1</id>
        <group>first</group>
     </element>
<element>
        <id>3</id>
        <group>first</group>
    </element>
</groups>
   <groups name="second"><element>
       <id>2</id>
        <group>second</group>
    </element>
</groups>
</root>
Is there a way to group it based on same node values?
I just tested the code below and it matches your results.
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 xml =
"<root>" +
"<element>" +
"<id>1</id>" +
"<group>first</group>" +
"</element>" +
"<element>" +
"<id>2</id>" +
"<group>second</group>" +
"</element>" +
"<element>" +
"<id>3</id>" +
"<group>first</group>" +
"</element>" +
"</root>";
XDocument doc = XDocument.Parse(xml);
var groups = doc.Descendants("element")
.GroupBy(x => (string)x.Element("group"))
.ToList();
XElement newXml = new XElement("root");
foreach(var group in groups)
{
newXml.Add(new XElement("groups", new object[] {
new XAttribute("name", group.Key),
group
}));
}
}
}
}
XPath approach
string val= "first";
XmlDocument doc = new XmlDocument();
doc.Load(Server.MapPath("./Xml/YourXML.xml"));
string text = string.Empty;
XmlNodeList xnl = doc.SelectNodes("/root/groups ");
foreach (XmlNode node in xnl)
{
text = node.Attributes["name"].InnerText;
if (text == val)
{
XmlNodeList xnl = doc.SelectNodes(string.Format("/root/groups [#name='{0}']/element", val));
foreach (XmlNode node2 in xnl )
{
text = text + "<br>" + node2["id"].InnerText;
text = text + "<br>" + node2["group"].InnerText;
}
}
Response.Write(text);
}
or
var nodes = (from n in xml.Descendants("element").
Where(r => r.Parent.Attribute("name").Value == "first")
select new
{
id = (string)n.Element("id").Value,
group = (string)n.Element("group").Value
}).ToList();
Group the elements and build up a new document placing each group in a new <groups> element.
var newDoc = new XDocument(
new XElement("root",
from e in doc.Descendants("element")
group e by (string)e.Element("group") into g
select new XElement("groups",
new XAttribute("name", g.Key),
g
)
)
);
I had to try this in VB. My Group By skills need a lot of practice.
Using this test data
Dim myXML As XElement
myXML = <root>
<element>
<id>1</id>
<group>first</group>
</element>
<element>
<id>2</id>
<group>second</group>
</element>
<element>
<id>3</id>
<group>first</group>
</element>
</root>
this seems to work
Dim newXML As XElement = <root></root>
newXML.Add(From el In myXML.Elements
Order By el.<group>.Value
Group By gn = el.<group>.Value Into g = Group
Select New XElement("Groups", New XAttribute("name", gn), g))
newXML =
<root>
<Groups name="first">
<element>
<id>1</id>
<group>first</group>
</element>
<element>
<id>3</id>
<group>first</group>
</element>
</Groups>
<Groups name="second">
<element>
<id>2</id>
<group>second</group>
</element>
</Groups>
</root>
Similar to other answers.

Parse from single node in multiple nodes that have same name

<physicalResource>
<prName>PRS_EID</prName>
<prDescription>PRS_EID</prDescription>
<physicalResourceCharacteristic>
<characteristic>
<name>eidno</name>
<value>SH001499000</value>
</characteristic>
<characteristic>
<name>flatno</name>
<value>14303</value>
</characteristic>
</physicalResourceCharacteristic>
</physicalResource>
<physicalResource>
<prName>PRS_OLT</prName>
<prDescription>PRS_OLT</prDescription>
<physicalResourceCharacteristic>
<characteristic>
<name>device</name>
<value>WC-OMU-AO01</value>
</characteristic>
<characteristic>
<name>frame</name>
<value>1</value>
</characteristic>
<characteristic>
<name>port</name>
<value>5</value>
</characteristic>
</physicalResourceCharacteristic>
</physicalResource>
Hello Dears.. I have an xml file. It contains different nodes with same node name. In the example under physicalResource node, I want to extract prName and all characteristic's name and value. But I cant parse them seperately.
I'm using
nodeListPrs = root.SelectNodes("/physicalResource/physicalResourceCharacteristic/characteristic", nsmgr);
It extracts all charactics value for both nodes. How can i extract these parameters from single physicalResource node?
You can use xmldocument and load that xml to xmldocument then you can use selectsinglenode. It would help!!
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(xml);
xdoc.DocumentElement.SelectSingleNode(path for the node..);
Use XML Linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication78
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
var results = doc.Descendants("physicalResource").Select(x => new {
prName = (string)x.Element("prName"),
characteristics = x.Descendants("characteristic").Select(y => new {
name = (string)y.Element("name"),
value = (string)y.Element("value")
}).ToList()
}).ToList();
}
}
}
To select all prName nodes with the corresponding characteristic nodes of the first physicalResourceCharacteristic node, you would use the follwing XPath expression in SelectNodes.
/res/physicalResource[1]/physicalResourceCharacteristic/characteristic | /res/physicalResource[1]/prName
The result is
<?xml version="1.0" encoding="UTF-8"?>
<prName>PRS_EID</prName>
<characteristic>
<name>eidno</name>
<value>SH001499000</value>
</characteristic>
<characteristic>
<name>flatno</name>
<value>14303</value>
</characteristic>
I'm not sure if this is what you strive for. You could iterate over the count of physicalResource constructing [xxx] XPath-expressions to get a nodeset with a couple of these entries. Or you could omit the [1] to get a nodeset with all physicalResources prefixed by prNames, but with mixed node-types.
Thanks all. I have resolved by using SelectSingleNode and SelectNodes cascade:
XmlNodeList nodeListPrs = root.SelectNodes("/ns2:serviceOrder/resourceFacingService/physicalResource", nsmgr);
foreach (XmlNode book in nodeListPrs)
{
string prsName = book["prName"].InnerText;
try
{
nodeListPrsCh = book.SelectSingleNode("physicalResourceCharacteristic").SelectNodes("characteristic");
foreach (XmlNode characteristics in nodeListPrsCh)
{
dataGridView3.Rows.Add();
dataGridView3.Rows[i].Cells[0].Value = prsName;
try { string name = characteristics["name"].InnerText; dataGridView3.Rows[i].Cells[1].Value = name; }
catch { dataGridView3.Rows[i].Cells[1].Value = "-"; }
try { string value = characteristics["value"].InnerText; dataGridView3.Rows[i].Cells[2].Value = value; }
catch { dataGridView3.Rows[i].Cells[2].Value = "-"; }
i = i + 1;
}
}
catch
{
dataGridView3.Rows.Add();
dataGridView3.Rows[i].Cells[0].Value = prsName;
dataGridView3.Rows[i].Cells[1].Value = "-";
dataGridView3.Rows[i].Cells[2].Value = "-";
i = i + 1;
}
}

XML to CSV in C# newbie

Hello Guys i need help with this xml data.
<?xml version="1.0" encoding="utf-8"?>
<xml-data xmlns="http://www.lucom.com/ffw/xml-data-1.0.xsd">
<form>catalog://Unternehmen/ust/ZM_Formular_online</form>
<instance>
<dataset id="tbl_ZM_tabelle">
<datarow>
<element id="knre1">AT</element>
<element id="knre2">U18713701</element>
<element id="umsatz_art">0</element>
<element id="betrag">7605</element>
<element id="zeile_m">1</element>
</datarow>
I want to write a csv data by using the knre1,knre2 and "betrag" id's. Its always the same name.
It should be looking like this
AT;U18713701;7605
Iam a totally newbie in c# and i need the help.
This is something very basic:
using System.Xml.XPath;
using System.Xml.Linq;
using System.IO;
XDocument xdoc = XDocument.Load("test.xml");
StringBuilder csv = new StringBuilder();
foreach (XElement datarow in xdoc.Root.XPathSelectElements("dataset/datarow"))
{
string knre1 = datarow.Elements("element").Where(i => i.Attribute("id").Value.Contains("knre1")).First().Value;
string knre2 = datarow.Elements("element").Where(i => i.Attribute("id").Value.Contains("knre2")).First().Value;
string betrag = datarow.Elements("element").Where(i => i.Attribute("id").Value.Contains("betrag")).First().Value;
csv.AppendLine(knre1 + "," + knre2 + "," + betrag);
}
File.WriteAllText("cvsFile.csv", csv.ToString());
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
XDocument xdoc = XDocument.Load("C:\\Users\\edvazubi\\Desktop\\ZM05.2015.xml");
StringBuilder csv = new StringBuilder();
foreach (XElement datarow in xdoc.Root.XPathSelectElements("instance/dataset/datarow"))
{
string knre1 = datarow.Elements("element").Where(i => i.Attribute("id").Value.Contains("knre1")).First().Value;
string knre2 = datarow.Elements("element").Where(i => i.Attribute("id").Value.Contains("knre2")).First().Value;
string betrag = datarow.Elements("element").Where(i => i.Attribute("id").Value.Contains("betrag")).First().Value;
csv.AppendLine(knre1 + "," + knre2 + "," + betrag + "L");
}
File.WriteAllText("C:\\Users\\edvazubi\\Desktop\\CSVFile.csv", csv.ToString());
this is my file now

Categories