I am attempting to write some code to read in a *.CSPROJ file using C#
The code I have is as follows
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(fullPathName);
XmlNamespaceManager mgr = new XmlNamespaceManager(xmldoc.NameTable);
//mgr.AddNamespace("x", "http://schemas.microsoft.com/developer/msbuild/2003");
foreach (XmlNode item in xmldoc.SelectNodes("//EmbeddedResource") )
{
string test = item.InnerText.ToString();
}
using the debugger I can see that 'fullPathName" has the correct value and the xmldoc once loaded has the correct contents.
The xmldoc does not have any "Nodes" though, as if the contents are not recognised as XML.
Using a XML editor the *.csproj file validates an XML document.
Where am I going wrong?
Why not use the MSBuild API?
Project project = new Project();
project.Load(fullPathName);
var embeddedResources =
from grp in project.ItemGroups.Cast<BuildItemGroup>()
from item in grp.Cast<BuildItem>()
where item.Name == "EmbeddedResource"
select item;
foreach(BuildItem item in embeddedResources)
{
Console.WriteLine(item.Include); // prints the name of the resource file
}
You need to reference the Microsoft.Build.Engine assembly
You were getting close with your XmlNamespaceManager addition, but weren't using it in the SelectNodes method:
XmlNamespaceManager mgr = new XmlNamespaceManager(xmldoc.NameTable);
mgr.AddNamespace("x", "http://schemas.microsoft.com/developer/msbuild/2003");
foreach (XmlNode item in xmldoc.SelectNodes("//x:ProjectGuid", mgr))
{
string test = item.InnerText.ToString();
}
(I switched to searching for a different element as my project didn't have any embedded resources)
For completeness here the XDocument version, this simplifies namespace management:
XDocument xmldoc = XDocument.Load(fullPathName);
XNamespace msbuild = "http://schemas.microsoft.com/developer/msbuild/2003";
foreach (var resource in xmldoc.Descendants(msbuild + "EmbeddedResource"))
{
string includePath = resource.Attribute("Include").Value;
}
Related
I am new to C# programming and trying to update the XML file using C#. Here when I am trying to get the root element using XDocument it is showing the complete script in the file.
Below is my code explanation:
I am having the below function and it is reading the file path from the command line arguments.
private XDocument doc;
public void Update(string filepath)
{
string filename = Path.GetFileName(filepath);
doc = xDocument.Load(filepath);
XElement rootelement = doc.Root;
}
Into the filepath variable, we are taking the path "E:\BuilderTest\COMMON.wxs"
Then we are loading the file using XDocument.
But when we are trying to get the rootelement from the file, it is not showing the root element. Instead, it is showing the complete data in the file.
But when I am using XmlDocument() instead of XDocument() I am able to see only the root element.
Below is the code using XmlDocument():
private XmlDocument doc;
public void Update(string filepath)
{
string filename = Path.GetFileName(filepath);
doc = new XmlDocument();
doc.Load(filepath);
XmlElement rootelement = doc.DocumentElement;
}
Please help me by providing your valuable inputs on this.
XDocument and XmlDocument are different class structure to follow as per requirement.
XDocument will work like below
XDocument doc;
doc = XDocument.Load(filepath);
XElement root = doc.Root;
Root, Descendants, Elements are the operations provided in XDocument. For every node its gives XElement
In your case you should use doc.Root to find the element, then use .Value to get its value
XElement comes with System.Xml.Linq. It is derived from XNode.
It gives you serialized information of every node one by one.
On the other hand XMLDocument will work like below
XmlDocument doc;
doc = new XmlDocument();
doc.Load(filepath);
XmlElement rootelement = doc.DocumentElement;
XmlElement comes with System.Xml. It is derived from XmlNode which is again derived from IEnumerable.
It gives you information in a Enumerable which you can easily parse.
So I am running into an issue when I run a security scan on my application. It turns out that I am failing to protect against XXE.
Here is a short snippet showing the offending code:
static void Main()
{
string inp = Console.ReadLine();
string xmlStr = ""; //This has a value that is much too long to put into a single post
if (!string.IsNullOrEmpty(inp))
{
xmlStr = inp;
}
XmlDocument xmlDocObj = new XmlDocument {XmlResolver = null};
xmlDocObj.LoadXml(xmlStr);
XmlNodeList measureXmlNodeListObj = xmlDocObj.SelectNodes("REQ/MS/M");
foreach (XmlNode measureXmlNodeObj in measureXmlNodeListObj)
{
XmlNode detailXmlNodeListObj = xmlDocObj.SelectSingleNode("REQ/DTD");
string measureKey = measureXmlNodeObj.Attributes["KY"].Value;
if (detailXmlNodeListObj.Attributes["MKY"].Value ==
measureKey) //Checking if selected MeasureKey is same
{
XmlNode filerNode = measureXmlNodeObj.SelectSingleNode("FS");
if (filerNode != null)
{
XDocument fixedFilterXmlObj = XDocument.Load(new StringReader(filerNode.OuterXml));
var measureFixedFilters = (from m in fixedFilterXmlObj.Element("FS").Elements("F")
select m).ToList();
foreach (var fixedFilter in measureFixedFilters)
{
var fixedFilterValues = (from m in fixedFilter.Elements("VS").Elements("V")
select m.Attribute("DESC").Value).ToList();
foreach (var value in fixedFilterValues)
{
Console.WriteLine(value.Trim());
}
}
}
}
}
Console.ReadLine();
}
According to Veracode, the line that unsafe is XDocument fixedFilterXmlObj = XDocument.Load(new StringReader(filerNode.OuterXml));
But it seems like according to Owsap, it should be safe:
Both the XElement and XDocument objects in the System.Xml.Linq library
are safe from XXE injection by default. XElement parses only the
elements within the XML file, so DTDs are ignored altogether.
XDocument has DTDs disabled by default, and is only unsafe if
constructed with a different unsafe XML parser.
So it seems like I am making the mistake of using an usafe XML Parser, opening XDocument to XXE.
I found a unit test that replicates the issue and also has a safe usage of XDocument but I can't seem to find what exactly my code is unsafe, because I do not use:
XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Parse; // unsafe!
You can run my code to replicate the issue, but you should replace the line with the empty xmlStr with this value: here (too large for a single post)
I'm not sure how or why this works, but it does:
XDocument fixedFilterXmlObj;
using (XmlNodeReader nodeReader = new XmlNodeReader(filerNode))
{
nodeReader.MoveToContent();
fixedFilterXmlObj = XDocument.Load(nodeReader);
}
I'm trying to extract the contents of soap:Body from multiple XML files in a folder. It is working for a single file as the following:
XmlNamespaceManager mgr = new XmlNamespaceManager(doc.NameTable);
mgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
doc.LoadXml(doc.DocumentElement.SelectSingleNode("soap:Body", mgr).ChildNodes[0].OuterXml);
doc.Save(#"E:\new.xml");
To do the same for multiple files, I am using the following code:
XmlDocument xDoc = new XmlDocument();
string path = #"C:\Folder";
foreach (string file in Directory.EnumerateFiles(path, "*.xml"))
{
xDoc.Load(Path.Combine(Directory.GetCurrentDirectory(), file));
XmlNamespaceManager mgr = new XmlNamespaceManager(xDoc.NameTable);
mgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
xDoc.LoadXml(xDoc.DocumentElement.SelectSingleNode("soap:Body", mgr).ChildNodes[0].OuterXml);
}
How to save the files after processing them?
What about something like this :
XmlDocument xDoc = new XmlDocument();
string path = #"C:\Folder";
foreach (string file in Directory.EnumerateFiles(path, "*.xml"))
{
xDoc.Load(Path.Combine(Directory.GetCurrentDirectory(), file));
XmlNamespaceManager mgr = new XmlNamespaceManager(xDoc.NameTable);
mgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
xDoc.LoadXml(xDoc.DocumentElement.SelectSingleNode("soap:Body", mgr).ChildNodes[0].OuterXml);
doc.Save("E:\\" + file);
}
Regards,
EDIT: I think I understand what you asking about.
You are looping over every document in some kind of folder, and then you want to save every file after some edits to another folder ?
You can add some variable as counter in foreach loop, and use Save() method as such(so that each file is saved with new name):
doc.Save(string.Format(#"E:\new{0}.xml", counter);
++counter; // new number for next file
Or you could use TaiT's answer and save each file with same name as original.
I am working with some codes where I am trying to modify an XML file by loading in XmlDocument and then adding/removing an element. This file has a cache dependency set which triggers a piece of code when ever we change the file. If I edit the file in editor or use FileStream it does expire the cache and trigger codes but not with XmlDocument.Save method. Any idea what could be the reason behind this?
string filePath = Server.MapPath("~/App_Data/ssl.xml");
XmlDocument doc = new XmlDocument();
doc.Load(filePath);
XmlElement ele = doc.CreateElement("directory");
XmlAttribute atr = doc.CreateAttribute("path");
atr.Value = "/";
XmlNode node = doc.DocumentElement.SelectSingleNode("//SecurePages");
node.AppendChild(ele);
doc.Save(filePath);
Above code does modify the file but it doesn't expire the cache item which it should since file is modified. The cache does expire if change the file manually or using FileStream.
UPDATE
I found a workaround which is if I load the XmlDocument using LoadXml method then Save call does trigger the cache dependency. I am still not sure why Load/Save combination doesn't work
string filePath = Server.MapPath("~/App_Data/ssl.xml");
XmlDocument doc = new XmlDocument();
doc.LoadXml(ReadTextFile(filePath));
XmlElement ele = doc.CreateElement("directory");
XmlAttribute atr = doc.CreateAttribute("path");
atr.Value = "/";
XmlNode node = doc.DocumentElement.SelectSingleNode("//SecurePages");
node.AppendChild(ele);
doc.Save(filePath);
Disclaimer: This issue is happening within a Unity application, but AFAIK, this is more of a C# issue than a Unity issue...
I am trying to use System.Xml.XmlDocument to parse an Amazon S3 bucket listing. Here is my bucket xml. I am using an example that I found in a C# Xml tutorial.
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("http://rss.cnn.com/rss/edition_world.rss");
XmlNode titleNode = xmlDoc.SelectSingleNode("//rss/channel/title");
if(titleNode != null)
Debug.Log(titleNode.InnerText);
This works fine for that particular XML file, but when I put my stuff in there:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("https://s3.amazonaws.com/themall/");
Debug.Log ( xmlDoc.InnerXml );
XmlNode nameNode = xmlDoc.SelectSingleNode("//Name");
if(nameNode != null)
Debug.Log(nameNode.InnerText);
I get the raw XML in the console, so I know it is being downloaded successfully, but even the simplest XPath produces no results!
My only theory is that perhaps it has something to do with the default namespace in my XML? Do I need to tell XmlDocument about that somehow? Here is my root tag:
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
I have tried creating an XmlNamespaceManager and using it with all of my calls to "SelectSingleNode", but that doesn't seem to work either.
XPathNavigator nav = xmlDoc.CreateNavigator();
XmlNamespaceManager ns = new XmlNamespaceManager(nav.NameTable);
ns.AddNamespace(System.String.Empty, "http://s3.amazonaws.com/doc/2006-03-01/");
What am I doing wrong?
Thanks!
When you add the namespace to the namespace manager you need to give it a non-empty prefix. According to MSDN:
If the XmlNamespaceManager will be used for resolving namespaces in an XML Path Language (XPath) expression, a prefix must be specified.
Blockquote
The prefix must then be used in your XPath select statement. Here is the code I used and the output was "themall" as expected:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("http://s3.amazonaws.com/themall/");
XmlNamespaceManager namespaceManager =
new XmlNamespaceManager(xmlDoc.NameTable);
namespaceManager.AddNamespace("ns",
"http://s3.amazonaws.com/doc/2006-03-01/");
XmlNode titleNode =
xmlDoc.SelectSingleNode("//ns:Name", namespaceManager);
if (titleNode != null)
Console.WriteLine(titleNode.InnerText);