Adding xml tag via code - c#

I'm writing an application in C#. I already an .xml file that is not empty and I want to add new values to it, without deleting the existing values.
I have tried this code:
First:
FileStream docNewUser = new FileStream(#"C:\\MyApp\\MySubDir\\Data\\" + pr + ".xml", FileMode.Open);
XmlTextWriter xmlNewUser = new XmlTextWriter(docNewUser, null);
xmlNewUser.WriteStartDocument();
xmlNewUser.WriteStartElement("RootEl");//root
xmlNewUser.WriteStartElement("Zapis");
xmlNewUser.WriteStartElement("Name");
xmlNewUser.WriteString(txtEnterName.Text);
xmlNewUser.WriteEndElement();
xmlNewUser.WriteEndElement();//end of zapis
this.Close();
Second:
FileStream docNewUser = new FileStream(#"C:\\MyApp\\MySubDir\\Data\\" + pr + ".xml", FileMode.Open);
XmlTextWriter xmlNewUser = new XmlTextWriter(docNewUser, null);
xmlNewUser.WriteStartDocument();
xmlNewUser.WriteStartElement("RootEl");//root-ot
xmlNewUser.WriteStartElement("Zapis");
xmlNewUser.WriteStartElement("Name");
xmlNewUser.WriteString(txtEnterName.Text);
xmlNewUser.WriteEndElement();
xmlNewUser.WriteEndElement();//end of zapis
xmlNewUser.WriteElementString("Ime", null, txtEnterName.Text);
this.Close();
Third:
FileStream docNewUser = new FileStream(#"C:\\MyApp\\MySubDir\\Data\\" + pr + ".xml", FileMode.Open);
XmlTextWriter xmlNewUser = new XmlTextWriter(docNewUser, null);
xmlNewUser.WriteStartDocument();
xmlNewUser.WriteStartElement("Zapis");
xmlNewUser.WriteStartElement("Name");
xmlNewUser.WriteString(txtEnterName.Text);
xmlNewUser.WriteEndElement();
xmlNewUser.WriteEndElement();//end of zapis
xmlNewUser.WriteElementString("Ime", null, txtEnterName.Text);
this.Close();
I think the problem is that the stream doesn’t know where to put the new value.
some more information: the root element is already entered.

If your .NET version supports it, use LINQ to XML. (Caveat: I'm not an expert, and there's probably a more elegant way to write this.)
// Without error handling
var root = XElement.Load(#"C:\Users\TrueWill\Downloads\Foo.xml");
var product =
(from item in root.Elements("item")
where item.Element("name").Value == "Product1"
select item)
.Single();
product.Add(new XElement("size", "small"));
root.Save(#"C:\Users\TrueWill\Downloads\FooCopy.xml");
My test file (before):
<?xml version="1.0" encoding="utf-8"?>
<test>
<item><name>Product1</name></item>
<item><name>Product2</name></item>
</test>
My test file copy (after) (I copied rather than replacing):
<?xml version="1.0" encoding="utf-8"?>
<test>
<item>
<name>Product1</name>
<size>small</size>
</item>
<item>
<name>Product2</name>
</item>
</test>

When you write XML to a file this way, it always overwrites what was in the file before. If you want to write to it using XmlTextWriter, you would have to copy the current content first, and write the new elements at the proper position. Don't forget that you can't read from and write to the same file at the same time, so you have to use a temporary file and overwrite the original with it. Or you read the whole file into a string first. Or write your result into a string first.
But a better solution might be to use XDocument (or XmlDocument) to load the whole document, modify it and then save it. (Doing this is not a good idea if the XML file is huge.)

Use XmlDocument:
XmlDocument doc = new XmlDocument();
doc.Load("filepath");
XmlNode node = doc["MainNode"]["subnode1"]["subnode2"]; //to fetch the node after which you'd like to add something.
XmlElement stuffToAdd = doc.CreateNode("nodename");
stuffToAdd.InnerText = "the value of your added node";
node.AddChild(stuffToAdd);
doc.Save("filepath");
I'm doing this out of memory, so the names of the methods are aproximate.
As said in another answer, loading big XML files using XmlElement can be costy, because it is fully loaded in memory when you call Load().

Is there a reason you are using XmlTextWriter? I find the LINQ to XML alternatives much easier.
your code would be something like below;
XElement el = new XElement("Zapis");
el.Add(new XElement("Name",txtEnterName.Text))
el.Save(#"C:\\MyApp\\MySubDir\\Data\\" + pr + ".xml")

Related

Quickly create XML in C# VS VB.net [duplicate]

Is it possible to add literal XML data within a C# code file? I'm currently using a multiline string literal but it gets messy as you can see. Any better way of doing this?
string XML = #"<?xml version=""1.0"" encoding=""utf-8""?>
<customUI xmlns=""http://schemas.example.com/customui"">
<toolbar id=""save"">
</toolbar>
</customUI>";
XML literals are a feature of VB.NET, not C#.
What you have posted is as close as you can get in C#.
You may want to consider replacing the embedded double quotes with single quotes though (as both types are valid XML).
For larger amounts of XML you may want to consider the answer from Marc - using an XML file (loaded once and stored in memory), so you can take advantage of the XML editor.
If the XML is big enough to get in the way, consider using a flat .xml file instead, either loaded from disk, or embedded as a resource. As long as you only load it once (perhaps in a static constructor) this will make no difference to performance. It will be considerably easier to maintain, as it will use the IDE's XML file editor. And it won't get in the way of your code.
With reference to my comment, I couldn't recall where I saw this, but I finally found the XmlBuilder link.
In retrospect, it seems Linq to XML would be your best bet. It's cleaner, faster and more maintainable than concatenating XML strings:
XNamespace ns = "http://schemas.example.com/customui";
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement(ns + "customUI",
new XElement(ns + "taskbar",
new XAttribute("id", "save"))
)
);
var stringWriter = new StringWriter();
doc.Save(stringWriter); //Write to StringWriter, preserving the declaration (<?xml version="1.0" encoding="utf-16" standalone="yes"?>)
var xmlString = stringWriter.ToString(); //Save as string
doc.Save(#"d:\out.xml"); //Save to file
As a peculiar, and very case-specific solution, if you happen to be working in an ASP.NET environment using the Razor engine, in a CSHTML file you can:
Func<MyType, HelperResult> xml = #<root>
<item>#(item.PropertyA)</item>
<item>#(item.PropertyB)</item>
<item>#(item.PropertyC)</item>
</root>;
With the addition of an extension method:
public static XDocument ToXDocument<T>(this Func<T, HelperResult> source, T item)
{
return XDocument.Parse(source(item).ToHtmlString());
}
You can then:
XDocument document = xml.ToXDocument(new MyType() {
PropertyA = "foo",
PropertyB = "bar",
PropertyC = "qux",
});
Again, peculiar? Yes. Case-specific? Yes. But it works, and gives great Intellisense. (mind you, it also will give a bunch of validity warnings, depending on the document validation version)
The closest we could have in C# would be through LINQ, something like that:
var xml = XDocument.Load(
new StringReader(#"<Books>
<Book author='Dan Brown'>The Da Vinci Code</Book>
<Book author='Dan Brown'>The Lost Symbol</Book>
</Books>"));
var query = from book in xml.Elements("Books").Elements("Book")
where book.Attribute("author").Value == "Dan Brown"
select book.Value;
foreach (var item in query) Console.WriteLine(item);

How to get data from an XML File in C# using XMLDocument class?

Good Evening All, and happy weekend!.
I have been trying all day to understand how to parse my simple XML file so I can understand it enough to write a personal project I want to work on.
I have been reading articles on this site and others but cannot get past where I am :(
My XML Document is ...
<XML>
<User>
<ID>123456789</ID>
<Device>My PC</Device>
</User>
<History>
<CreationTime>27 June 2013</CreationTime>
<UpdatedTime>29 June 2013</UpdatedTime>
<LastUsage>30 June 2013</LastUsage>
<UsageCount>103</UsageCount>
</History>
<Configuration>
<Name>Test Item</Name>
<Details>READ ME</Details>
<Enabled>true</Enabled>
</Configuration>
</XML>
I am trying to get the value in the details element (READ ME). Below is my code
// Start Logging Progress
Console.WriteLine("Test Application - XML Parsing and Creating");
Console.ReadKey();
// Load XML Document
XmlDocument MyDoc = new XmlDocument(); MyDoc.Load(#"E:\MyXML.XML");
// Select Node
XmlNode MyNode = MyDoc.SelectSingleNode("XML/Configuration/Details");
// Output Node Value
Console.WriteLine(String.Concat("Details: ", MyNode.Value));
// Pause
Console.ReadKey();
My console application is running and outputing "Target: " but not giving me the detail within the element.
Can somebody see why this is happening, and perhaps give me advice if I am completely off the wheel? I have no previous knowledge in reading XML files; hence where I am now :)
Thanks! Tom
With the your XPATH expression
// Select Node
XmlNode MyNode = MyDoc.SelectSingleNode("XML/Configuration/Details");
your are selection an element so the type of the MyNode will be XmlElement but the Value of an XmlElement is always null (see on MSDN) so you need to use XmlElement.InnerText or XmlElement.InnerXml isntead.
So the changed your code to
// Output Node Value
Console.WriteLine(String.Concat("Details: ", MyNode.InnerText));
Or you can select the content of an element with using the XPATH text() function, in this case MyNode will be XmlText where you get its value with Value:
// Select Node
XmlNode MyNode = MyDoc.SelectSingleNode("XML/Configuration/Details/text()");
// Output Node Value
Console.WriteLine(String.Concat("Details: ", MyNode.Value));
As a sidenote if you are anyway learning XML manipulation in C# you should check out LINQ to XML which is another/newer way to working with XML in C#.
Just for interest, a little-known "simple" syntax is this:
XmlDocument myDoc = new XmlDocument();
myDoc.Load(#"D:\MyXML.XML");
string details = myDoc["XML"]["Configuration"]["Details"].InnerText;
Note that this (and the XPath approach) could go pop if your XML doesn't conform to the structure you're expecting, so you'd ideally put some validation in there as well.
U can use Xpath library for that (u must include "System.Xml.XPath"):
XmlDocument document = new XmlDocument();
document.Load("MyXml.xml");
XPathNavigator navigator = document.CreateNavigator();
foreach (XPathNavigator nav in navigator.Select("//Details"))
{
Console.WriteLine(nav.Value);
}
the above code iterate over every node called (Details) extracting information and print it.
If you want to retrieve a particular value from an XML file
XmlDocument _LocalInfo_Xml = new XmlDocument();
_LocalInfo_Xml.Load(fileName);
XmlElement _XmlElement;
_XmlElement = _LocalInfo_Xml.GetElementsByTagName("UserId")[0] as XmlElement;
string Value = _XmlElement.InnerText;
Value contains the text value

Split XML document apart creating multiple output files from repeating elements

I need to take an XML file and create multiple output xml files from the repeating nodes of the input file. The source file "AnimalBatch.xml" looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<Animals>
<Animal id="1001">
<Quantity>One</Quantity>
<Adjective>Red</Adjective>
<Name>Rooster</Name>
</Animal>
<Animal id="1002">
<Quantity>Two</Quantity>
<Adjective>Stubborn</Adjective>
<Name>Donkeys</Name>
</Animal>
<Animal id="1003">
<Quantity>Three</Quantity>
<Color>Blind</Color>
<Name>Mice</Name>
</Animal>
</Animals>
The program needs to split the repeating "Animal" and produce 3 files named: Animal_1001.xml, Animal_1002.xml, and Animal_1003.xml
Each output file should contain just their respective element (which will be the root). The id attribute from AnimalsBatch.xml will supply the sequence number for the Animal_xxxx.xml filenames. The id attribute does not need to be in the output files.
Animal_1001.xml:
<?xml version="1.0" encoding="utf-8"?>
<Animal>
<Quantity>One</Quantity>
<Adjective>Red</Adjective>
<Name>Rooster</Name>
</Animal>
Animal_1002.xml
<?xml version="1.0" encoding="utf-8"?>
<Animal>
<Quantity>Two</Quantity>
<Adjective>Stubborn</Adjective>
<Name>Donkeys</Name>
</Animal>
Animal_1003.xml>
<?xml version="1.0" encoding="utf-8"?>
<Animal>
<Quantity>Three</Quantity>
<Adjective>Blind</Adjective>
<Name>Mice</Name>
</Animal>
I want to do this with XmlDocument, since it needs to be able to run on .Net 2.0.
My program looks like this:
static void Main(string[] args)
{
string strFileName;
string strSeq;
XmlDocument doc = new XmlDocument();
doc.Load("D:\\Rick\\Computer\\XML\\AnimalBatch.xml");
XmlNodeList nl = doc.DocumentElement.SelectNodes("Animal");
foreach (XmlNode n in nl)
{
strSeq = n.Attributes["id"].Value;
XmlDocument outdoc = new XmlDocument();
XmlNode rootnode = outdoc.CreateNode("element", "Animal", "");
outdoc.AppendChild(rootnode); // Put the wrapper element into outdoc
outdoc.ImportNode(n, true); // place the node n into outdoc
outdoc.AppendChild(n); // This statement errors:
// "The node to be inserted is from a different document context."
strFileName = "Animal_" + strSeq + ".xml";
outdoc.Save(Console.Out);
Console.WriteLine();
}
Console.WriteLine("END OF PROGRAM: Press <ENTER>");
Console.ReadLine();
}
I think I have 2 problems.
A) After doing the ImportNode on node n into outdoc, I call outdoc.AppendChild(n) which complains: "The node to be inserted is from a different document context." I do not know if this is a scope issue referencing node n within the ForEach loop - or if I am somehow not using ImportNode() or AppendChild properly. 2nd argument on ImportNode() is set to true, because I want the child elements of Animal (3 fields arbitrarily named Quantity, Adjective, and Name) to end up in the destination file.
B) Second problem is getting the Animal element into outdoc. I'm getting '' but I need ' ' so I can place node n inside it. I think my problem is how I am doing: outdoc.AppendChild(rootnode);
To show the xml, I'm doing: outdoc.Save(Console.Out); I do have the code to save() to an output file - which does work, as long as I can get outdoc assembled properly.
There is a similar question at: Split XML in Multiple XML files, but I don't understand the solution code yet. I think I'm pretty close on this approach, and will appreciate any help you can provide.
I'm going to be doing this same task using XmlReader, since I'm going to need to be able to handle large input files, and I understand that XmlDocument reads the whole thing in and can cause memory issues.
That's a simple method that seems what you are looking for
public void test_xml_split()
{
XmlDocument doc = new XmlDocument();
doc.Load("C:\\animals.xml");
XmlDocument newXmlDoc = null;
foreach (XmlNode animalNode in doc.SelectNodes("//Animals/Animal"))
{
newXmlDoc = new XmlDocument();
var targetNode = newXmlDoc.ImportNode(animalNode, true);
newXmlDoc.AppendChild(targetNode);
newXmlDoc.Save(Console.Out);
Console.WriteLine();
}
}
This approach seems to work without using the "var targetnode" statement. It creates an XmlNode object called targetNode from outdoc's "Animal" element in the ForEach loop. I think the main things that were problems in my original code were: A) I was getting nodelist nl incorrectly. And B) I couldn't "Import" node n, I think because it was associated specifically with doc. It had to be created as its own Node.
The problem with the prior proposed solution was the use of the "var" keyword. My program has to assume 2.0 and that came in with v3.0. I like Rogers solution, in that it is concise. For me - I wanted to do each thing as a separate statement.
static void SplitXMLDocument()
{
string strFileName;
string strSeq;
XmlDocument doc = new XmlDocument(); // The input file
doc.Load("D:\\Rick\\Computer\\XML\\AnimalBatch.xml");
XmlNodeList nl = doc.DocumentElement.SelectNodes("//Animals/Animal");
foreach (XmlNode n in nl)
{
strSeq = n.Attributes["id"].Value; // Animal nodes have an id attribute
XmlDocument outdoc = new XmlDocument(); // Create the outdoc xml document
XmlNode targetNode = outdoc.CreateElement("Animal"); // Create a separate node to hold the Animal element
targetNode = outdoc.ImportNode(n, true); // Bring over that Animal
targetNode.Attributes.RemoveAll(); // Remove the id attribute in <Animal id="1001">
outdoc.ImportNode(targetNode, true); // place the node n into outdoc
outdoc.AppendChild(targetNode); // AppendChild to make it stick
strFileName = "Animal_" + strSeq + ".xml";
outdoc.Save(Console.Out); Console.WriteLine();
outdoc.Save("D:\\Rick\\Computer\\XML\\" + strFileName);
Console.WriteLine();
}
}

Load info from xml, save related (changed) info using c#

Friends,
My school project is having an xml data file:
<patients>
<patient>
<regNo>2012/Mar/003</regNo>
<name>Jhon</name>
<add>Somewhere</add>
<mobile>0000</mobile>
.
.
.
<stay>2</stay>
<costofroom>100</costofroom>
<total>200</total>
</patient>
</patients>
My Windowsform "EditPatients_Load" is able to fetch all info of patient Jhon, and now let's assume that the Admin needs to change some information in the form & resubmit.
Then how to write back all values to Jhon's account in the same xml
file????
I'm not able to makeup the logical code, even if I check the node if (patients.paptient.name = "nameComboBox.text").... how to make sure that I'm writing other values on proper place?
Rgrdz,
Try this:
//string xml =
//#"<patients><patient><regNo>2012/Mar/003</regNo><name>Jhon</name><add>Somewhere
//</add><mobile>0000</mobile><stay>2</stay><costofroom>100</costofroom><total>200</total>
//</patient></patients>";
XDocument xmlDoc = XDocument.Load(#"c:\abc.xml");
var items = (from item in xmlDoc.Descendants("patient")
where item.Element("name").Value == "Jhon"
select item);
if (items.Count() > 0)
{
var item = items.First();
item.SetElementValue("add", "New New Address");
xmlDoc.Save(#"c:\abc.xml", SaveOptions.None);
}
You can get single element using
var item = (from item in xmlDoc.Descendants("patient")
where item.Element("name").Value == "Jhon"
select item).FirstOrDefault();
then update it using SetElementValue() method.
//Updated Xml
<?xml version="1.0" encoding="utf-8"?>
<patients>
<patient>
<regNo>2012/Mar/003</regNo>
<name>Jhon</name>
<add>New Address</add>
<mobile>0000</mobile>
<stay>2</stay>
<costofroom>100</costofroom>
<total>200</total>
</patient>
</patients>
Reference:
Update XML with C# using Linq
I would take the xml serialization/deserialization route to solve this:
http://support.microsoft.com/kb/815813
How to Deserialize XML document
That way you can work with objects and not have to parse xml files manually.
If you're using .NET 3.5 onward you can use the XDocument class like the following. I'm assuming your content is in a .xml file.
XDocument xdoc = XDocument.Load(#"C:\Tmp\test.xml");
//this would ensure you get the right node and set its text content/value
xdoc.Element("patients")
.Element("patient").Element("add").Value = "some new address?";
xdoc.Save(#"C:\Tmp\test.xml");
The file test.xml would change to:
<patients>
<patient>
<regNo>2012/Mar/003</regNo>
<name>Jhon</name>
<add>some new address?</add>
<mobile>0000</mobile>
<stay>2</stay>
<costofroom>100</costofroom>
<total>200</total>
</patient>
</patients>

Use XML Literals in C#?

Is it possible to add literal XML data within a C# code file? I'm currently using a multiline string literal but it gets messy as you can see. Any better way of doing this?
string XML = #"<?xml version=""1.0"" encoding=""utf-8""?>
<customUI xmlns=""http://schemas.example.com/customui"">
<toolbar id=""save"">
</toolbar>
</customUI>";
XML literals are a feature of VB.NET, not C#.
What you have posted is as close as you can get in C#.
You may want to consider replacing the embedded double quotes with single quotes though (as both types are valid XML).
For larger amounts of XML you may want to consider the answer from Marc - using an XML file (loaded once and stored in memory), so you can take advantage of the XML editor.
If the XML is big enough to get in the way, consider using a flat .xml file instead, either loaded from disk, or embedded as a resource. As long as you only load it once (perhaps in a static constructor) this will make no difference to performance. It will be considerably easier to maintain, as it will use the IDE's XML file editor. And it won't get in the way of your code.
With reference to my comment, I couldn't recall where I saw this, but I finally found the XmlBuilder link.
In retrospect, it seems Linq to XML would be your best bet. It's cleaner, faster and more maintainable than concatenating XML strings:
XNamespace ns = "http://schemas.example.com/customui";
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement(ns + "customUI",
new XElement(ns + "taskbar",
new XAttribute("id", "save"))
)
);
var stringWriter = new StringWriter();
doc.Save(stringWriter); //Write to StringWriter, preserving the declaration (<?xml version="1.0" encoding="utf-16" standalone="yes"?>)
var xmlString = stringWriter.ToString(); //Save as string
doc.Save(#"d:\out.xml"); //Save to file
As a peculiar, and very case-specific solution, if you happen to be working in an ASP.NET environment using the Razor engine, in a CSHTML file you can:
Func<MyType, HelperResult> xml = #<root>
<item>#(item.PropertyA)</item>
<item>#(item.PropertyB)</item>
<item>#(item.PropertyC)</item>
</root>;
With the addition of an extension method:
public static XDocument ToXDocument<T>(this Func<T, HelperResult> source, T item)
{
return XDocument.Parse(source(item).ToHtmlString());
}
You can then:
XDocument document = xml.ToXDocument(new MyType() {
PropertyA = "foo",
PropertyB = "bar",
PropertyC = "qux",
});
Again, peculiar? Yes. Case-specific? Yes. But it works, and gives great Intellisense. (mind you, it also will give a bunch of validity warnings, depending on the document validation version)
The closest we could have in C# would be through LINQ, something like that:
var xml = XDocument.Load(
new StringReader(#"<Books>
<Book author='Dan Brown'>The Da Vinci Code</Book>
<Book author='Dan Brown'>The Lost Symbol</Book>
</Books>"));
var query = from book in xml.Elements("Books").Elements("Book")
where book.Attribute("author").Value == "Dan Brown"
select book.Value;
foreach (var item in query) Console.WriteLine(item);

Categories

Resources