Ok so I am new to C# but a very experienced developer in other langauges but i dont know how to handle if the NodeSelect is nill
DirectoryInfo root = new DirectoryInfo(root_path);
XmlDocument xmlDoc = new XmlDocument(); //* create an xml document object.
xmlDoc.Load(root_path + #"\file.xml"); //* load the XML document from the specified file.
//* Get elements.
XmlNodeList elements = xmlDoc.SelectNodes("//elements");
foreach (XmlNode node in elements){
string link = node.SelectSingleNode("link").InnerText.Trim();
In the above example link may or maynot be in the element block in the xml and i need it to not give me this error
NULLReferenceExemption
Object reference not set to an instance of an object.
I figured a try catch would work but i know there has got to be a better way in C#
UPDATE
var linkNode = node.SelectSingleNode("link");
if (linkNode != null)
{
string link = linkNode.InnerText.Trim();
}
Console.WriteLine("link: " + link);
error is Error
The name 'link' does not exist in the current context
As you say, if the link node may or may not be there, you have to test for its existence before accessing its methods and properties.
something in the sort of
var ln = node.SelectSingleNode("link");
if (ln != null && ln.InnerText!=null)
{
string link = ln.InnerText.Trim();
...
}
var linkNode = node.SelectSingleNode("link");
string link = String.Empty;
if (linkNode != null && linkNode.InnerText != null)
link = linkNode.InnerText.Trim();
You can do, for example,
foreach (XmlNode node in elements)
{
var linkNode = node.SelectSingleNode("link");
if (linkNode != null)
{
string link = linkNode.InnerText.Trim();
}
}
Related
I have a XML Doc that I'm pulling out a specific Node and all of it's attributes. In debug mode I can see that I'm getting the specific Nodes and all of their attributes. However, when I try to get the attribute value it can't find it and returns a NULL value. I've done some searching and looked at some examples and from what I can tell I should be getting the value but I'm not and I don't see what I'm doing wrong.
I'm trying to get the StartTime value.
Here is the XML that is returned.
Here you can see in debug and with the Text Visualizer the value should be there.
The code I'm trying.
XmlNodeList nodes = xmlDoc.GetElementsByTagName("PlannedAbsences");
if (nodes != null && nodes.Count > 0)
{
foreach (XmlNode node in nodes)
{
if (node.Attributes != null)
{
var nameAttribute = node.Attributes["StartTime"];
if (nameAttribute != null)
{
//var startDate = nameAttribute.Value;
}
}
}
}
Using the XDocument class contained within the System.Xml.Linq namespace, grab the sub elements from the PlannedAbsences parent, then iterate over sub elements retrieving the value of the desired attribute.
var xmlDoc = XDocument.Load(#"path to xml file")
var absences = xmlDoc.Element("PlannedAbsences")?.Elements("Absence");
foreach (var item in absences)
{
var xElement = item.Attribute("StartTime").Value;
Console.WriteLine(xElement);
}
I'm having a following XML, in that I'm having two Nodes namely "param" but the Names are different. I need to get the value by Name from the node param. Kindly look at the code
void Main()
{
string _commentXml = string.Format("<root>{0}{1}</root>"
, "<param name=\"Super\">Parameter One</param>"
, "<param name=\"Supreme\">Parameter Two</param>");
XmlDocument _comment = new XmlDocument();
_comment.LoadXml(_commentXml);
XElement element = XElement.Load(_comment.DocumentElement.CreateNavigator().ReadSubtree());
TryGetElementValue(element, "param").Dump();
}
public string TryGetElementValue(XElement parentEl, string elementName, string defaultValue = null)
{
var foundEl = parentEl.Element(elementName);
if (foundEl != null)
{
var xyz = foundEl.Elements("param");
if (xyz != null)
{
return xyz.First(x => x.Attribute("name").Value == "Super").Value;
}
}
return defaultValue;
}
I can't able to get the value of param with name=Super
I refereed one of the stack-overflow question which is opt for this requirement but I can't.
Referred: XDocument get XML element by the value of its name attribute
Kindly assist me.
Why all this mess?
XDocument has a Descendants method and with linq it's easy:
var xdoc = XDocument.Parse(_commentXml);
var xel = xdoc.Descendants("param")
.Where(xElement => xElement.Attribute("name")?.Value == "Super");
You can also user XPath.
var element = doc.XPathSelectElement("/path/to/element/I/want");
In your case it would be something like this:
var element = doc.XPathSelectElement("/root/param[#name="Super"]");
Check here for more info:
https://stackoverflow.com/a/11224645/1582065
I've tried deleting a node from my XML file 3 different ways; and each way I've come up empty. I am querying a SQL database and grabbing a filename, I want to delete the entire node were the file name in the XML document is = to the SQL database result.
I'm not sure what's wrong in my code:
Background Information
fn44 is the Filename grabbed from a SQL database (all my info is in a
SQL table, I need an XML file for use with JavaScript)
XML:
<?xml version="1.0" encoding="utf-8"?>
<xml>
<bannerMain>
<department>main</department>
<filename>resdrop.png</filename>
<title>This is a Title</title>
<text><![CDATA[caption text]]></text>
</bannerMain>
</xml>
Attempt 1 (I know that I'm not getting to the child correctly, can't seem to figure out how to fix it):
XDocument doc = XDocument.Load(Server.MapPath("~/uploads/banners.xml"));
var q = from node in doc.Descendants("bannerMain")
let fina = node.Descendants("filename")/*PROBLEM LINE*/
where fina != null && fina == myReader[0]/*Gets filename from SQL database*/
select node;
q.ToList().ForEach(x => x.Remove());
doc.Save(Server.MapPath("~/uploads/banners.xml"));
Attempt 2 (should work in my mind but doesn't)
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(Server.MapPath("~/uploads/banners.xml"));
XmlNode nodeToDelete = xmlDoc.SelectSingleNode("/xml/bannerMain[#filename="
+ fn44 + "]");
if (nodeToDelete != null)
{
nodeToDelete.ParentNode.RemoveChild(nodeToDelete);
}
xmlDoc.Save(Server.MapPath("~/uploads/banners.xml"));
Attempt 3 (similar to attempt 2)
string nodeToDelete = fn44;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(Server.MapPath("~/uploads/banners.xml"));
XmlNode node = xmlDoc.SelectSingleNode(string.Format("//*[filename=\"{0}\"]"
, nodeToDelete));
if (node != null)
xmlDoc.SelectSingleNode("xml/bannersMain").RemoveChild(node);
xmlDoc.Save(Server.MapPath("~/uploads/banners.xml"));
I want to delete the whole node where the filename is = to the filename that is grabbed from the SQL database. Any help/resources is much appreciated.
SOLVED:
There's a few different options in the below answers that work out well.
Solution 1:
var xDoc = XDocument.Load(Server.MapPath("~/uploads/banners.xml"));
string fileName = fn44; //Use whatever value you found in SQL DB...
xDoc.Descendants("filename").Where(c => c.Value == fileName).Select(x => x.Parent).Remove();
xDoc.Save(Server.MapPath("~/uploads/banners.xml"));
Solution 2:
XDocument doc = XDocument.Load(Server.MapPath("~/uploads/banners.xml"));
var q = from node in doc.Descendants("bannerMain")
let fina = node.Element("filename")
where fina != null && fina.Value == fn44
select node;
q.Remove();
doc.Save(Server.MapPath("~/uploads/banners.xml"));
That seems to work for me:
string xmlfile = Server.MapPath("~/uploads/banners.xml");
var xDoc = XDocument.Load(xmlfile);
string fileName = "resdrop.png"; // Value from SQL DB
xDoc.Descendants("filename")
.Where(c => c.Value == fileName)
.Select(x => x.Parent)
.Remove();
xDoc.Save(xmlfile);
Your problem with attempt #1 is that you are trying to compare an IEnumerable<XElement> to your reader value, this should work (assuming each bannerMain only has a single filename element):
var q = from node in doc.Descendants("bannerMain")
let fina = node.Element("filename")//only single filename, so get just that XElement
where fina != null && fina.Value == reader[0]//assumes reader[0] is a string value
select node;
To remove them just do this:
q.Remove();
doc.Save(Server.MapPath("~/uploads/banners.xml"));
I ran this through LINQPad and after doing q.Remove();, here were the contents of doc: <xml />.
It's a little verbose but here is a non-linq snippet:
void DeleteNode(string fileName)
{
XmlDocument doc = new XmlDocument();
doc.Load(Server.MapPath("~/uploads/banners.xml"));
//Get all the bannerMain nodes.
XmlNodeList nodelist = doc.SelectNodes("/xml//bannerMain");
if (nodelist != null)
{
foreach (XmlNode node in nodelist)
{
//Look for then filename child. If it contains desired value
//delete the entire bannerMain node. Assumes order of child nodes
//may not be a constant.
foreach (XmlNode child in node.ChildNodes)
{
if (child.Name == "filename" && child.InnerText == name)
{
node.ParentNode.RemoveChild(node);
}
}
}
doc.Save(Server.MapPath("~/uploads/banners.xml"));
}
}
For Attempt #2, remove the # sign for the filename. The # symbol represents an Attribute, but the filename is a child-node.
If your phrase doesn't work, I'd rephrase it a little from:
"/xml/bannerMain[filename=
to
"//bannerMain[filename=
We are getting an XML document from a vendor that we need to perform an XSL transform on using their stylesheet so that we can convert the resulting HTML to a PDF. The actual stylesheet is referenced in an href attribute of the ?xml-stylesheet definition in the XML document. Is there any way that I can get that URL out using C#? I don't trust the vendor not to change the URL and obviously don't want to hardcode it.
The start of the XML file with the full ?xml-stylesheet element looks like this:
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="http://www.fakeurl.com/StyleSheet.xsl"?>
As a processing instruction can have any contents it formally does not have any attributes. But if you know there are "pseudo" attributes, like in the case of an xml-stylesheet processing instruction, then you can of course use the value of the processing instruction to construct the markup of a single element and parse that with the XML parser:
XmlDocument doc = new XmlDocument();
doc.Load(#"file.xml");
XmlNode pi = doc.SelectSingleNode("processing-instruction('xml-stylesheet')");
if (pi != null)
{
XmlElement piEl = (XmlElement)doc.ReadNode(XmlReader.Create(new StringReader("<pi " + pi.Value + "/>")));
string href = piEl.GetAttribute("href");
Console.WriteLine(href);
}
else
{
Console.WriteLine("No pi found.");
}
Linq to xml code:
XDocument xDoc = ...;
var cssUrlQuery = from node in xDoc.Nodes()
where node.NodeType == XmlNodeType.ProcessingInstruction
select Regex.Match(((XProcessingInstruction)node).Data, "href=\"(?<url>.*?)\"").Groups["url"].Value;
or linq to objects
var cssUrls = (from XmlNode childNode in doc.ChildNodes
where childNode.NodeType == XmlNodeType.ProcessingInstruction && childNode.Name == "xml-stylesheet"
select (XmlProcessingInstruction) childNode
into procNode select Regex.Match(procNode.Data, "href=\"(?<url>.*?)\"").Groups["url"].Value).ToList();
xDoc.XPathSelectElement() will not work since it for some reasone cannot cast an XElement to XProcessingInstruction.
You can also use XPath. Given an XmlDocument loaded with your source:
XmlProcessingInstruction instruction = doc.SelectSingleNode("//processing-instruction(\"xml-stylesheet\")") as XmlProcessingInstruction;
if (instruction != null) {
Console.WriteLine(instruction.InnerText);
}
Then just parse InnerText with Regex.
To find the value using a proper XML parser you could write something like this:
using(var xr = XmlReader.Create(input))
{
while(xr.Read())
{
if(xr.NodeType == XmlNodeType.ProcessingInstruction && xr.Name == "xml-stylesheet")
{
string s = xr.Value;
int i = s.IndexOf("href=\"") + 6;
s = s.Substring(i, s.IndexOf('\"', i) - i);
Console.WriteLine(s);
break;
}
}
}
private string _GetTemplateUrl(XDocument formXmlData)
{
var infopathInstruction = (XProcessingInstruction)formXmlData.Nodes().First(node => node.NodeType == XmlNodeType.ProcessingInstruction && ((XProcessingInstruction)node).Target == "mso-infoPathSolution");
var instructionValueAsDoc = XDocument.Parse("<n " + infopathInstruction.Data + " />");
return instructionValueAsDoc.Root.Attribute("href").Value;
}
XmlProcessingInstruction stylesheet = doc.SelectSingleNode("processing-instruction('xml-stylesheet')") as XmlProcessingInstruction;
I am a beginner to XML and XPath in C#. Here is an example of my XML doc:
<root>
<folder1>
...
<folderN>
...
<nodeMustExist>...
<nodeToBeUpdated>some value</nodeToBeUpdated>
....
</root>
What I need is to update the value of nodeToBeUdpated if the node exists or add this node after the nodeMustExist if nodeToBeUpdated is not there. The prototype of the function is something like this:
void UpdateNode(
xmlDocument xml,
string nodeMustExist,
string nodeToBeUpdte,
string newVal
)
{
/*
search for XMLNode with name = nodeToBeUpdate in xml
to XmlNodeToBeUpdated (XmlNode type?)
if (xmlNodeToBeUpdated != null)
{
xmlNodeToBeUpdated.value(?) = newVal;
}
else
{
search for nodeMustExist in xml to xmlNodeMustExist obj
if ( xmlNodeMustExist != null )
{
add xmlNodeToBeUpdated as next node
xmlNodeToBeUpdte.value = newVal;
}
}
*/
}
Maybe there are other better and simplified way to do this. Any advice?
By the way, if nodeToBeUpdated appears more than once in other places, I just want to update the first one.
This is to update all nodes in folder:
public void UpdateNodes(XmlDocument doc, string newVal)
{
XmlNodeList folderNodes = doc.SelectNodes("folder");
if (folderNodes.Count > 0)
foreach (XmlNode folderNode in folderNodes)
{
XmlNode updateNode = folderNode.SelectSingleNode("nodeToBeUpdated");
XmlNode mustExistNode = folderNode.SelectSingleNode("nodeMustExist"); ;
if (updateNode != null)
{
updateNode.InnerText = newVal;
}
else if (mustExistNode != null)
{
XmlNode node = folderNode.OwnerDocument.CreateNode(XmlNodeType.Element, "nodeToBeUpdated", null);
node.InnerText = newVal;
folderNode.AppendChild(node);
}
}
}
If you want to update a particular node, you cannot pass string nodeToBeUpdte, but you will have to pass the XmlNode of the XmlDocument.
I have omitted the passing of node names in the function since nodes names are unlikely to change and can be hardcoded. However, you can pass these to the functions and use the strings instead of hardcoded node names.
The XPath expression that selects all instances of <nodeToBeUpdated> would be this:
/root/folder[nodeMustExist]/nodeToBeUpdated
or, in a more generic form:
/root/folder[*[name() = 'nodeMustExist']]/*[name() = 'nodeToBeUpdated']
suitable for:
void UpdateNode(xmlDocument xml,
string nodeMustExist,
string nodeToBeUpdte,
string newVal)
{
string xPath = "/root/folder[*[name() = '{0}']]/*[name() = '{1}']";
xPath = String.Format(xPath, nodeMustExist, nodeToBeUpdte);
foreach (XmlNode n in xml.SelectNodes(xPath))
{
n.Value = newVal;
}
}
Have a look at the SelectSingleNode method MSDN Doc
your xpath wants to be something like "//YourNodeNameHere" ;
once you have found that node you can then traverse back up the tree to get to the 'nodeMustExist' node:
XmlNode nodeMustExistNode = yourNode.Parent["nodeMustExist];