XML with several levels to Dataset with XmlDocument - c#

I have this xml file with several levels for node's childs:
-product
--name
--price
--made
-----product
--------name
--distributor
-----name
------address
/product
I need to read it in sequence, but it's not possible with XmlReader, so I have to use XmlDocument to read each node and copy into DataSet (or similar).
How can I read node by node and copy to dataset in C# ??? Any help please?

Convert xml string to DataSet using StringReader
string xmlData = "<product><name></name><price></price><made></made></product>";
StringReader reader = new StringReader(xmlData);
DataSet ds = new DataSet();
ds.ReadXml(reader);
Write XML Data into a DataSet directly from xml file:
string myfile = #"C:\myXml.xml";
DataSet ds = new DataSet();
ds.ReadXml(myfile);

I think you can use Xpath over XMl Document to read node by node.

It is possible using XDocument and LINQ-to-XML:
XDocument doc = XDocument.Load("test.xml");
// Create two DataTable instances.
DataTable table1 = new DataTable("patients");
table1.Columns.Add("name");
foreach(var name in doc.Root.DescendantNodes().OfType<XElement>()
.Select(x => x.Name).Distinct())
{
table1.Rows.Add(name);
Console.WriteLine(name);
}

Related

Can DataSet be loaded into XmlDocument?

I see code example from Microsoft here how to convert DataSet object into XmlDataDocumentto work with XML better but this class is going to be depreicated.
It means we should perhaps work with XmlDocument class but can we convert a DataSet into XmlDocument?
This is the part of the code of interest: (basically I want to extract certain nodes)
XmlDataDocument xmlDoc = new XmlDataDocument(dataSet);
XmlNodeList nodeList = xmlDoc.DocumentElement.SelectNodes(
"descendant::Customers[*/OrderDetails/ProductID=43]");
DataRow dataRow;
foreach (XmlNode xmlNode in nodeList)
{
dataRow = xmlDoc.GetRowFromElement((XmlElement)xmlNode);
if (dataRow != null)
Console.WriteLine(dataRow[0]);
}
You can write directly to an XmlDocument by creating an XPathNavigator for the document then writing the DataSet to it directly AppendChild() like so:
var doc = new XmlDocument();
var navigator = doc.CreateNavigator();
using (var writer = navigator.AppendChild())
{
dataSet.WriteXml(writer);
}
However, if you are rewriting your code anyway, you should consider upgrading to XDocument from LINQ to XML which easily supports LINQ queries as well as XPath queries:
var doc = new XDocument();
using (var writer = doc.CreateWriter())
{
dataSet.WriteXml(writer);
}
Either option will be more performant than serializing the DataSet to an XML string then parsing the string.
Notes:
Unlike XmlDataDocument there is no dynamic link maintained between the XmlDocument or XDocument and the DataSet. Changes to one are not reflected in the other after creation.
You might consider whether reading XML into both a DataSet and XDocument / XmlDocument is really required. Consider simplifying your design by using one or the other, but not both.
Demo fiddle here.

Appending to specific location in XML file

I'm trying to append raw XML data part of a List<string> to a XML file as follows:
XmlDocument doc = new XmlDocument();
XmlNode docnode = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
doc.AppendChild(docnode);
doc.AppendChild(doc.CreateProcessingInstruction("xml-stylesheet", "type='text/xsl' href=''"));
XmlElement Ver = doc.CreateElement("Run");
Ver.SetAttribute("version", "3.0");
XmlElement elem = doc.CreateElement("List");
elem.SetAttribute("Name", ObjectName_string);
doc.AppendChild(Ver);
doc.DocumentElement.AppendChild(elem);
doc.Save(#"1.xml");
List<string> data = Event_table.Rows.OfType<DataRow>().Select(dr=>dr.Field<string>(0)).ToList();
using (var writer = new StreamWriter(#"1.xml", append:true))
{
writer.WriteLine("<cmList>");
foreach (var row in data)
{
writer.WriteLine(row);
}
writer.WriteLine("</cmList>");
}
XML FILE: This is how the final result should be
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type='text/xsl' href=''?>
<Run version="3.0" startTime="3.0" endTime="3.0">
<List Name="ABCD" />
<cmList>
**// My Raw data should come here from List<string> data**
</cmList>
</Run>
How can I append the RAW XML data List<string> data in between the element <cmList> I tried doeing a write.BaseStream.Seek but that gives me an error:
Unable seek backward to overwrite data that previously existed in a
file opened in Append mode.
I wrote a blog many moons ago that specifically addressed how to get data from a DataSet/DataTable into XML (for use in Excel specifically). It appears that this may help you. Just exclude the instructions that tell the XML file to open in Excel. It is in VB.NET, so you will have to use the analogous C# code.
DataSet to XML Blog
Maybe I don't understand something in your example but why don't you append raw data before saving xml to file?
XmlDocument doc = new XmlDocument();
XmlNode docnode = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
doc.AppendChild(docnode);
doc.AppendChild(doc.CreateProcessingInstruction("xml-stylesheet", "type='text/xsl' href=''"));
XmlElement Ver = doc.CreateElement("Run");
Ver.SetAttribute("version", "3.0");
XmlElement elem = doc.CreateElement("List");
elem.SetAttribute("Name", ObjectName_string);
XmlElement cmList = doc.CreateElement("cmList");
List<string> data = Event_table.Rows.OfType<DataRow>().Select(dr => dr.Field<string>(0)).ToList();
StringBuilder builder = new StringBuilder();
foreach (var row in data)
{
builder.AppendLine(row);
}
cmList.Value = builder.ToString();
elem.AppendChild(cmList);
Ver.AppendChild(elem);
doc.AppendChild(Ver);
doc.Save(#"1.xml");
// do something with file 1.xml

Read xml string into dataset

I'm getting xml content from a web api. The xml content is a datatable which was converted to xml in the controller and returned as a string.
I try to read it back in a dataset as follows:
var results = response.Content.ReadAsStringAsync().Result;
DataTable dt = new DataTable();
byte[] xmlBytes = Encoding.UTF8.GetBytes(results);
using (DataSet ds = new DataSet())
using (MemoryStream ms = new MemoryStream(xmlBytes))
{
ds.ReadXml(ms);
dt = ds.Tables[0];
}
However is dataset is empty.
The web api controller action does the following:
string xml = string.Empty;
using (DataTable dt = GetResultsAsDataTable())
{
if (dt != null)
{
using (StringWriter sw = new StringWriter())
{
dt.WriteXml(sw, true);
xml = sw.ToString();
}
}
}
The string looks like this:
<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/"><DocumentElement>
<tablename>
<id>734</id>
<name>734</name>
</tablename>
</DocumentElement></string>
Is it normal that the xml string looks like this?
Absolutely not.
You should fix results first:
results = System.Web.HttpUtility.HtmlDecode(results);
Then, results will look like this:
<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/"><DocumentElement>
<tablename>
<id>734</id>
<name>734</name>
</tablename>
</DocumentElement></string>
I think you shall make your controller action return a datatable object, then the Web API framework will convert it to XML automatically by default, but the XML maybe hard to read.
here is the code I tried,C# Code,returned XML

Dataset.ReadXML returns invalid characters in path. Why?

I'm reading a string into a DataSet using the ReadXML method. When I try that it returns an Invalid Characters in the path error. if I save and open the string in IE as an xml file it throws an error on the encoding="UTF-16" line so I assume that is the cause of the problem.
Is there a simple way to fix this? Shouldn't it be able to handle unicode or UTF-16?
Any suggestions would be much appreciated. Using C# & .Net 4
<?xml version="1.0" encoding="UTF-8" ?>
<iCall xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Rows>
<Row>
<Code />
<Title>Test Title</Title>
</Row>
</Rows>
</iCall>
DataSet.ReadXml(string) expects a file path not an xml document. So it tries to parse your xml document as a filepath and fails
if you only have your XML runtime, then you can do like this:
StringReader sr = new StringReader(xml);
dataSet.ReadXml(sr);
It is better to use an extra line XmlTextReader xtr = ... and pass xtr to ReadXml method.
DataSet ds = new DataSet();
StringReader sr = new StringReader(strXml); // or xdoc.InnerXml
XmlTextReader xtr = new XmlTextReader(sr);
ds.ReadXml(xtr);
I think you can try to use ReadStartElement to advance to the next node and read the whole table into DataSet.
XmlTextReader r = new XmlTextReader(#"c:\b.xml");
r.MoveToContent();
r.ReadStartElement("iCall");
DataSet ds = new DataSet();
ds.ReadXml(r);
this.dataGrid1.DataSource = ds;
This sample code will fix the problem.
XmlDocument layoutXml = new XmlDocument();
layoutXml.Load(MyXmlPath); //file location
StringReader sr = new StringReader(layoutXml.DocumentElement.OuterXml);
ds.ReadXml(sr);

Getting a certain node using DataSet

I have the following XML
<xml>
<ObsCont xCampo="field1">
<xTexto>example1</xTexto>
</ObsCont>
<ObsCont xCampo="field2">
<xTexto>example2</xTexto>
</ObsCont>
<ObsCont xCampo="field3">
<xTexto>example3</xTexto>
</ObsCont>
<field>information</field>
</xml>
Is there a way to get the content of "xTexto" inside the ObsCont that has "field2" value for the attribute xCampo using DataSet ?
It would be desireable to have a single liner like the following:
DataSet ds = new DataSet();
ds.ReadXml(StrArquivoProc);
ds.Tables["xml"].Rows[0]["field"].ToString();
//field == "information"
If I use the same method I'm not specifying that I want the one with the desired attribute.
If you have an absolutely known data path then you can use XPath:
Dim myFile = "c:\test.xml"
Dim X As New System.Xml.XmlDocument()
X.Load(myFile)
Dim N = X.SelectSingleNode("//xml/ObsCont[#xCampo=""field2""]/xTexto")
Trace.WriteLine(N.InnerText)
You could certainly use something like Linq to XML to load the entire document into a collection that you could further query or iterate over to use to whatever ends you desire, including binding to data controls. Or you could specifically query the document directly for the desired attribute/element value combination, like below.
XDocument document = XDocument.Parse(xml);
// or document = XDocument.Load(xmlFile)
// System.Xml.Linq namespace
var query = (from obscont in document.Descendants("ObsCont")
where obscont.Attribute("xCampo").Value == "field2"
select obscont.Element("xTexto").Value).First();
Console.WriteLine(query);
Here's the XPathDocument version of Chris Haas' solution, for what it's worth.
Dim myFile = "c:\test.xml"
Dim fs As New FileStream(myFile, FileMode.Open)
Dim doc As New XPathDocument(fs)
fs.Dispose()
Dim nav = doc.CreateNavigator()
Dim node = nav.SelectSingleNode("//xml/ObsCont[#xCampo=""field2""]/xTexto")
Trace.WriteLine(node.Value)
Or if you have the XML in a string, use a StringReader instead.
Dim doc As New XPathDocument(New StringReader(myXml))
'And so forth...'

Categories

Resources