Selectnodes for XmlNode cannot find Relationship nodes - c#

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

Related

Insert a node into a XML file

I am trying to add a single line/node (provided below) into an XML:
<Import Project=".www\temp.proj" Condition="Exists('.www\temp.proj')" />
The line could be under the main/root node of the XML:
<Project Sdk="Microsoft.NET.Sdk">
The approach I used:
XmlDocument Proj = new XmlDocument();
Proj.LoadXml(file);
XmlElement root = Proj.DocumentElement;
// Not sure about the next steps
root.SetAttribute("not sure", "not sure", "not sure");
Though I don't exactly know how to add that line in the XML, cause it was my first try on directly editing XML files, the error caused an extra problem over it.
I get this error on my first attempt:
C# "loadxml" 'Data at the root level is invalid. Line 1, position 1.'
Know this error was a famous one, which some provided a variety of approaches in this link:
xml.LoadData - Data at the root level is invalid. Line 1, position 1
Unfortunately, most of the solutions are outdated, the answer didn't work on this case, and I don't know how to apply others on this case.
Provided/accepted answer on the link for that issue:
string _byteOrderMarkUtf8 = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble());
if (xml.StartsWith(_byteOrderMarkUtf8))
{
xml = xml.Remove(0, _byteOrderMarkUtf8.Length);
}
Basically it didn't work, cause xml.StartsWith seems not existing anymore, at the same time xml.Remove also doesn't exist.
Can you please provide a piece of code that bypass the error and add the line to the XML?
Edit:
The sample XML file is provided in the comments section.
For the Xml posted in the comment, I have used two approachs :
1 - XmlDocument
XmlDocument Proj = new XmlDocument();
Proj.Load(file);
XmlElement root = Proj.DocumentElement;
//Create node
XmlNode node = Proj.CreateNode(XmlNodeType.Element, "Import", null);
//create attributes
XmlAttribute attrP = Proj.CreateAttribute("Project");
attrP.Value = ".www\\temp.proj";
XmlAttribute attrC = Proj.CreateAttribute("Condition");
attrC.Value = "Exists('.www\\temp.proj')";
node.Attributes.Append(attrP);
node.Attributes.Append(attrC);
//Get node PropertyGroup, the new node will be inserted before it
XmlNode pG = Proj.SelectSingleNode("/Project/PropertyGroup");
root.InsertBefore(node, pG);
Console.WriteLine(root.OuterXml);
2 - Linq To Xml, by using XDocument
XDocument xDocument = XDocument.Load(file);
xDocument.Root.AddFirst(new XElement("Import",
new XAttribute[]
{
new XAttribute("Project", ".www\\temp.proj"),
new XAttribute("Condition", "Exists('.www\\temp.proj')")
}));
Console.WriteLine(xDocument);
Namespace to add for XDocument:
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
Both solutions give the same result, but the last one is simple.
I hope you find this helpful.
Would it be possible for you to use the official MSBuild libraries?(https://www.nuget.org/packages/Microsoft.Build/)
I'm not sure which nuget package is actually required to read and edit project files only.
I've tried to programatically edit MSBuild project files directly and can not recommend it. It broke regulary due to unexpected changes...
The MSBuild library does a good job in editing project files and e.g. adding Properties, Items or Imports.

Getting child nodes in XML with custom Namespace

I have a rather large XML file from a computer diagnostics session, and my goal is to grab the test results data and pump it into a PDF for the customer. I've very little experience with XML and this is turning out to be a huge problem.
Here is a sample of the Document:
<pcd:DiagLog xmlns="http://www.pc-doctor.com/2004/8/diagLogger"
xmlns:pcd="http://www.pc-doctor.com/2004/8/diagLogger"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.pc-doctor.com/2004/8/diagLogger
http://www.pc-doctor.com/2004/8/diagLogger/diagLogger.xsd">
<Application>
<version>6.0.6818.10</version>
<Start>
<Time hour="04" minute="14" second="01" millisecond="34" month="10" day="15" year="2016" utcOffset="-480">2016-10-15T04:14:01.034-08:00</Time>
</Start>
<OS>Windows 10 Service Pack 0 PE x86 32-bit</OS>
</Application>
.......
<DiagInfo>
....
<TestResult EnglishResult="PASS">
....
</TestResult>
</DiagInfo>
There are thousands of lines between </Application> and <DiagInfo>, but I'm only concerned with the information found in <DiagInfo> and <TestResult>.
I thought I could grab the Nodes by simply:
XmlDocument doc = new XmlDocument();
doc.Load(xmlFilePath);
XmlNamespaceManager manager = new XmlNamespaceManager(doc.NameTable);
manager.AddNamespace("pcd", "http://www.pc-doctor.com/2004/8/diagLogger");
XmlNodeList xnList = doc.SelectNodes("/pcd:DiagLog/DiagInfo", manager);
But this is returning an empty list. When I refer to Namespace Manager or XsltContext needed, it appears I'm doing it right, but I don't think I'm understanding adding a namespace correctly. When I change the Root Element to just: <Diagnostics></Diagnostics> instead of the <pcd:DiagLog>, and try: doc.SelectNodes("/Diagnostics/DiagInfo", manager); my nodes list is populated.
Can anyone see where I'm screwing up the Namespace?
You need to use the namespace prefix for all nodes in that namespace.
This is incorrect: /pcd:DiagLog/DiagInfo.
This is correct: /pcd:DiagLog/pcd:DiagInfo.

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

Determine whether an element exists in XML to Linq

So I'm using Amazon MWS and I was finally able to parse through the ListOrder response, but I have a problem. If there are over 100 orders, it will put a "NextToken" element in the 3rd level. I keep trying to find it, but whenever I run my code, it shows up null even though I know it's there (by looking at the actual XML generated in the response). To clear things up, here's an XML sample (irrelevant Elements redacted) and the code I'm using to read it.
<ListOrdersResponse xmlns="https://mws.amazonservices.com/Orders/2011-01-01">
<ListOrdersResult>
<NextToken>let's just pretend this is a nice token :)</NextToken>
</ListOrdersResult>
</ListOrdersResponse>
And the code:
XElement nextToken = null;
XDocument responseXMLDoc = XDocument.Parse(responseXml);
XNamespace ns = "http://mws.amazonservices.com/schema/Products/2011-10-01";
nextToken = responseXMLDoc.Root.Element(ns + "NextToken");
if (nextToken != null)
{
hasNext = true;
}
else
{
Console.WriteLine("No more pages!");
System.Threading.Thread.Sleep(20000);
}
Every time I run this, even though is always there, I receive a null. I actually have to define the XElement in a parent scope so I can use it later.
Some things I've tried:
Removed "root" from responseXMLDoc.Root.Element
Didn't use the namespace in (ns + "NextToken)
There will only ever be one NextToken element in a request, and I just need the token from it so I can call the request again with the token - and keep repeating until there is no "NextToken".
Update: I'm certain I am getting the syntax wrong, I just can't seem to put my finger on the problem. With the same sample, trying
XElement listOrdersResult = responseXMLDoc.Root.Element(ns + "ListOrdersResult");
will also return a null value! I've read a bunch of questions on the Linq/XML topics here, and that's where I learned most of the syntax. Still not getting any results.
Update 2: Thanks to Brad Cunningham for the answer!
To fix the namespace issue I changed the following [the root node always has the xmlns attribute (and only that attribute):
String docNameSpace = responseXMLDoc.Root.FirstAttribute.Value.ToString();
XNamespace ns = docNameSpace;
And changing the following gives me the element I'm looking for:
nextToken = responseXMLDoc.Root.Descendants(ns + "NextToken").FirstOrDefault();
Your namespace is incorrect in your example (maybe a copy and paste error just for the example)
NextToken has a namespace of
https://mws.amazonservices.com/Orders/2011-01-01
However you are looking for a namespace of
http://mws.amazonservices.com/schema/Products/2011-10-01
Also using Element will only return the element if it is a immediate child of the parent element.
You should use Descendants if you don't know what level the node will be at
This works for me
XDocument responseXMLDoc = XDocument.Parse(responseXml);
XNamespace ns = "https://mws.amazonservices.com/Orders/2011-01-01";
XElement nextToken = responseXMLDoc.Root.Descendants(ns + "NextToken").FirstOrDefault();

Retrieving XmlNode SelectSingleNode Parents Node

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

Categories

Resources