Retrieving XmlNode SelectSingleNode Parents Node - c#

stack overflow has helped me a ton and decided to join and ask a question myself.
My process that I am trying to do is basically select a node out of an XML document and delete the entire node that the user had selected.
Now for some code!
int index = index = list_serverlist.SelectedIndex;
string selectedItem = list_serverlist.Items[index].ToString();
XmlNode selectedNode = doc.SelectSingleNode("/ServerList/Server/ServerName[text()='" + selectedItem + "']");
selectedNode.ParentNode.RemoveAll();
doc.Save(filePath);
Also the XML file that I am using
<?xml version="1.0"?>
<ServerList>
<Server>
<ServerName>FAB13-HST01</ServerName>
<ServerIP>wasd</ServerIP>
<ServerUsername>..\Administrator</ServerUsername>
<ServerPassword>wasd</ServerPassword>
</Server>
<Server>
<ServerName>FAB13-HST02</ServerName>
<ServerIP>wasd</ServerIP>
<ServerUsername>..\Administrator</ServerUsername>
<ServerPassword>wasd</ServerPassword>
</Server>
<Server>
<ServerName>FAB13-HST03</ServerName>
<ServerIP>wasd</ServerIP>
<ServerUsername>..\Administrator</ServerUsername>
<ServerPassword>wasd</ServerPassword>
</Server>
</ServerList>
Now how I see that code happening is...
basically I get what the user selected out of the ListBox make it a string and than select the single node that has that in the ServerName field. Which when debugging seems to work fine.
However when I use the command
selectedNode.ParentNode.RemoveAll();
It deletes all childs of the node, and not including the parent null. When I debug it and try to get the Parent it seems to be returning null for some odd reason and can't figure out why.
New to XML so not sure what I am doing wrong...

If you try to get the parent after calling RemoveAll(), the selected node no longer exists.
To remove the whole server element, you could use something like.
XmlNode nodeParent = selectedNode.ParentNode;
nodeParent.ParentNode.RemoveChild(nodeParent);

Related

Checking in xml if node exists c#

I send some request and i get xml response sometimes i get
<?xml version="1.0" encoding="UTF-8"?>
<debt-response>
<status>0</status>
<name>ნ.ს.</name>
<schedules>
<schedule>07.07.2017 1171.8000 GEL 1</schedule>
<schedule>07.08.2017 1171.8000 GEL 1</schedule>
<schedule>07.09.2017 1171.8000 GEL 1</schedule>
</schedules>
</debt-response>
and sometimes i get
<?xml version="1.0" encoding="UTF-8"?>
<debt-response>
<status>0</status>
<name>ნ.ბ.</name>
<schedules>
<schedule>06.07.2018 1.5 GEL 1</schedule>
<debt>15.06.2018 0.97</debt>
</schedules>
</debt-response>
I am using var acc_numArray = xmlDoc.SelectNodes("/debt-response/schedules/debt"); but if no such element exists it goes in exception.
I want to get that debt if such node exists any solution ?
You could try
var debt = XDocument.Load("path to your xml").Descendants("debt").FirstOrDefault()?.Value;
which gets the first debt element and returns its value.
If you have more than one debt, the following code saves the element values in a list:
var debt = XDocument.Load("path to your xml").Descendants("debt").Select(d => d.Value).ToList();
If you are reading the xml from a stream, use XDocument.Parse("your string") instead of Load().
Note: you will need the System.Xml.Linq namespace.
Without seeing your code, I can only guess what you're doing - but one thing is certain, this line will not throw an exception if no nodes can be located by the specified XPath:
var acc_numArray = xmlDoc.SelectNodes("/debt-response/schedules/debt");
Instead, it will return an XmlNodeList with a count of zero.
I am assuming your exception is occurring when you try and access the nodelist as if it had items in it - but simply check the count and only access it if it is non-zero:
var acc_numArray = xmlDocument.SelectNodes("/debt-response/schedules/debt");
if (acc_numArray.Count > 0)
{
// Do stuff here
}
If you put an example of your actual code into the question it will be a lot easier to help you.

Selectnodes for XmlNode cannot find Relationship nodes

Hullo,
I am trying to use XmlNode's SelectNodes method to get the ID information below. I need the ID to tie it to an image reference from it. I've used selectnodes with namespaces before successfully, but I think it has something to do with the namespace for relationships specifically, since it's defining namespaces within this node.
<pkg:package xmlns:pkg="http:blah">
<pkg:part pkg:name="/_rels/.rels" pkg:contentType="blah" pkg:padding="512">...</pkg:part>
<pkg:part pkg:name="blah" pkg:contentType="blah" pkg:padding="256">
<pkg:xmlData>
<Relationships xmlns="http:blah">
<Relationship Id="rId8" Type="http:blah" Target="media/image2.png/>
<Relationship Id="rId3" Type="http:blah" Target="media/image3.png/>
Things I've tried that have returned no results:
...SelectNodes("//pkg:package/pkg:part/pkg:xmlData/Relationships/Relationship[#Type='blah'", nsm);
...SelectNodes("//Relationships", nsm);
...SelectNodes("//xmlns:Relationships", nsm);
...SelectNodes("//Relationship", nsm);
...SelectNodes("//Relationship[#Type='http:blah'], nsm);
and so on and so on. I hoped this would help but it didnt work.
here is my setup of nsm...
nsm = new XmlNamespaceManager(xmldocin.NameTable);
nsm.AddNamespace ("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
nsm.AddNamespace("pkg", "http://schemas.microsoft.com/office/2006/xmlPackage");
nsm.AddNamespace("r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships");
nsm.AddNamespace("a", "http://schemas.openxmlformats.org/drawingml/2006/main");
nsm.AddNamespace("pic", "http://schemas.openxmlformats.org/drawingml/2006/picture");
nsm.AddNamespace("wp", "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing");
If I use pkg:xmlData in the select query works just fine, so I know I have the rest of my code working fine.The problem is specifically with the "Relationship" nodes.
The actual information I need is the ID so i can correlate it to the target so i know which image to use when i hit that ID going through the document.
Any ideas?
Add the default namespace to your namespace manager, using some key (for instance ns):
nsm.AddNamespace("ns", ...)
then you can target the nodes of this default namespace with that key:
.. SelectNodes("//ns:Relationship", nsm);

Parsing XFDL Contents - C#

I am tasked with ripping and stripping pertinent data from XFDL files. I am attempting to use XmlDocument's SelectSignleNode method to do so. However, it has proven unsuccessful.
Represntative XML:
<XFDL>
...
<page1>
<check3>true</check3>
</page1>
...
<page sid="PAGE1">
<check sid="CHECK9">
<value>true</value>
</check>
</page>
...
Code:
XmlDocument document = new XmlDocument();
document.Load(memoryStream);//decoded and unzipped xfdl file
//Doesn't work
XmlNode checkBox = document.SelectSingleNode("//check[#sid='CHECK9']/value");
//Doesn't work
XmlNode checkBox = document.SelectSingleNode("//page[#sid='PAGE1']/check[#sid='CHECK9']");
MsgBox(checkBox.InnerXml);
Yields me System.NullReferenceException as an XmlNode isn't selected.
I think I'm having an xpath issue but I can't seem to understand where. The earlier xml node is easily selected using:
XmlNode checkBox = document.SelectSingleNode("//page1/check3");
MsgBox(checkBox.InnerText);
Displays just fine. And just to head it off at the pass, there isn't a definition of <check9></check9> in the <page1> tag.
Anyone have some insight?
Thanks in advance.
Okay, so here's the deal. XFDL defines a default namespace that requires an arbitrary mapping for xpath querying. In my case:
XML:
<XFDL xmlns="http://www.ibm.com/xmlns/prod/xfdl/8.0" ... >
Code:
manager.AddNamespace("a", "http://www.ibm.com/xmlns/prod/xfdl/8.0");
//Append 'a:' to query elements
document.SelectSingleNode("//a:check[#sid='CHECK9']/a:value", manager);
The problem is compounded because <check> is buried in <page> which is defined in another namespace: xfdl. My xpath query becomes:
document.SelectSingleNode("//xfdl:page[#sid='PAGE1']/a:check[#sid='CHECK9']/a:value", manager);
Now this is rather XFDL specific but can be applied to other issues where there are multiple namespaces defined within an XML document.
EDIT 1
Source: http://codeclimber.net.nz/archive/2008/01/09/How-to-query-a-XPath-doc-that-has-a-default.aspx

SelectSingleNode() with XPath C# Failure

i got a XML File as export from Wireshark and want to select the Number of the actual frame
The structure of this file is like this
<packet>
<proto>
...
</proto>
....
<proto>
<field name="frame.number" show="1">
</proto>
</packet>
<packet>
<proto>
...
</proto>
....
<proto>
<field name="frame.number" show="2">
</proto>
</packet>
...and so on...
I use this code to select the packets/fields
XmlNodeList packages = xmlDoc.SelectNodes("//packet");
foreach (XmlNode packet in packages) {
string frameNumber = packet.SelectSingleNode("//field[#name='frame.number']").
Attributes["show"].Value;
Console.WriteLine(frameNumber);
}
If I Debug through the code, it always selects the right Nodes with the correct attributes. But at each iteration there is a "1" printed out.
Does anyone suspect what failure this is? I didn't found anything on the internet for this failure
Thank you very much!!
Its because your XPath in SelectSingleNode starts with // - which means "start from the root of the document". Therefore you're always getting the first one.
Just change the XPath in that method to proto/field[#name='frame.number'].

Is there a bug in my XML code or in .NET?

I just ran into an issue where my code was parsing xml fine but once I added in a second node it started to load incorrect data. The real code spans a number of classes and projects but for the sample I've put together the basics of what's causing the issue
When the code runs I'd expect the output to be the contents of the second Task node, but instead the contents of the first node is output. It keeps pulling from the first occurrence of the EmailAddresses node despite how when you check the settings object its inner xml is that of the second Task node. The call to SelectSingleNode("//EmailAddresses") is where the issue happens.
I have two ways around this issue
Remove the leading slashes from the EmailAddresses XPath expression
Call Clone() after getting the Task or Settings node
Solution 1 works in this case but I believe this will cause other code in my project to stop working.
Solution 2 looks more like a hack to me than a real solution.
MY question is am I in fact doing this correctly and there's a bug in .NET (all versions) or am I just pulling the XML wrong?
The c# code
var doc = new XmlDocument();
doc.Load(#"D:\temp\Sample.xml");
var tasks = doc.SelectSingleNode("Server/Tasks");
foreach (XmlNode threadNode in tasks.ChildNodes)
{
if (threadNode.Name.ToLower() != "thread")
{
continue;
}
foreach (XmlNode taskNode in threadNode.ChildNodes)
{
if (taskNode.Name.ToLower() != "task" || taskNode.Attributes["name"].Value != "task 1")
{
continue;
}
var settings = taskNode.SelectSingleNode("Settings");
var emails = settings.SelectSingleNode("//EmailAddresses");
Console.WriteLine(emails.InnerText);
}
}
The XML
<?xml version="1.0"?>
<Server>
<Tasks>
<Thread>
<Task name="task 2">
<Settings>
<EmailAddresses>task 2 data</EmailAddresses>
</Settings>
</Task>
</Thread>
<Thread>
<Task name="task 1">
<Settings>
<EmailAddresses>task 1 data</EmailAddresses>
</Settings>
</Task>
</Thread>
</Tasks>
</Server>
From http://www.w3.org/TR/xpath/#path-abbrev
// is short for
/descendant-or-self::node()/. For
example, //para is short for
/descendant-or-self::node()/child::para
and so will select any para element in
the document (even a para element that
is a document element will be selected
by //para since the document element
node is a child of the root node);
And also:
A location step of . is short for
self::node(). This is particularly
useful in conjunction with //. For
example, the location path .//para
is short for
self::node()/descendant-or-self::node()/child::para
and so will select all para descendant
elements of the context node.
Instead of:
var settings = taskNode.SelectSingleNode("Settings");
var emails = settings.SelectSingleNode("//EmailAddresses");
Use:
var emails = taskNode.SelectSingleNode("Settings/EmailAddresses");
The // XPath expression does not do what you think it does. It selects nodes in the document from the current node that match the selection no matter where they are.
In other words, it's not limited by the current scope, it actually crawls back up the document tree and starts matching from the root element.
To select the first <EmailAddresses> element in your current scope, you only need:
var emails = settings.SelectSingleNode("EmailAddresses");

Categories

Resources