How to add data to an existing xml file in c# - c#

I'm using this c# code to write data to xml file:
Employee[] employees = new Employee[2];
employees[0] = new Employee(1, "David", "Smith", 10000);
employees[1] = new Employee(12, "Cecil", "Walker", 120000);
using (XmlWriter writer = XmlWriter.Create("employees.xml"))
{
writer.WriteStartDocument();
writer.WriteStartElement("Employees");
foreach (Employee employee in employees)
{
writer.WriteStartElement("Employee");
writer.WriteElementString("ID", employee.Id.ToString());
writer.WriteElementString("FirstName", employee.FirstName);
writer.WriteElementString("LastName", employee.LastName);
writer.WriteElementString("Salary", employee.Salary.ToString());
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.WriteEndDocument();
}
Now suppose I restart my application and I want to add new data to the xml file without losing the existed data, using the same way will overwrite the data on my xml file, I tried to figure out how to do that and I searched for a similar example but I couldn't come to anything , any ideas ??

Perhaps you should look at some examples using datasets and xml:
http://www.codeproject.com/Articles/13854/Using-XML-as-Database-with-Dataset
or use System.Xml.Serialization.XmlSerializer, when you dont't have amount of records.
Example using XmlDocument
XmlDocument xd = new XmlDocument();
xd.Load("employees.xml");
XmlNode nl = xd.SelectSingleNode("//Employees");
XmlDocument xd2 = new XmlDocument();
xd2.LoadXml("<Employee><ID>20</ID><FirstName>Clair</FirstName><LastName>Doner</LastName><Salary>13000</Salary></Employee>");
XmlNode n = xd.ImportNode(xd2.FirstChild,true);
nl.AppendChild(n);
xd.Save(Console.Out);

Using an xml writer for small amounts of data is awkward. You would be better of using an XDocument that you either initialize from scratch for the first run, or read from an existing file in subsequent runs.
Using XDocument you can manipulate the XML with XElement and XAttribute instances and then write the entire thing out to a file when you want to persist it.

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

Convert a really-large collection to Xml in .NET

I have a database result (as a list of items) that is huge. 450K items are returned.
I then use Linq-To-Xml to convert this collection to an XDocument. It works - but it's a huge memory hit.
Is it possible to convert the collection items to XML but not all in memory, at once .. but streaming to a file as the conversation happens?
The XML data finally saves to disk at over 1Gig. So it's fair to assume that the memory consumption will at least be this.
So - is there a way to stream the XML segments to disk, as we iterate over each item in the list instead of converting the entire result-set to an InMemory XDocument and then saving this to disk?
NOTE: Please do not make suggestions about breaking up the list into smaller parts, etc. I understand that, but I've ruled that out.
Here's some sample code I'm doing (to help give you an idea).
// Create the xml doc.
var elements = from user in userResults
select new XElement("user",
new XElement("id", user.Id),
.....<snip>...... );
return new XDocument(new XDeclaration("1.0", "utf-8", "yes"),
new XElement("users", elements));
// Save the doc to the filesystem.
using (var writer = _fileSystemWrapper.CreateText(destinationXmlFileName))
{
xmldDocument.Save(writer);
}
Update
Maybe there's some other tricks like using Linq-To-Xml to create element segments in batches of 10 .. and for each 10, append that to the end of a file?
You can use XStreamingElement. It would require some small refactoring though, mainly not creating XDocument and using different Save method.
Here is the corresponding sample to yours just to give you an idea:
var elements = from user in userResults
select new XElement("user",
new XElement("id", user.Id),
.....<snip>...... );
var content = new XStreamingElement("users", elements);
using (var output = _fileSystemWrapper.CreateText(destinationXmlFileName))
using (var writer = XmlWriter.Create(output, new XmlWriterSettings { Encoding = Encoding.UTF8, Indent = true })
{
// Use the next line if you don't require standalone="yes" attribute
// content.Save(writer);
writer.WriteStartDocument(true);
content.WriteTo(writer);
writer.WriteEndDocument();
}

Xml within an Xml

I basically want to know how to insert a XmlDocument inside another XmlDocument.
The first XmlDocument will have the basic header and footer tags.
The second XmlDocument will be the body/data tag which must be inserted into the first XmlDocument.
string tableData = null;
using(StringWriter sw = new StringWriter())
{
rightsTable.WriteXml(sw);
tableData = sw.ToString();
}
XmlDocument xmlTable = new XmlDocument();
xmlTable.LoadXml(tableData);
StringBuilder build = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(build, new XmlWriterSettings { OmitXmlDeclaration = true }))
{
writer.WriteStartElement("dataheader");
//need to insert the xmlTable here somehow
writer.WriteEndElement();
}
Is there an easier solution to this?
Use importNode feature in your document parser.
You can use this code based on CreateCDataSection method
// Create an XmlCDataSection from your document
var cdata = xmlTable.CreateCDataSection("<test></test>");
XmlElement root = xmlTable.DocumentElement;
// Append the cdata section to your node
root.AppendChild(cdata);
Link : http://msdn.microsoft.com/fr-fr/library/system.xml.xmldocument.createcdatasection.aspx
I am not sure what you are really looking for but this can show how to merge two xml documents (using Linq2xml)
string xml1 =
#"<xml1>
<header>header1</header>
<footer>footer</footer>
</xml1>";
string xml2 =
#"<xml2>
<body>body</body>
<data>footer</data>
</xml2>";
var xdoc1 = XElement.Parse(xml1);
var xdoc2 = XElement.Parse(xml2);
xdoc1.Descendants().First(d => d.Name == "header").AddAfterSelf(xdoc2.Elements());
var newxml = xdoc1.ToString();
OUTPUT
<xml1>
<header>header1</header>
<body>body</body>
<data>footer</data>
<footer>footer</footer>
</xml1>
You will need to write the inner XML files in CDATA sections.
Use writer.WriteCData for such nodes, passing in the inner XML as text.
writer.WriteCData(xmlTable.OuterXml);
Another option (thanks DJQuimby) is to encode the XML to some XML compatible format (say base64) - note that the encoding used must be XML compatible and that some encoding schemes will increase the size of the encoded document (base64 adds ~30%).

Save xml string or XmlNode to text file in indent format?

I have an xml string which is very long in one line. I would like to save the xml string to a text file in a nice indent format:
<root><app>myApp</app><logFile>myApp.log</logFile><SQLdb><connection>...</connection>...</root>
The format I prefer:
<root>
<app>myApp</app>
<logFile>myApp.log</logFile>
<SQLdb>
<connection>...</connection>
....
</SQLdb>
</root>
What are .Net libraries available for C# to do it?
This will work for what you want to do ...
var samp = #"<root><app>myApp</app><logFile>myApp.log</logFile></root>";
var xdoc = XDocument.Load(new StringReader(samp), LoadOptions.None);
xdoc.Save(#"c:\temp\myxml.xml", SaveOptions.None);
Same result with System.Xml namespace ...
var xdoc = new XmlDocument();
xdoc.LoadXml(samp);
xdoc.Save(#"c:\temp\myxml.xml");
I'm going to assume you don't mean that you have a System.String instance with some XML in it, and I'm going to hope you don't create it via string manipulation.
That said, all you have to do is set the proper settings when you create your XmlWriter:
var sb = new StringBuilder();
var settings = new XmlWriterSettings {Indent = true};
using (var writer = XmlWriter.Create(sb, settings))
{
// write your XML using the writer
}
// Indented results available in sb.ToString()
Just another option:
using System.Xml.Linq;
public string IndentXmlString(string xml)
{
XDocument doc = XDocument.Parse(xml);
return doc.ToString();
}

Categories

Resources