how to use XMLTextWriter to write XML node at a specified position - c#

I have an xml file likes below.
<?xml version="1.0" encoding="utf-8" ?>
<Book>
<Title>Title</Title>
<Content>Content</Content>
</Book>
I want to write a new node after 'Content', I know how to use XMLDocument to do that, is there a way to use XMLTextWriter to do that?

You will have to write the whole Xml document, i.e. all elements and attributes and attribute values by using the XmlTextWriter. After you've written the <Content> element, you can write your additional element.
Something like this:
writer.WriteStartDocument();
writer.WriteStartElement("Book");
writer.WriteStartElement("Title");
writer.WriteString("Title");
writer.WriteEndElement();
writer.WriteStartElement("Content");
writer.WriteString("Content");
writer.WriteEndElement();
// insert your new data here
writer.WriteEndElement();
writer.WriteEndDocument();

Related

How do I add a CDATA to an already existing ElementString? (C#)

I have my XML file setup like this:
<Entry>
<Desired_due_date>date</Desired_due_date>
<Order_number>order</Order_number>
<Series_number>series</Series_number>
<Product_name>product</Product_name>
</Entry>
I want to add CDATA without dropping the tags f.e.:
<Product_name>[CDATA[Z19 7HW]]</Product_name>
Every solution I found either makes a new node which messes up my structure or drops the tags.
My code looks something like this:
writer.WriteComment("Results in XML format.");
writer.WriteStartElement("Entries");
writer.WriteElementString("Series_number", entries[i].Series_number);
writer.WriteElementString("End_date_2", entries[i].End_date2);
writer.WriteElementString("Norm", entries[i].Norm);
writer.WriteEndElement();
writer.WriteEndDocument();
writer.Flush();
writer.Close();
Do not use XmlWriter.WriteElementString. Instead:
writer.WriteStartElement("elementname");
writer.WriteCData("text that goes in CDATA section");
writer.WriteEndElement();

How to parse a XML with nested XML text

Trying to read XML file with nested XML object with own XML declaration. As expected got exception:
Unexpected XML declaration. The XML declaration must be the first node in the document, and no white space characters are allowed to appear before it.
How can i read that specific element as text and parse it as separate XML document for later deserialization?
<?xml version="1.0" encoding="UTF-8"?>
<Data>
<Items>
<Item>
<Target type="System.String">Some target</Target>
<Content type="System.String"><?xml version="1.0" encoding="utf-8"?><Data><Items><Item><surname type="System.String">Some Surname</surname><name type="System.String">Some Name</name></Item></Items></Data></Content>
</Item>
</Items>
</Data>
Every approach i'm trying fail due to declaration exception.
var xml = System.IO.File.ReadAllText("Info.xml");
var xDoc = XDocument.Parse(xml); // Exception
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xml); // Exception
var xmlReader = XmlReader.Create(new StringReader(xml));
xmlReader.ReadToFollowing("Content"); // Exception
I have no control over XML creation.
The only way I would know is by getting rid of the illegal second <?xml> declaration. I wrote a sample that will simply look for and discard the second <?xml>. After that the string has become valid XML and can be parsed. You may need to tweak it a bit to make it work for your exact scenario.
Code:
using System;
using System.Xml;
public class Program
{
public static void Main()
{
var badXML = #"<?xml version=""1.0"" encoding=""UTF-8""?>
<Data>
<Items>
<Item>
<Target type=""System.String"">Some target</Target>
<Content type=""System.String""><?xml version=""1.0"" encoding=""utf-8""?><Data><Items><Item><surname type=""System.String"">Some Surname</surname><name type=""System.String"">Some Name</name></Item></Items></Data></Content>
</Item>
</Items>
</Data>";
var goodXML = badXML.Replace(#"<Content type=""System.String""><?xml version=""1.0"" encoding=""utf-8""?>"
, #"<Content type=""System.String"">");
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(goodXML);
XmlNodeList itemRefList = xmlDoc.GetElementsByTagName("Content");
foreach (XmlNode xn in itemRefList)
{
Console.WriteLine(xn.InnerXml);
}
}
}
Output:
<Data><Items><Item><surname type="System.String">Some Surname</surname><name type="System.String">Some Name</name></Item></Items></Data>
Working DotNetFiddle: https://dotnetfiddle.net/ShmZCy
Perhaps needless to say: all of this would not have been needed if the thing that created this invalid XML would have applied the common rule to wrap the nested XML in a <![CDATA[ .... ]]> block.
The <?xml ...?> processing declaration is only valid on the first line of an XML document, and so the XML that you've been given isn't well-formed XML. This will make it quite difficult to parse as is without either changing the source document (and you've indicated that's not possible) or preprocessing the source.
You could try:
Stripping out the <?xml ?> instruction with regex or string manipulation, but the cure there may be worse than the disease.
The HTMLAgilityPack, which implements a more forgiving parser, may work with an XML document
Other than that, the producer of the document should look to produce well-formed XML:
CDATA sections can help this, but be aware that CDATA can't contain the ]]> end tag.
XML escaping the XML text can work fine; that is, use the standard routines to turn < into < and so forth.
XML namespaces can also help here, but they can be daunting in the beginning.

Write to existing xml file without replacing it's contents (XmlWriter)

I encountered following issue,
I first write to my xml file like this:
XmlTextWriter writer = new XmlTextWriter("course.xml", null);
writer.Formatting = Formatting.Indented;
writer.WriteStartDocument();
writer.WriteStartElement("Course");
writer.WriteAttributeString("title", "Examle");
writer.WriteAttributeString("started", "true");
writer.WriteEndElement();
writer.WriteEndDocument();
writer.Close();
And xml output I get is:
<?xml version="1.0"?>
<Course title="Example" started="true" />
After that I want to write more data to this xml file so I use my code again:
XmlTextWriter writer = new XmlTextWriter("course.xml", null);
writer.Formatting = Formatting.Indented;
writer.WriteStartDocument();
writer.WriteStartElement("Course");
writer.StartElement("Level");
writer.StartElement("Module");
writer.EndElement();
writer.EndElement();
writer.WriteEndElement();
writer.WriteEndDocument();
writer.Close();
And the xml output is:
<?xml version="1.0"?>
<Course>
<Level>
<Module>
</Module>
</Level>
</Course>
So it replaces my original data, and all attributes in the course tag. Therefore I need a way where it doesn't replace data, but instead add it inside existing tags.
XML files are just sequential text files. They are not a database or a random-access file. There is no way to just write into the middle of them.
I would recommend parsing the entire xml document into memory, adding the new data, and then writing the whole document back out again.

Process namespaces using XmlReader

I have a complex XML file with structure as follows:
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="xxx:xxx:xxx:xxx:xxxxx:xxx:xsd:xxxx.xxx.xxx.xx">
<Element1>
<Element2>
<Element2A>xxxxxx</Element2A>
<Element2B>2012-08-29T00:00:00</Element2B>
</Element2>
</Element1>
</Document>
Now I am using XmlReader to read this XML document and process information as follows
XmlReader xr = XmlReader.Create(filename);
while (xr.Read())
{
xr.MoveToElement();
XElement node = (XElement)XElement.ReadFrom(xr);
Console.WriteLine(node.Name);
}
xr.Close();
The problem I am facing is in the output the namespace is prefixed to the ElementName. E.g output
{xxx:xxx:xxx:xxx:xxxxx:xxx:xsd:xxxx.xxx.xxx.xx}Element1
Is there any way I can remove/ handle this as I need to do further filtering using Element names and Child names.
XElement.Name is not (as you might expect) a String, but rather an XName which has a LocalName property, thus:
Console.WriteLine(node.Name.LocalName);
You may want to remove the namespace. One way to remove namespace is to write c# code and other way is to use XSLT transformation as suggested in Remove Namespace
-Milind

Editing an XML file?

How do I add another element/childnode to a specific parent node in an XML file?
Specifically a new video object to the media node.
I want to turn this:
<?xml version="1.0" encoding="utf-8" ?>
<media>
<Video name="Gladiator">
<English>path1</English>
<Chinese>path2</Cinese>
<French>path3</French>
</Video>
<Video name="Transformers">
<English>path4</English>
<Chinese>path5</Cinese>
<French>path6</French>
</Video>
</media>
into this:
<?xml version="1.0" encoding="utf-8" ?>
<media>
<Video name="Gladiator">
<English>path1</English>
<Chinese>path2</Cinese>
<French>path3</French>
</Video>
<Video name="Transformers">
<English>path4</English>
<Chinese>path5</Cinese>
<French>path6</French>
</Video>
<Video name="Terminator">
<English>path7</English>
<Chinese>path8</Cinese>
<French>path9</French>
</Video>
</media>
If I open an xmlTextwriter, create a new element tag, add attributes and end the element tag; it deletes all previous data in the text file :/
If you use the class XmlTextWriter, you need to read your xml file to get the content before using XmlTextWriter. XmlTextWriter does not load the content of your xml file. That's why all your previous data are gone.
XmlDocument is the simplest way to add a new node.
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(filePath);
XmlNode node = FindYourNode(xmlDoc); //Method to find the specific node
node.AppendChild(yourNewXmlNode);
xmlDoc.Save(filePath);
If your xml file is small, the class XmlDocument is perfectly fine. But if you have to manipulate a big xml file, I would suggest to use another class because XmlDocument can hurt your performance.
In that case, I would use a combination of XmlReader and XmlWriter.
I would do something along these lines:
mediaElement.AppendChild(xmlDocument.CreateElement("Video"))
Where mediaElement is a reference to the <media/> element and xmlDocument is of the type XmlDocument.

Categories