I have an XmlNode like this:
XmlNode node = document.GetElementById("myid");
Given its attribute:
XmlAttribute a = node.Attributes("type");
Is there a way to obtain back the node?
Something like this:
XmlNode node = a.Node;
I tried ParentNode, but it doesn't seem to work (returning null).
You can use:
XmlElement node = a.OwnerElement;
or using XPath:
XmlNode node = a.SelectSingleNode("..");
In terms of flexibility, the first approach requires the variable a to already be an XmlAttribute or requires you to cast it to one, but it gives you an XmlElement without requiring an explicit cast.
In contrast, a only has to be an XmlNode to use the second approach, but the return value on the method is also an XmlNode.
This SO thread should answer your question
Search XML file for nodes with specific attribute value in .NET 2.
XPath is also an option, because after you get all elements that match you can use Linq to get the specific node that you're interested in.
Related
I'm trying to get values from a XML document using the iXF format, but I'm having some issues with the XPath syntax.
I have the following XML document
<SOAP_ENV:Envelope xmlns:NS2="http://www.ixfstd.org/std/ns/core/classBehaviors/links/1.0" xmlns:NS1="CATIA/V5/Electrical/1.0" xmlns:tns="IXF_Schema.xsd" xmlns:ixf="http://www.ixfstd.org/std/ns/core/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP_ENV="http://schemas.xmlsoap.org/soap/envelope/" xsi:schemaLocation="IXF_Schema.xsd ElectricalSchema.xsd">
<SOAP_ENV:Body>
<ixf:object id="Electrical Physical System00000089.1" xsi:type="tns:Harness">
<tns:Name>Electrical Physical System00000089.1</tns:Name>
</ixf:object>
<ixf:object id="X10(1)//X11(1)" xsi:type="tns:Wire">
<tns:Name>X10(1)//X11(1)</tns:Name>
<NS1:Wire>
<NS1:Length>763,752mm</NS1:Length>
<NS1:Color>RD</NS1:Color>
<NS1:OuterDiameter>1,32mm</NS1:OuterDiameter>
</NS1:Wire>
</ixf:object>
</SOAP_ENV:Body>
</SOAP_ENV:Envelope>
And i'm trying to find all the Wire objects and get the Name and Length values with the following code.
XmlDocument xlDocument = new XmlDocument();
xlDocument.Load(importFile);
XmlNamespaceManager nsManager = new XmlNamespaceManager(xlDocument.NameTable);
nsManager.AddNamespace("tns", "IXF_Schema.xsd");
nsManager.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
nsManager.AddNamespace("ixf", "http://www.ixfstd.org/std/ns/core/1.0");
nsManager.AddNamespace("NS1", "CATIA/V6/Electrical/1.0");
nsManager.AddNamespace("NS2", "http://www.ixfstd.org/std/ns/core/classBehaviors/links/1.0");
//Get all wire objects
XmlNodeList wires = xlDocument.SelectNodes("descendant::ixf:object[#xsi:type = \"tns:Wire\"]", nsManager);
foreach (XmlNode wire in wires)
{
string wireName;
string wireLength;
XmlNode node = wire.SelectSingleNode("./tns:Name", nsManager);
wireName = node.InnerText;
XmlNode node1 = wire.SelectSingleNode("./NS1:Wire/NS1:Length", nsManager);
wireLength = node1.InnerText;
}
I can get the wireName value without any problems but the Length element selection always returns 0 matches and I can not figure out why. I also tried to only select the Wire element using the same syntax as the Name element ./NS1:Wire but that also returns 0 matches.
Your XML declares
xmlns:NS1="CATIA/V5/Electrical/1.0"
^^
Your C# declares a different namespacem
nsManager.AddNamespace("NS1", "CATIA/V6/Electrical/1.0")
^^
Make sure both namespaces match exactly.
Regarding your comment asking about the use of version numbers in namespaces...
It is an unfortunately common but certainly not widely accepted practice to include a version number in an XML namespace. Realize that by doing so, you're effectively saying that every namespaced XML component (element or attribute) should now be considered to differ from its counterpart in the old namespace. This is rarely what you want.
See also
Should I use a Namespace of an XML file to identify its version
What are the best practices for versioning XML schemas?
When selecting from an XmlDocument by e.g. the XPath-method SelectSingleNode we get an XmlNode that consist of the first matching node, lets call it <node1>. If we do further selection on <node1> then one might expect that the XPath-root now is this node, but this is incorrect, the root is still the same as in the original XmlDocument. Here's an example:
XmlDocument xd = new XmlDocument();
xd.LoadXml(#"<root>
<subroot>
<elm>test1</elm>
<elm>test2</elm>
<elm>test3</elm>
</subroot>
</root>");
XmlNode xnSubRoot = xd.SelectSingleNode("/root/subroot");
//This is the XPath I want to be able to use, but it returns null.
XmlNode xnElm = xnSubRoot.SelectSingleNode("/subroot/elm");
//This works, but the XPath-root is the same as in the original document.
xnElm = xnSubRoot.SelectSingleNode("/root/subroot/elm");
Is there any way to "fix" the root of xnSubRoot so that I can use the XPath I want? The reason for my question is because I have a case where I'm calling a webservice that returns an XmlNode where the OuterXml-property shows a structure of "/Data/SubElement/..." and so on, but when running XPath "/Data" then null is returned, only "/SubElement" works, i.e. the XPath-root seems to be one level lower than the actual document-root.
I'm sure there is a perfectly reasonable explanation for this, or that I'm missing something vital. However I really can't seem to find anything, even though I've read http://msdn.microsoft.com/en-us/library/d271ytdx(VS.80).aspx.
N.B. I do realize that it would be possible to use the XPath "//subroot/elm", but then I might also get other elements further down in the XML structure.
Since your selecting from the Root/SubElement Try this:
XmlNode xnElm = xnSubRoot.SelectSingleNode("elm");
It will return the first child elm node of the current node.
Edit (from additionals informations provided in comments):
In this specific case, you are obtaning a XmlNode (which is your Data node) from a WebService call. All XPath requests on that XmlNode will be relative to it.
I would suggest that you modify all your XPaths to use a selector like webServiceNode.SelectSingleNode("SubElement/SubSubElement"); . There is no reason to specify absolute XPaths queries here.
This works:
XmlNode xnSubRoot = xd.SelectSingleNode("/root/subroot");
XmlNode xnElm = xnSubRoot.SelectSingleNode("elm");
And so does this:
XmlNode xnRoot = xd.SelectSingleNode("/root");
XmlNode xnElm = xnRoot.SelectSingleNode("subroot/elm");
Where am i going wrong???
I have an xml file with OppDetails as a tag already as shown below
<OppDetails>
<OMID>245414</OMID>
<ClientName>Best Buy</ClientName>
<OppName>International Rate Card</OppName>
<CTALinkType>AO,IO,MC,TC</CTALinkType>
</OppDetails>
</OppFact>
Now i am trying to add another element to it but getting an error in AppendChild method please help
XmlNode rootNode = xmlDoc.SelectSingleNode("OppDetails");
XmlElement xmlEle = xmlDoc.CreateElement("CTAStartDate");
xmlEle.InnerText = ExcelUtility.GetCTAStartDate();
rootNode.AppendChild(xmlEle);
xmlDoc.Save("C:\\test.xml");
XmlElement xmlEle = xmlDoc.DocumentElement["OppDetails"];
XmlElement eleNew = xmlDoc.CreateElement("CTAStartDate");
eleNew.InnerText = ExcelUtility.GetCTAStartDate();
xmlEle.AppendChild(eleNew);
xmlDoc.Save("C:\\test.xml");
It is hard to tell without a full sample, but a common reason for SelectNodes / SelectSingleNode returning null is xml namespaces. If you xml makes use of element namespaces, you'll probably need to use an XmlNamespaceManager along with your query, and define a suitable alias for the namespace you want.
Is rootNode null?
From MSDN on SelectSingleNode:
The first XmlNode that matches the
XPath query or a null reference
(Nothing in Visual Basic) if no
matching node is found.
If rootNode is null, it indicates that the node could not be found, and trying to use the null rootNode would cause the exception you are seeing.
The exception you've reported means that you have not located the root element. When SelectSingleNode can't find the requested node, it returns null. You didn't check for that.
Read root node and add the new element in to the root node. I think you are trying to append in XML document.
How can I append an XML document to an xml node in c#?
An XmlDocument is basically an XmlNode, so you can append it just like you would do for any other XmlNode. However, the difference arises from the fact that this XmlNode does not belong to the target document, therefore you will need to use the ImportNode method and then perform the append.
// xImportDoc is the XmlDocument to be imported.
// xTargetNode is the XmlNode into which the import is to be done.
XmlNode xChildNode = xSrcNode.ImportNode(xImportDoc, true);
xTargetNode.AppendChild(xChildNode);
Yes:
XmlNode imported = targetNode.OwnerDocument.ImportNode(otherDocument.DocumentElement, true);
targetNode.AppendChild(imported);
I think this creates a clone of your document though.
Perhaps like this:
XmlNode node = ...... // belongs to targetDoc (XmlDocument)
node.AppendChild(targetDoc.ImportNode(xmlDoc.DocumentElement));
Marc
Let's say you have the following construction:
The following structure is stored in an XmlElement named xmlElement:
</root>
and the following structure is stored in an XmlNode object named FooNode;
<foo>
<bar>This is a test</bar>
<baz>And this is another test</baz>
</foo>
Then you do the following:
XmlNode node = doc.ImportNode(FooNode.SelectSingleNode("foo"), true);
xmlElement.AppendChild(node);
Hope it helps someone
Once you have the root node of the XML document in question you can append it as a child node of the node in question. Does that make sense?
I have loaded XmlDocument into memory and created new XmlElement. Now I am trying to add XmlElement to the path /report/section/hosts but I don't know how. I can add it easily below root node of XML but I cannot figure out how can I navigate deeper level in XML and just append there. In pseudo I am trying to do this:
doc.SelectNodes("/report/section/hosts").AppendChild(subRoot);
The code:
XmlDocument doc = new XmlDocument();
doc.Load("c:\\data.xml");
//host
XmlElement subRoot = doc.CreateElement("host");
//Name
XmlElement ElName = doc.CreateElement("name");
XmlText TxtName = doc.CreateTextNode("text text");
ElName.AppendChild(TxtName);
subRoot.AppendChild(ElName);
doc.DocumentElement.AppendChild(subRoot);
doc.Save("c:\\data.xml");
Try SelectSingleNode instead of SelectNodes
XmlElement parent = (XmlElement)doc.SelectSingleNode("/report/section/hosts")
parent.AppendChild(subRoot);
You are almost there. Try using SelectSingleNode instead:
XmlNode node = doc.SelectSingleNode("/report/section/hosts");
node.AppendChild(subRoot);
The SelectNodes method returns a list of Nodes.
You should use SelectSingleNode instead...
e.g. (top of my head, did not test in Visual Studio)
doc.SelectSingleNode("/report/section/hosts").AppendChild(subRoot);
You need to get a reference to an XmlElement in your doc (other than the root) to append to. There are a number of methods available on XmlDocument such as GetElementById and SelectSingleNode which do this for you in different ways, research to taste.
That said, the whole API in this area is generally regarded as a bit painful, do you have LINQ available?