I've been trying to select some XML comments like so:
XDocument doc = XDocument.Load(args[0]);
var comments = from node in doc.Elements().DescendantNodesAndSelf()
where node.NodeType == XmlNodeType.Comment
select node as XComment;
With this solution I'm getting all xml comment of the file, but I want to select only those comments and create XElement with it:
<Connections>
...
<!-- START Individual Account Authentication -->
<!--<authentication mode="None"/>
<roleManager enabled="false"/>
<profile enabled="false"/>-->
<!-- END Individual Account Authentication -->
...
</Connections>
Any solutions ? :S
Here is a sample:
XDocument doc = XDocument.Load("input.xml");
foreach (XComment start in doc.DescendantNodes().OfType<XComment>().Where(c => c.Value.StartsWith(" START")).ToList())
{
XComment end = start.NodesAfterSelf().OfType<XComment>().FirstOrDefault(c => c.Value.StartsWith(" END"));
if (end != null)
{
foreach (XComment comment in end.NodesBeforeSelf().OfType<XComment>().Intersect(start.NodesAfterSelf().OfType<XComment>()).ToList())
{
comment.ReplaceWith(XElement.Parse("<dummy>" + comment.Value + "</dummy>").Nodes());
}
// if wanted/needed
start.Remove();
end.Remove();
}
}
doc.Save("output.xml");
That gives me
<Connections>
...
<authentication mode="None" /><roleManager enabled="false" /><profile enabled="false" />
...
</Connections>
Related
I have the following xml file and I am trying to read name element which I am not able to, any idea how can I read that and other elements ?
<?xml version="1.0" encoding="us-ascii"?>
<WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1">
<name>tata</name>
<SSIDConfig>
<SSID>
<name>SampleSingleSignOn</name>
</SSID>
</SSIDConfig>
<connectionType>ESS</connectionType>
<connectionMode>auto</connectionMode>
<autoSwitch>false</autoSwitch>
<MSM>
<security>
<authEncryption>
<authentication>WPA2</authentication>
<encryption>AES</encryption>
<useOneX>true</useOneX>
</authEncryption>
<OneX xmlns="http://www.microsoft.com/networking/OneX/v1">
<cacheUserData>true</cacheUserData>
<maxAuthFailures>3</maxAuthFailures>
<authMode>user</authMode>
<singleSignOn>
<type>preLogon</type>
<maxDelay>10</maxDelay>
</singleSignOn>
<EAPConfig>
<EapHostConfig xmlns="http://www.microsoft.com/provisioning/EapHostConfig"
xmlns:eapCommon="http://www.microsoft.com/provisioning/EapCommon"
xmlns:baseEap="http://www.microsoft.com/provisioning/BaseEapMethodConfig">
<EapMethod>
<eapCommon:Type>25</eapCommon:Type>
<eapCommon:AuthorId>0</eapCommon:AuthorId>
</EapMethod>
<Config xmlns:baseEap="http://www.microsoft.com/provisioning/BaseEapConnectionPropertiesV1"
xmlns:msPeap="http://www.microsoft.com/provisioning/MsPeapConnectionPropertiesV1"
xmlns:msChapV2="http://www.microsoft.com/provisioning/MsChapV2ConnectionPropertiesV1">
<baseEap:Eap>
<baseEap:Type>25</baseEap:Type>
<msPeap:EapType>
<msPeap:ServerValidation>
<msPeap:DisableUserPromptForServerValidation>false</msPeap:DisableUserPromptForServerValidation>
<msPeap:TrustedRootCA />
</msPeap:ServerValidation>
<msPeap:FastReconnect>true</msPeap:FastReconnect>
<msPeap:InnerEapOptional>0</msPeap:InnerEapOptional>
<baseEap:Eap>
<baseEap:Type>26</baseEap:Type>
<msChapV2:EapType>
<msChapV2:UseWinLogonCredentials>true</msChapV2:UseWinLogonCredentials>
</msChapV2:EapType>
</baseEap:Eap>
<msPeap:EnableQuarantineChecks>false</msPeap:EnableQuarantineChecks>
<msPeap:RequireCryptoBinding>false</msPeap:RequireCryptoBinding>
<msPeap:PeapExtensions />
</msPeap:EapType>
</baseEap:Eap>
</Config>
</EapHostConfig>
</EAPConfig>
</OneX>
</security>
</MSM>
</WLANProfile>
And I am reading like this:
XDocument xdoc = XDocument.Load("xmlfile1.xml");
xdoc.Root.Element("name")
it returns null element.
You have to take the XML namespace into account:
XNamespace ns = "http://www.microsoft.com/networking/WLAN/profile/v1";
XElement name = xdoc.Root.Element(ns + "name");
What is the easiest way of uncommenting the body of some node in XML? The elements have unique name, the structure of the documents look as follows:
somefile.xml
<?xml version="1.0"?>
<name1>
<irrelevant1>
<irrelevant2>
<!--
<irrelevant3 />
-->
</irrelevant2>
</irrelevant1>
<name2>
<name3>
<!--
<name4 field="The" />
<name4 field="Owls" />
<name4 field="Are />
<name4 field="Not" />
<name4 field="What" />
<name4 field="They" />
<name4 field="Seem />
-->
</name3>
</name2>
</name1>
The goal should look like this, with comments removed:
uncommented.xml
<?xml version="1.0"?>
<name1>
<irrelevant1>
<irrelevant2>
<!--
<irrelevant3 />
-->
</irrelevant2>
</irrelevant1>
<name2>
<name3>
<name4 field="The" />
<name4 field="Owls" />
<name4 field="Are />
<name4 field="Not" />
<name4 field="What" />
<name4 field="They" />
<name4 field="Seem />
</name3>
</name2>
</name1>
My approach to parsing:
XmlDocument xdoc = new XmlDocument();
xdoc.Load(#"C:\somefile.xml");
XmlNodeList nl = xdoc.GetElementsByTagName("name2");
XmlNode xn = nl[0];
string xn_content = xn.InnerXml;
xn_content = Regex.Replace(xn_content, "<!--|-->", String.Empty);
XmlDocument doc = new XmlDocument();
doc.LoadXml(xn_content);
XmlNode newNode = doc.DocumentElement;
// this import doesn't really help
xdoc.ImportNode(newNode, true);
xn.RemoveAll();
xn.AppendChild(newNode);
xdoc.Save(#"C:\uncommented.xml");
Results with the ArgumentException:
{"The node to be inserted is from a different document context."}
Your immediate problem is that you call XmlDocument.ImportNode() but do not use the returned node. You need to do newNode = xDoc.ImportNode(newNode, true);.
However, a cleaner way to do this would be to avoid the Regex parsing entirely. Instead, descend the XmlNode hierarchy, pick out the XmlComment nodes you wish to uncomment, load their InnerText into an XmlDocumentFragment, then add its newly created children to the parent node of the comment:
public static class XmlNodeExtensions
{
public static XmlDocument Document(this XmlNode node)
{
for (; node != null; node = node.ParentNode)
{
var doc = node as XmlDocument;
if (doc != null)
return doc;
}
return null;
}
public static IEnumerable<XmlNode> AncestorsAndSelf(this XmlNode node)
{
for (; node != null; node = node.ParentNode)
yield return node;
}
public static IEnumerable<XmlNode> DescendantsAndSelf(this XmlNode root)
{
if (root == null)
yield break;
yield return root;
foreach (var child in root.ChildNodes.Cast<XmlNode>())
foreach (var subChild in child.DescendantsAndSelf())
yield return subChild;
}
public static void UncommentXmlNodes(IEnumerable<XmlComment> comments)
{
foreach (var comment in comments.ToList())
UncommentXmlNode(comment);
}
public static void UncommentXmlNode(XmlComment comment)
{
if (comment == null)
throw new NullReferenceException();
var doc = comment.Document();
if (doc == null)
throw new InvalidOperationException();
var parent = comment.ParentNode;
var innerText = comment.InnerText;
XmlDocumentFragment docFrag = doc.CreateDocumentFragment();
//Set the contents of the document fragment.
docFrag.InnerXml = innerText;
XmlNode insertAfter = comment;
foreach (var child in docFrag.ChildNodes.OfType<XmlElement>().ToList())
{
insertAfter = parent.InsertAfter(child, insertAfter);
}
parent.RemoveChild(comment);
}
}
Then call it like:
string xml = #"<?xml version=""1.0""?>
<name1>
<irrelevant1>
<irrelevant2>
<!--
<irrelevant3 />
-->
</irrelevant2>
</irrelevant1>
<name2>
<name3>
<!--
<name4 field=""The"" />
<name4 field=""Owls"" />
<name4 field=""Are"" />
<name4 field=""Not"" />
<name4 field=""What"" />
<name4 field=""They"" />
<name4 field=""Seem"" />
-->
</name3>
</name2>
</name1>
";
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xml);
Debug.WriteLine(xmlDoc.ToXml());
XmlNodeExtensions.UncommentXmlNodes(xmlDoc.DocumentElement.DescendantsAndSelf().OfType<XmlComment>().Where(c => c.ParentNode.Name == "name3"));
Debug.WriteLine(xmlDoc.ToXml());
Note that your commented XML is invalid. <name4 field="Are /> should be <name4 field="Are"/> and <name4 field="Seem /> should be <name4 field="Seem"/>. I fixed that for you in the test case since I assume it's a typo.
I'm trying to write a routine that updates values in an XML file and believe I'm close. Here's an example of the XML:
<?xml version="1.0" encoding="utf-8"?>
<!-- This file is generated by the GPS_Client program. -->
<Sites>
<Site>
<Id>A</Id>
<add key="landingName" value="Somewhere" />
<add key="landingLat" value="47.423719" />
<add key="landingLon" value="-123.011364" />
<add key="landingAlt" value="36" />
</Site>
<Site>
<Id>B</Id>
<add key="landingName" value="Somewhere Else" />
<add key="landingLat" value="45.629160" />
<add key="landingLon" value="-128.882934" />
<add key="landingAlt" value="327" />
</Site>
</Sites>
The key is that I need to update a specific key without updating the rest of the keys with the same name. Here's the current code:
private void UpdateOrCreateAppSetting(string filename, string site, string key, string value)
{
string path = "\"#" + filename + "\"";
XDocument doc = XDocument.Load(path);
var list = from appNode in doc.Descendants("appSettings").Elements()
where appNode.Attribute("key").Value == key
select appNode;
var e = list.FirstOrDefault();
// If the element doesn't exist, create it
if (e == null) {
new XElement(site,
new XAttribute(key, value));
// If the element exists, just change its value
} else {
e.Element(key).Value = value;
e.Attribute("value").SetValue(value);
}
}
I assume I need to concatenate site, Id and key in some way, but don't see how it's done.
Using this XmlLib, you can have it create the node if it doesn't exist automatically.
private void UpdateOrCreateAppSetting(string filename, string site,
string key, string value)
{
string path = "\"#" + filename + "\"";
XDocument doc = XDocument.Load(path);
XPathString xpath = new XPathString("//Site[Id={0}]/add[#key={1}]", site, key);
XElement node = doc.Root.XPathElement(xpath, true); // true = create if not found
node.Set("value", value, true); // set as attribute, creates attribute if not found
doc.Save(filename);
}
I have a .net web application that uses XmlDocument to load an XML String. The format of the XML is as follows:
<Sections name="Section Opening Page" PageContentID="201" Template="ReportTags">
<Chapter Name="Introduction" PageContentID="202" Template="ReportTags">
<Pages Name="Why this Document?" PageContentID="203" Template="ReportTags" />
<Pages Name="Target Audience" PageContentID="204" Template="ReportTags" />
</Chapter>
<Chapter Name="Detailed Results" PageContentID="205" Template="ReportTags">
<Pages Name="Question List" PageContentID="206" Template="ReportTags" />
<Pages Name="Answers" PageContentID="207" Template="ReportTags" />
<Pages Name="Comments" PageContentID="208" Template="ReportTags" />
</Chapter>
<Chapter Name="Appendix 1" PageContentID="209" Template="ReportTags">
<Pages Name="Page 1" PageContentID="210" Template="ReportTags" />
<Pages Name="Page 2" PageContentID="211" Template="ReportTags" />
<Pages Name="Page 3" PageContentID="212" Template="ReportTags" />
</Chapter>
<Chapter Name="Appendix 2" PageContentID="213" Template="ReportTags">
<Pages Name="Page 1" PageContentID="214" Template="ReportTags" />
<Pages Name="Page 2" PageContentID="215" Template="ReportTags" />
</Chapter>
</Sections>
As an example, I am trying to insert a NEW NODE under the 'Chapter' named 'Detailed Results' BETWEEN 'Page' named 'Question List' and 'Answers'. The code I am using to do this is as follows:
try
{
string sPath = "Sections/Chapter/Pages[#PageContentID='" + selectedNode.Value + "']";
XmlNode xmlNode = xmlDoc.SelectSingleNode(sPath);
XmlElement xNewChild = xmlDoc.CreateElement("Pages");
xNewChild.SetAttribute("Name", "Another New Page");
xNewChild.SetAttribute("PageContentID", Guid.NewGuid().ToString());
xNewChild.SetAttribute("Template", "ReportTags");
xmlDoc.DocumentElement.InsertAfter(xNewChild, xmlNode);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
However, it is not working, my new node is not inserted into the proper place, it is inserted at the top.
Any help would be greatly appreciated!!
Are you sure that xmlNode is returning the node you think it is? Step through it with the debugger.
If it is null, then the node you insert will get added as a child of the DocumentElement as you describe. If it is returning the Pages node, then you should get an exception 'The reference node is not a child of this node'.
This is fairly self explanatory. The code:
doc.DocumentElement.InsertAfter(xNewChild, xmlNode);
should be:
xmlNode.ParentNode.InsertAfter(xNewChild, xmlNode);
This said, if possible I'd be using XDocument, a part of XLinq. The API's a lot nicer & more powerful, and is generally higher performance. An example:
var element = x.Descendants("Pages").Single(e => e.Attribute("PageContentID").Value == "206");
var newElement = new XElement("Pages",
new XAttribute("Name", "Another New Page"),
new XAttribute("PageContentID", Guid.NewGuid().ToString()),
new XAttribute("Template", "ReportTags"));
element.AddAfterSelf(newElement);
If you don't mind using Linq-To-Xml
XDocument xmlDocument = XDocument.Load("asd.xml");
try
{
var root = xmlDocument.Root;
var pages = root.Elements("Chapter").Elements("Pages");
var mypage = pages.Where(p => p.Attribute("PageContentID").Value == "206").FirstOrDefault();
XElement xNewChild = new XElement("Pages", new XAttribute("Name", "Another New Page"), new XAttribute("PageContentID", Guid.NewGuid().ToString()), new XAttribute("Template", "ReportTags"));
mypage.AddAfterSelf(xNewChild);
xmlDocument.Save("asd.xml");
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
It's const value 206 put in the code, modify to your needs
After execution
"That would probably work, but i am not using Linq for this project. LOL, maybe I should be!!!!!"
So try this, you have to call insertAfter on xmlNode's parent node
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load("asd.xml");
try
{
string sPath = "//Pages[#PageContentID=\"206\"]";
var xmlNode = xmlDocument.SelectSingleNode(sPath);
XmlElement xNewChild = xmlDocument.CreateElement("Pages");
xNewChild.SetAttribute("Name", "Another New Page");
xNewChild.SetAttribute("PageContentID", Guid.NewGuid().ToString());
xNewChild.SetAttribute("Template", "ReportTags");
xmlNode.ParentNode.InsertAfter(xNewChild, xmlNode);
xmlDocument.Save("asd.xml");
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
I have the following but its not working for me:
static void SaveVersion(string configFile, string Version)
{
XmlDocument config = new XmlDocument();
config.Load(configFile);
XmlNode appSettings = config.SelectSingleNode("configuration/appSettings");
XmlNodeList appKids = appSettings.ChildNodes;
foreach (XmlNode setting in appKids)
{
if (setting.Attributes["key"].Value == "AgentVersion")
setting.Attributes["value"].Value = Version;
}
config.Save(configFile);
}
The config file i'm loading up on config.Load(configFile) is the following:
<?xml version="1.0"?>
<configuration>
<startup>
<supportedRuntime version="v2.0.50727" />
</startup>
<appSettings>
<add key="AgentVersion" value="2.0.5" />
<add key="ServerHostName" value="" />
<add key="ServerIpAddress" value="127.0.0.1" />
<add key="ServerPort" value="9001" />
</appSettings>
</configuration>
Am I missing something? I figured it would edit just that particular attribute AgentVersion but its not really doing anything.
Are you aware of the ConfigurationManager class? You can use it to manipulate your app.config file without doing anything manually. I don't think you should reinvent the wheel unless you have a good reason to:
static void SaveVersion(string configFile, string version)
{
var myConfig = ConfigurationManager.OpenExeConfiguration(configFile);
myConfig.AppSettings.Settings["AgentVersion"].Value = version;
myConfig.Save();
}
Try this:
static void SaveVersion(string configFile, string Version)
{
var config = new XmlDocument();
config.Load(configFile);
var agentVersionElement = config.DocumentElement.SelectSingleNode("configuration/appSettings/add[#key = 'AgentVersion']") as XmlElement;
if (agentVersionElement != null)
agentVersionElement.SetAttribute("value", version);
config.Save(configFile);
}
Note that I'm doing the SelectSingleNode from the DocumentElement, not from the XmlDocument itself.