Reading XML comments in C# - c#

I have got some XML files that contain comments above the nodes. When I am reading the file in, as part of the process I would like to get the comment out as well. I know you can write a comment to the file using XmlComment, but not sure how to read them back out.
My XML looks similar to this:
<Objects>
<!--Comment about node-->
<GUID-bf2401c0-ef5e-4d20-9d20-a2451a199362>
<info job="SAVE" person="Joe" />
<info job="SAVE" person="Sally" />
</GUID-bf2401c0-ef5e-4d20-9d20-a2451a199362>
<!--Another Comment about node-->
<GUID-bf2401c0-ef5e-4d20-9d20-a5844113284112>
<info job="SAVE" person="John" />
<info job="SAVE" person="Julie" />
</GUID-bf2401c0-ef5e-4d20-9d20-a5844113284112>

Try this:
XmlReaderSettings readerSettings = new XmlReaderSettings();
readerSettings.IgnoreComments = false;
using (XmlReader reader = XmlReader.Create("input.xml", readerSettings))
{
XmlDocument myData = new XmlDocument();
myData.Load(reader);
// etc...
}
To read comments:
XmlReader xmlRdr = XmlReader.Create("Test.XML");
// Parse the file
while (xmlRdr.Read())
{
switch (xmlRdr.NodeType)
{
case XmlNodeType.Element:
// You may need to capture the last element to provide a context
// for any comments you come across... so copy xmlRdr.Name, etc.
break;
case XmlNodeType.Comment:
// Do something with xmlRdr.value

Using System.Xml.Linq:
var doc = XElement.Load(fileName);
var comments = doc.DescendantNodes().OfType<XComment>();
foreach (XComment comment in comments)
...

They are a part of the child nodes of the containing node as all other nodes: http://msdn.microsoft.com/en-us/library/system.xml.xmlcomment.aspx

I know the question is very old, but yesterday I had the same problem. So here is my solution:
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreWhitespace = false;
settings.IgnoreComments = false;
XmlReaderSettings settings2 = new XmlReaderSettings();
settings2.IgnoreWhitespace = false;
settings2.IgnoreComments = false;
XmlReader xmlreaderOriginalCfg = XmlReader.Create(#"C:\...xml", settings);
XmlReader xmlreaderVerificationCfg = XmlReader.Create(#"C:\....xml", settings);
XmlDocument myData = new XmlDocument();
myData.Load(xmlreaderOriginalCfg);
XmlDocument myData2 = new XmlDocument();
myData2.Load(xmlreaderVerificationCfg);
XmlNode parentNode = myData.SelectSingleNode("/configuration/appSettings");
foreach (XmlComment comment in myData2.SelectNodes("//comment()"))
{
XmlComment importedCom = myData.CreateComment(comment.Value);
parentNode.AppendChild(importedCom);
foreach (XmlNode node in myData2.DocumentElement.SelectNodes("/configuration/appSettings/add"))
{
XmlNode imported = myData.ImportNode(node, true);
parentNode.AppendChild(imported);
}
}
myData.Save(this.pathNew);
Maybe it helps somebody

I stored your XML into a file, here is the code sample.
XmlDocument document = new XmlDocument();
document.Load("test.xml");
foreach (XmlComment comment in document.SelectNodes("//comment()"))
{
Console.WriteLine("Comment: \"{0}\".", comment.Value);
}

Some sample code on how to access comments hope this helps
using System;
using System.IO;
using System.Xml;
public class Sample {
public static void Main() {
XmlDocument doc = new XmlDocument();
doc.LoadXml(#"<Objects><!--Comment about node--><othernode/><!--Some more comment--></Objects>");
XmlNode root = doc.FirstChild;
if (root.HasChildNodes)
{
for (int i=0; i<root.ChildNodes.Count; i++)
{
if( root.ChildNodes[i] is XmlComment)
Console.WriteLine(root.ChildNodes[i].InnerText);
}
}
}
}

Related

iterating XmlDocument with xpath

How do we iterate an XmlDocument using an xpath?
I'm attempting to return a list of nodes by xpath:
public static List<string> Filter(string xpath, string input, string ns, string nsUrl)
{
var bytes = Encoding.UTF8.GetBytes(input); //i believe this unescapes the string
var stream = new MemoryStream(bytes);
var doc = new XmlDocument();
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(doc.NameTable);
namespaceManager.AddNamespace(ns, nsUrl);
var links = new List<string>();
var nodes = doc.SelectNodes(xpath, namespaceManager);
using (var reader = new XmlTextReader(stream))
{
reader.Namespaces = false;
doc.Load(reader);
}
foreach (XmlNode node in nodes)
{
if (IsNullOrWhiteSpace(node.InnerText))
{
continue;
}
links.Add(node.InnerText);
}
return links;
}
however, the count is always 0 !
I'm using this xpath. notice how i am using only 1 namespace:
/ns0:Visit/ns0:DocumentInterface/ns0:Documents/ns0:Document/ns0:BinaryData
The header of the file looks like this:
<ns0:Visit xmlns:ns0="http://NameSpace.ExternalSchemas.Patient"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
I'm certain that I am using the right xpath because I tested it against my payload:
I'm calling the function this way:
var links = Filter(xpath, xml, "ns0", "http://NameSpace.ExternalSchemas.Patient");
How do we iterate an XmlDocument using an xpath? Perhaps the XmlDocument should be an XDocument instead?

C# append object to xml file using serialization

I am trying append a serialized object to an existing xml file beneath the root element, which I thought would be simple but is proving to be a little challenging.
The problem is in the AddShortcut method but I added some more code for completeness.
I believe what I need to do is:
load the file into an XmlDocument.
navigate to the node I want to append beneath (here the node name is Shortcuts).
create some type of writer and then serialize the object.
save the XmlDocument.
The trouble is in steps 2 and 3. I have tried different variations but I think using XPathNavigator somehow to find the "root" node to append under is a step in the right direction.
I have also looked at almost every question on Stack Overflow on the subject.
Any suggestions welcome. Here is my code
class XmlEngine
{
public string FullPath { get; set; } // the full path to the xmlDocument
private readonly XmlDocument xDoc;
public XmlEngine(string fullPath, string startElement, string[] rElements)
{
FullPath = fullPath;
xDoc = new XmlDocument();
CreateXmlFile(FullPath, startElement, rElements);
}
public void CreateXmlFile(string path, string startElement, string[] rElements)
{
try
{
if (!File.Exists(path))
{
// create a txt writer
XmlTextWriter wtr = new XmlTextWriter(path, System.Text.Encoding.UTF8);
// make sure the file is well formatted
wtr.Formatting = Formatting.Indented;
wtr.WriteProcessingInstruction("xml", "version='1.0' encoding='UTF-8'");
wtr.WriteStartElement(startElement);
wtr.Close();
// write the top level root elements
writeRootElements(path, rElements);
}
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
Console.WriteLine("Could not create file: " + path);
}
}
public void AddShortcut(Shortcut s)
{
xDoc.Load(FullPath);
rootNode = xDoc.AppendChild(xDoc.CreateElement("Shortcuts"));
var serializer = new XmlSerializer(s.GetType());
using (var writer = new StreamWriter(FullPath, true))
{
XmlWriterSettings ws = new XmlWriterSettings();
ws.OmitXmlDeclaration = true;
serializer.Serialize(writer, s);
}
xDoc.Save(FullPath);
}
}
This code sample worked for me:
xml:
<?xml version="1.0" encoding="UTF-8"?>
<Launchpad>
<Shortcuts>
<Shortcut Id="1">
<Type>Folder</Type>
<FullPath>C:\SomePath</FullPath>
<Name>SomeFolderName</Name>
</Shortcut>
</Shortcuts>
</Launchpad>
Method:
public void AddShortcut(Shortcut s)
{
xDoc.Load(FullPath);
var rootNode = xDoc.GetElementsByTagName("Shortcuts")[0];
var nav = rootNode.CreateNavigator();
var emptyNamepsaces = new XmlSerializerNamespaces(new[] {
XmlQualifiedName.Empty
});
using (var writer = nav.AppendChild())
{
var serializer = new XmlSerializer(s.GetType());
writer.WriteWhitespace("");
serializer.Serialize(writer, s, emptyNamepsaces);
writer.Close();
}
xDoc.Save(FullPath);
}
load the file into an XmlDocument.
navigate to the node I want to append beneath (here the node name is Shortcuts).
create some type of writer and then serialize the object.
save the XmlDocument
So:
public void AddShortcut(Shortcut s)
{
// 1. load existing xml
xDoc.Load(FullPath);
// 2. create an XML node from object
XmlElement node = SerializeToXmlElement(s);
// 3. append that node to Shortcuts node under XML root
var shortcutsNode = xDoc.CreateElement("Shortcuts")
shortcutsNode.AppendChild(node);
xDoc.DocumentElement.AppendChild(shortcutsNode);
// 4. save changes
xDoc.Save(FullPath);
}
public static XmlElement SerializeToXmlElement(object o)
{
XmlDocument doc = new XmlDocument();
using(XmlWriter writer = doc.CreateNavigator().AppendChild())
{
new XmlSerializer(o.GetType()).Serialize(writer, o);
}
return doc.DocumentElement;
}
This post

Error using XElement.Parse

I have a XML configuration for Query CAML:
<add key="QueryList" value="<Query><Where><Eq><FieldRef Name='Cargar_x0020_Optimyth'/><Value Type='Boolean'>1</Value></Eq></Where></Query>" />
<add key="PaginacionList" value="10" />
<add key="QueryOptions" value="<IncludeMandatoryColumns>FALSE</IncludeMandatoryColumns><Paging ListItemCollectionPositionNext=''/>" />
Now, I want do this:
XElement ndQuery = XElement.Parse(Configuracion.QueryList);
XElement ndViewFields = XElement.Parse(Configuracion.ViewFields);
XElement ndQueryOptions = XElement.Parse(Configuracion.QueryOptions);
but I get an error.
I try this using XmlElement and its working:
XmlElement ndQuery = xmlDoc.CreateElement("Query");
if (!String.IsNullOrEmpty(Configuracion.QueryList))
{
ndQuery.InnerXml = Configuracion.QueryList;
}
XmlElement ndViewFields = xmlDoc.CreateElement("ViewFields");
if (!String.IsNullOrEmpty(Configuracion.ViewFields))
{
ndViewFields.InnerXml = Configuracion.ViewFields;
}
XmlElement ndQueryOptions = xmlDoc.CreateElement("QueryOptions");
if (!String.IsNullOrEmpty(Configuracion.QueryOptions))
{
ndQueryOptions.InnerXml = Configuracion.QueryOptions;
}
XElement ndQuery = XElement.Parse(ndQuery2.OuterXml);
XElement ndViewFields = XElement.Parse(ndViewFields2.OuterXml);
XElement ndQueryOptions = XElement.Parse(ndQueryOptions2.OuterXml);
I want avoid use XmlElement, and only XElement.
Any solution about it?
Your XML in the QueryOptions setting isn't an element. It's two sibling elements. You need one surrounding them, which in this case would be <QueryOptions>:
<add key="QueryOptions"
value="<QueryOptions><IncludeMandatoryColumns>FALSE</IncludeMandatoryColumns><Paging ListItemCollectionPositionNext=''/></QueryOptions>" />
Quite complex because you have an Xml fragment (multiple root nodes) in Configuracion.QueryOptions. The XLinq doesn't have direct methods to handle it... Based on Fragmented XML string parsing with Linq, you could
public static IEnumerable<XNode> ParseXml(string xml)
{
// Note the added escaping
// You can replace it with WebUtility.HtmlDecode
// They are defined in different assemblies
xml = HttpUtility.HtmlDecode(xml);
var settings = new XmlReaderSettings
{
ConformanceLevel = ConformanceLevel.Fragment,
IgnoreWhitespace = true
};
using (var stringReader = new StringReader(xml))
using (var xmlReader = XmlReader.Create(stringReader, settings))
{
xmlReader.MoveToContent();
while (xmlReader.ReadState != ReadState.EndOfFile)
{
yield return XNode.ReadFrom(xmlReader);
}
}
}
and then
XElement ndQuery = new XElement("Query", ParseXml(Configuracion.QueryList));
XElement ndViewFields = new XElement("ViewFields", ParseXml(Configuracion.ViewFields));
XElement ndQueryOptions = new XElement("QueryOptions", ParseXml(Configuracion.QueryOptions));
The use of HttpUtility.HtmlDecode/WebUtility.HtmlDecode is from Built in .NET function for unescaping characters in XML stream? .
Or perhaps you want to keep the configurations encoded as they are... then
XElement ndQuery = new XElement("Query", HttpUtility.HtmlDecode(Configuracion.QueryList));
XElement ndViewFields = new XElement("ViewFields", HttpUtility.HtmlDecode(Configuracion.ViewFields));
XElement ndQueryOptions = new XElement("QueryOptions", HttpUtility.HtmlDecode(Configuracion.QueryOptions));
(much shorter :-) )

Incorrect parsing of Textbox in docx by OpenXML

I am reading a .docx file using OpenXML in C#. It reads everything correctly but strangely, the content of textbox is being read thrice. What could be wrong? Here is the code to read .docx:
public static string TextFromWord(String file)
{
const string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
StringBuilder textBuilder = new StringBuilder();
using (WordprocessingDocument wdDoc = WordprocessingDocument.Open(file, false))
{
// Manage namespaces to perform XPath queries.
NameTable nt = new NameTable();
XmlNamespaceManager nsManager = new XmlNamespaceManager(nt);
nsManager.AddNamespace("w", wordmlNamespace);
// Get the document part from the package.
// Load the XML in the document part into an XmlDocument instance.
XmlDocument xdoc = new XmlDocument(nt);
xdoc.Load(wdDoc.MainDocumentPart.GetStream());
XmlNodeList paragraphNodes = xdoc.SelectNodes("//w:p", nsManager);
foreach (XmlNode paragraphNode in paragraphNodes)
{
XmlNodeList textNodes = paragraphNode.SelectNodes(".//w:t", nsManager);
foreach (System.Xml.XmlNode textNode in textNodes)
{
textBuilder.Append(textNode.InnerText);
}
textBuilder.Append(Environment.NewLine);
}
}
return textBuilder.ToString();
}
The part of file I am talking about is:
The result is: I read it in a test application like this:
What's wrong here?

Add node in XML file crashes

I created a ping application with a service that pings to URLs. The list of the URLs is stored in an XML file.
My application crashes when I'm trying to add a new site to my XML while the service is running.
VS2010 says my file is being used by some other process but I'm sure that everything is fine. My service isn't using the XML while I'm adding to it.
BUT I guess using an XmlReader & XmlWriter at the same time is where it crashes.
I'll rewrite my code with LINQ to XML but I was wondering if it's possible to use XmlReader & XmlWriter at the same time?
private void saveSites(Site newSite)
{
XmlDocument XDoc = new XmlDocument();
bool fileExists = true;
if (File.Exists("c:\\temp\\sites.xml") == false)
{
createXML();
fileExists = false;
}
using (XmlReader XReader = XmlReader.Create("c:\\temp\\sites.xml"))
{
XDoc.Load(XReader);
if (fileExists == true)
{
XmlNode SiteNode = XDoc.CreateNode(XmlNodeType.Element, "site", "");
XmlNode URLNode = XDoc.CreateNode(XmlNodeType.Element, "url", "");
URLNode.InnerText = newSite.URL;
XmlNode EmailNode = XDoc.CreateNode(XmlNodeType.Element, "email", "");
EmailNode.InnerText = newSite.Email;
SiteNode.AppendChild(URLNode);
SiteNode.AppendChild(EmailNode);
XDoc.DocumentElement.AppendChild(SiteNode);
}
else
{
foreach (Site site in sites)
{
XmlNode SiteNode = XDoc.CreateNode(XmlNodeType.Element, "site", "");
XmlNode URLNode = XDoc.CreateNode(XmlNodeType.Element, "url", "");
URLNode.InnerText= site.URL;
XmlNode EmailNode = XDoc.CreateNode(XmlNodeType.Element, "email", "");
EmailNode.InnerText = site.Email;
SiteNode.AppendChild(URLNode);
SiteNode.AppendChild(EmailNode);
XDoc.DocumentElement.AppendChild(SiteNode);
}
}
XDoc.Save("c:\\temp\\sites.xml");
}
}
Your reader is blocking the writing because it is in the using block. I'd suggest using the Load method the XmlDocument object with a uri instead of creating your own reader. Then also you can separate the initilisation from the writing operation.
Close your XMLReader explicitly.
using (XmlReader reader = XmlReader.Create("file.xml"))
{
while (reader.Read())
{
...
}
reader.Close();
}

Categories