How do I check particular attributes exist or not in XML? - c#

Part of the XML content:
<section name="Header">
<placeholder name="HeaderPane"></placeholder>
</section>
<section name="Middle" split="20">
<placeholder name="ContentLeft" ></placeholder>
<placeholder name="ContentMiddle"></placeholder>
<placeholder name="ContentRight"></placeholder>
</section>
<section name="Bottom">
<placeholder name="BottomPane"></placeholder>
</section>
I want to check in each node and if attribute split exist, try to assign an attribute value in a variable.
Inside a loop, I try:
foreach (XmlNode xNode in nodeListName)
{
if(xNode.ParentNode.Attributes["split"].Value != "")
{
parentSplit = xNode.ParentNode.Attributes["split"].Value;
}
}
But I'm wrong if the condition checks only the value, not the existence of attributes. How should I check for the existence of attributes?

You can actually index directly into the Attributes collection (if you are using C# not VB):
foreach (XmlNode xNode in nodeListName)
{
XmlNode parent = xNode.ParentNode;
if (parent.Attributes != null
&& parent.Attributes["split"] != null)
{
parentSplit = parent.Attributes["split"].Value;
}
}

If your code is dealing with XmlElements objects (rather than XmlNodes) then there is the method XmlElement.HasAttribute(string name).
So if you are only looking for attributes on elements (which it looks like from the OP) then it may be more robust to cast as an element, check for null, and then use the HasAttribute method.
foreach (XmlNode xNode in nodeListName)
{
XmlElement xParentEle = xNode.ParentNode as XmlElement;
if((xParentEle != null) && xParentEle.HasAttribute("split"))
{
parentSplit = xParentEle.Attributes["split"].Value;
}
}

Just for the newcomers: the recent versions of C# allows the use of ? operator to check nulls assignments
parentSplit = xNode.ParentNode.Attributes["split"]?.Value;

You can use LINQ to XML,
XDocument doc = XDocument.Load(file);
var result = (from ele in doc.Descendants("section")
select ele).ToList();
foreach (var t in result)
{
if (t.Attributes("split").Count() != 0)
{
// Exist
}
// Suggestion from #UrbanEsc
if(t.Attributes("split").Any())
{
}
}
OR
XDocument doc = XDocument.Load(file);
var result = (from ele in doc.Descendants("section").Attributes("split")
select ele).ToList();
foreach (var t in result)
{
// Response.Write("<br/>" + t.Value);
}

var splitEle = xn.Attributes["split"];
if (splitEle !=null){
return splitEle .Value;
}

EDIT
Disregard - you can't use ItemOf (that's what I get for typing before I test). I'd strikethrough the text if I could figure out how...or maybe I'll simply delete the answer, since it was ultimately wrong and useless.
END EDIT
You can use the ItemOf(string) property in the XmlAttributesCollection to see if the attribute exists. It returns null if it's not found.
foreach (XmlNode xNode in nodeListName)
{
if (xNode.ParentNode.Attributes.ItemOf["split"] != null)
{
parentSplit = xNode.ParentNode.Attributes["split"].Value;
}
}
XmlAttributeCollection.ItemOf Property (String)

You can use the GetNamedItem method to check and see if the attribute is available. If null is returned, then it isn't available. Here is your code with that check in place:
foreach (XmlNode xNode in nodeListName)
{
if(xNode.ParentNode.Attributes.GetNamedItem("split") != null )
{
if(xNode.ParentNode.Attributes["split"].Value != "")
{
parentSplit = xNode.ParentNode.Attributes["split"].Value;
}
}
}

Another way to handle the situation is exception handling.
Every time a non-existent value is called, your code will recover from the exception and just continue with the loop. In the catch-block you can handle the error the same way you write it down in your else-statement when the expression (... != null) returns false. Of course throwing and handling exceptions is a relatively costly operation which might not be ideal depending on the performance requirements.

Related

Accessing a sub attribute in a XML file using XElement

In C#, I am trying to change an option of a feature in an XML file which presents a Print Ticket and loaded as an XElement by the following code:
XElement ticketRootXElement = null;
using (Stream ticketReadStream = displayedPrintTicket.GetReadStream())
{
ticketRootXElement = XElement.Load(ticketReadStream);
}
The partial XML is something like following:
<?xml version="1.0"?>
<psf:Feature xmlns:psf="http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework" name="psk:PageMediaSize">
<psf:Option name="psk:ISOA4">
<psf:ScoredProperty name="psk:MediaSizeWidth">
<psf:Value xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:integer">210000</psf:Value>
</psf:ScoredProperty>
<psf:ScoredProperty name="psk:MediaSizeHeight">
<psf:Value xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:integer">297000</psf:Value>
</psf:ScoredProperty>
</psf:Option>
</psf:Feature>
How can I access the "Option" of a specific "Feature" and change it to something like <psf:Option name="psk:ISOA3">?
I tried the following code, but it fails.
foreach (XAttribute xAttr in ticketRootXElement.Descendants(xns_psf + "Feature").Attributes())
{
if (xAttr.Value.Equals("psk:PageMediaSize"))
{
foreach(XAttribute xSubAttr in ticketRootXElement.Element("PageMediaSize").Descendants(xns_psf + "Option").Attributes())
{
if (xAttr.NextAttribute.Name.LocalName.Equals("name"))
{
xAttr.NextAttribute.SetValue("psk:ISO" + cmb_PaperSize.SelectedValue.ToString());
}
}
}
}
You can can modify the option value of your selected feature as as follows:
var featureName = "psk:PageMediaSize";
var newOptionValue = "psk:ISOA3"; // Your modified value here
XNamespace xns_psf = #"http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework";
var query = from f in ticketRootXElement.DescendantsAndSelf(xns_psf + "Feature")
where (string)f.Attribute("name") == featureName
select f;
foreach (var f in query)
{
// TODO: handle the situation were a child <psf:Option> element is missing.
f.Element(xns_psf + "Option").SetAttributeValue("name", newOptionValue);
}
Notes:
XElement.Attribute(XName) can be used to look up an attribute by name, and XElement.SetAttributeValue(XName, Object) can be used to set or add an attribute value by name.
Casting an XAttribute to a string returns the value of the attribute, or null if the attribute was missing, and so is convenient to use when filtering by attribute value in a where statement.
If the selected <psf:Feature> element does not have a child <psf:Option> element, the above code will throw an exception. You will need to check your XML schema to determine whether this is possible, and if so, how to handle it.
Demo fiddle here.
Actually I did it using the following code. But "bdc" solution (above answer) sounds much better:
var element = ticketRootXElement.Descendants(xns_psf + "Feature")
.Where(arg => arg.Attribute("name").Value == "psk:PageMediaSize")
.Single();
var subelement = element.Descendants(xns_psf + "Option")
.Single();
subelement.FirstAttribute.SetValue("psk:ISOA3");

XML Node Attribute returning as NULL when populated?

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);
}

Check if any XML nodes exists with Specific Attribute using LINQ C#

This is my XML:
<configuration>
<Script name="Test Script">
<arguments>
<argument key="CheckStats" value="True" />
<argument key="ReferenceTimepoint" value="SCREENING" />
<argument key="outputResultSetName" value="ResultSet" />
</arguments>
</Script>
</configuration>
I am trying to use this linq statement to grab an argument element's value attrbiute if a specific key attribute exists.
XElement root = XElement.Load(configFileName);
var AttrVal = from el in root.Elements("Script").Elements("arguments").Elements("argument")
where el.Attribute("key").Value == "CheckStats"
select el.Attribute("value").Value;
Then I want to try and parse the attribute value into a boolean:
bool checkVal;
if (AttrVal != null)
{
if (!bool.TryParse(AttrVal.First().ToString(), out checkVal))
{
throw new Exception(string.Format("Invalid value"));
}
}
This code works if there is an element with that attribute, but if there isn't one, I get a System.InvalidOperationException: Sequence contains no elements.
How can I get around that?
I thought by checking if (AttrVal != null) it would work.
Should I replace that with if (AttrVal.FirstOrDefault() != null) or something similar?
Thanks
In if statement, you can write
if (AttrVal != null && AttrVal.Any())
EDIT: I'm wrong. The exception should come from First(), not any of Elements(). Old answer:
from el in root.Descendants("argument")
Or
from el in root.XPathSelectElements("./Script/arguments/argument")
you have to check if there is already your attribute in the element where el.Attributes("key")!=null&&
XElement root = XElement.Load("config.xml");
var AttrVal = from el in root.Elements("Script").Elements("arguments").Elements("argument")
where el.Attributes("key")!=null&& el.Attribute("key").Value == "CheckStats"
select el.Attribute("value").Value;
bool checkVal;
if (AttrVal != null)
{
if (!bool.TryParse(AttrVal.First().ToString(), out checkVal))
{
throw new Exception(string.Format("Invalid value"));
}
}
Here's a way to eliminate those pesky null checks - seek ahead with XPath to determine whether a node with both the necessary attributes (viz key="CheckStats" AND a value) exists, then parse it.
bool checkVal;
// using System.Xml.XPath;!
var el = root.XPathSelectElement(
"/Script/arguments/argument[#key='CheckStats' and #value]");
if (el != null && !bool.TryParse(el.Attribute("value").Value,
out checkVal))
{
throw new Exception(string.Format("Invalid value"));
}

How do i handle if a NodeSelect is nill in C#

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();
}
}

Update or inserting a node in an XML doc

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];

Categories

Resources