Finding Correct Xpath C# [duplicate] - c#

I'm having this XML document with namespaces and I want to extract some nodes using XPath.
Here's the document:
<ArrayOfAnyType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org/">
<anyType xsi:type="Document">
<Id>5</Id>
<Title>T1</Title>
</anyType>
<anyType xsi:type="Document">
<Id>15</Id>
<Title>T15</Title>
</anyType>
</ArrayOfAnyType>
What's the XPath expression going to be if I want to extract all "anyType" elements with xsi:type="Document"?
I've tried this:
//anyType[#xsi:type="Document"]
and it doesn't work:

If you are using C# then you need to specify the namespace for the "anyType" element in your XPath:
var xml = new XmlDocument();
xml.LoadXml( "your xml" );
var names = new XmlNamespaceManager( xml.NameTable );
names.AddNamespace( "xsi", "http://www.w3.org/2001/XMLSchema-instance" );
names.AddNamespace( "a", "http://tempuri.org/" );
var nodes = xml.SelectNodes( "//a:anyType[#xsi:type='Document']", names );

I think that
//anyType[namespace-uri() = "http://www.w3.org/2001/XMLSchema-instance"][local-name() = "type"]
Will do what you want.

This way you don't need to specify namespace:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml("your xml");
XmlNode node = xmlDoc.SelectSingleNode("/*[local-name() = 'anyType']");
XmlNode nodeToImport = xmlDoc2.ImportNode(node, true);
xmlDoc2.AppendChild(nodeToImport);

Had nearly the same problem, I forgot to add the correct namespace for xsi:type
(http://www.w3.org/2001/XMLSchema-instance) was using http://www.w3.org/2001/XMLSchema
and I did never get any result - now it is working the following way:
<xsl:value-of select="/item1/item2/item3/#xsi:type"></xsl:value-of>

Related

XPath does not work for selecting in XML

I have an XML document and for the life of me I cannot get data by using XPath. I've tried every sample I could find and no luck. I'm trying to extract the email address. Any thoughts?
The XML:
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:hl7-org:v3" xsi:schemaLocation="urn:hl7-org:v3 CDA.xsd">
<typeId root="test1" extension="test2"/>
<id root="test3" extension="test4"/>
<code code="test5" codeSystem="test6" />
<effectiveTime value="201509171214"/>
<confidentialityCode code="N" codeSystem="test7" codeSystemName="test8" displayName="normal"/>
<languageCode code="en"/>
<recordTarget>
<Role>
<id root="000000" extension="number1"/>
<id root="11111" extension="number2"/>
<addr>
<streetAddressLine>Street</streetAddressLine>
<postalCode>12345</postalCode>
<city>City</city>
<state>STATE</state>
<country>COUNTRY</country>
</addr>
<telecom value="number" use="HP"/>
<telecom value="number" use="MC"/>
<telecom value="email#email"/>
<person>
<name>
<family>family</family>
<given>given</given>
<prefix/>
<suffix/>
</name>
<administrativeGenderCode code="C" codeSystem="code" codeSystemName="code name" displayName="c"/>
<birthTime value="N/A"/>
</person>
</Role>
</recordTarget>
</Document>
To load it:
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(STRING DATA FROM XML);
XPathNavigator foo = xmlDocument.CreateNavigator();
foo.MoveToFollowing(XPathNodeType.Element);
foo.Select("Document/recordTarget/Role");
I've also tried:
XmlNodeList xmlNodeList = xmlDocument.SelectNodes("/Document/recordTarget/Role");
But none of this works. Everything comes back empty. Any ideas? I can't seem to navigate past the root.
I've also tried adding the namespace manager in the selects, with no luck.
XmlNamespaceManager manager = new XmlNamespaceManager(xmlDocument.NameTable);
You must add a namespace
XPathNavigator navigator = xmlDocument.CreateNavigator();
XmlNamespaceManager manager = new XmlNamespaceManager(navigator.NameTable);
manager.AddNamespace("ns", "urn:hl7-org:v3");
var role = navigator.Select("/ns:Document/ns:recordTarget/ns:Role", manager);
Try this:
//Create a namespacemanager to get the defaultnamespace of the xml document
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlDocument.NameTable);
//XPath to find the tag that the value isn't equals to number, in this case, it will
//return the <telecom value="email#email"/> element
String xpathQuery = "//role//telecom[#value!='number']";
If you are going to use XPath to get some attribute value, after the XPathNavigator foo = xmlDocument.CreateNavigator(); you should
use the SelectSingleNode method:
//from the XPathNavigator object, we call the SelectSingleNode method, to select a single
//node using the specified xpath query and then call the GetAttribute method to finally
//get the value attribute from that element
String email = foo.SelectSingleNode(xpathQuery).GetAttribute"value",nsmgr.DefaultNamespace);

Modify a xml node with spacing and quotes as attricutes

I am trying to access to the child node of an XML but the my first XML node has spacing and quotes as attributes.
var xml = #"<Envelope xsd "http">
<Catalog>
<Price>
<Value Default ="yes">P1</Value>
</Price>
</Catalog>
</Envelope>";
Im trying to change the attribute value of Default from "yes" to "1" but node always returns null.
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
var node = doc.SelectSingleNode("/*/Catalog/Price/Value");
Any ideas?
I do not think that is valid xml, did you perhaps mean the following
using System;
using System.Globalization;
using System.Xml;
namespace ConsoleApplication9
{
class Program
{
private static void Main(string[] args)
{
//Valid XML
string xml = #"<Envelope xsd='http'>
<Catalog>
<Price>
<Value Default='yes'>P1</Value>
</Price>
</Catalog>
</Envelope>";
var doc = new XmlDocument();
doc.LoadXml(xml);
//Select the Value Node
XmlNode node = doc.SelectSingleNode("/*/Catalog/Price/Value");
//Set the Default attribute to 1
node.Attributes["Default"].Value = 1.ToString(CultureInfo.InvariantCulture);
//Check the output
Console.WriteLine(doc.InnerXml.ToString(CultureInfo.InvariantCulture));
//Press enter to exit
Console.ReadLine();
}
}
}
Just saying.
See http://msdn.microsoft.com/en-us/library/ms256086(v=vs.110).aspx.
Use // instead of /* since // gets the root of the document
This might appear a little hardcoded but it should work:
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
Namespace ns= "http"; //set the namespace of the root node here
//the following is where you change the value to 1
doc.Document.Descendants(ns+"Envelope").FirstorDefault().Descendants(ns+"Catalog").Descendants(ns+"Price").FirstorDefault().Elements("Value").Attribute("Default").SetValue("1");
Also, the xml looks a little wrong to me, as someone mentioned, the root node needs to be corrected.

Getting an XElement with a namespace via XPathSelectElements

I have an XML e.g.
<?xml version="1.0" encoding="utf-8"?>
<A1>
<B2>
<C3 id="1">
<D7>
<E5 id="abc" />
</D7>
<D4 id="1">
<E5 id="abc" />
</D4>
<D4 id="2">
<E5 id="abc" />
</D4>
</C3>
</B2>
</A1>
This is may sample code:
var xDoc = XDocument.Load("Test.xml");
string xPath = "//B2/C3/D4";
//or string xPath = "//B2/C3/D4[#id='1']";
var eleList = xDoc.XPathSelectElements(xPath).ToList();
foreach (var xElement in eleList)
{
Console.WriteLine(xElement);
}
It works perfectly, but if I add a namespace to the root node A1, this code doesn't work.
Upon searching for solutions, I found this one, but it uses the Descendants() method to query the XML. From my understanding, this solution would fail if I was searching for <E5> because the same tag exists for <D7>, <D4 id="1"> and <D4 id="2">
My requirement is to search if a node exists at a particular XPath. If there is a way of doing this using Descendants, I'd be delighted to use it. If not, please guide me on how to search using the name space.
My apologies in case this is a duplicate.
To keep using XPath, you can use something link this:
var xDoc = XDocument.Parse(#"<?xml version='1.0' encoding='utf-8'?>
<A1 xmlns='urn:sample'>
<B2>
<C3 id='1'>
<D7><E5 id='abc' /></D7>
<D4 id='1'><E5 id='abc' /></D4>
<D4 id='2'><E5 id='abc' /></D4>
</C3>
</B2>
</A1>");
// Notice this
XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable());
nsmgr.AddNamespace("sample", "urn:sample");
string xPath = "//sample:B2/sample:C3/sample:D4";
var eleList = xDoc.XPathSelectElements(xPath, nsmgr).ToList();
foreach (var xElement in eleList)
{
Console.WriteLine(xElement);
}
but it uses the Descendants() method to query the XML. From my understanding, this solution would fail if I was searching for because the same tag exists for , and
I'm pretty sure you're not quite understanding how that works. From the MSDN documentation:
Returns a filtered collection of the descendant elements for this document or element, in document order. Only elements that have a matching XName are included in the collection.
So in your case, just do this:
xDoc.RootNode
.Descendants("E5")
.Where(n => n.Parent.Name.LocalName == "B4");
Try this
var xDoc = XDocument.Parse("<A1><B2><C3 id=\"1\"><D7><E5 id=\"abc\" /></D7><D4 id=\"1\"><E5 id=\"abc\" /></D4><D4 id=\"2\"><E5 id=\"abc\" /></D4></C3></B2></A1>");
foreach (XElement item in xDoc.Element("A1").Elements("B2").Elements("C3").Elements("D4"))
{
Console.WriteLine(item.Element("E5").Value);//to get the value of E5
Console.WriteLine(item.Element("E5").Attribute("id").Value);//to get the value of attribute
}

Cannot get XPath to work with unnamed namespace

The XML (fragment):
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetXMLResponse xmlns="http://sitecore.net/visual/">
<GetXMLResult>
<sitecore xmlns="">
<status>ok</status>
The code (fragment):
XmlNamespaceManager nsManager = new XmlNamespaceManager(template.NameTable);
nsManager.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
nsManager.PushScope();
XmlNode sitecoreRoot = template.SelectSingleNode("/soap:Envelope/soap:Body/*[namespace-uri()='http://sitecore.net/visual/' and local-name()='GetXMLResponse']", nsManager);
string status = sitecoreRoot.SelectSingleNode("/GetXMLResult/sitecore/status").Value;
sitecoreRoot element returns the correct node. However the XPath to get the status always returns null, even though the siteCoreRoot.OuterXMl property shows the element is present.
The only thing I can think of is the line:
<sitecore xmlns="">
is throwing off the XPath
TIA
XmlNamespaceManager ns = new XmlNamespaceManager(cd.NameTable);
ns.AddNamespace("sp", "http://schemas.xmlsoap.org/soap/envelope/");
ns.AddNamespace("sc", "http://sitecore.net/visual/");
XmlNode sitecoreRoot = cd.SelectSingleNode("//sp:Envelope/sp:Body/sc:GetXMLResponse/sc:GetXMLResult/sitecore/status", ns);
var status = sitecoreRoot.InnerText;
May help you?
If you just want the status node, you can try this xml library and the following XPath.
XElement root = XElement.Load(file); // or .Parse(string)
XElement status = root.XPathElement("//status");
The library handles doing the namespace for you.

how we can set value for xml element using XmlDocument class

Is it possible to set value dynamically for any XML element using the XmlDocument class? Suppose my XML is
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
<soapenv:Body>
<v9:ProcessShipmentReply xmlns:v9="http://fedex.com/ws/ship/v9">
<v9:HighestSeverity xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">SUCCESS</v9:HighestSeverity>
<v9:Notifications xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<v9:Severity>SUCCESS</v9:Severity>
<v9:Source>ship</v9:Source>
<v9:Code>0000</v9:Code>
<v9:Message>Success</v9:Message>
<v9:LocalizedMessage>Success</v9:LocalizedMessage>
</v9:Notifications>
<v9:CompletedShipmentDetail>
<v9:CompletedPackageDetails xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<v9:SequenceNumber>1</v9:SequenceNumber>
<v9:TrackingIds>
<v9:TrackingIdType>GROUND</v9:TrackingIdType>
<v9:TrackingNumber>634649515000016</v9:TrackingNumber>
</v9:TrackingIds>
<v9:Barcodes>
<v9:BinaryBarcodes>
<v9:Type>COMMON_2D</v9:Type>
<v9:Value>Wyk+HjAxHTAyMDI3ODAdODQwHTEzNx02MzQ2NDk1</v9:Value>
</v9:BinaryBarcodes>
<v9:StringBarcodes>
<v9:Type>GROUND</v9:Type>
<v9:Value>9612137634649515000016</v9:Value>
</v9:StringBarcodes>
</v9:Barcodes>
<v9:Label>
<v9:Type>OUTBOUND_LABEL</v9:Type>
<v9:ShippingDocumentDisposition>RETURNED</v9:ShippingDocumentDisposition>
<v9:Resolution>200</v9:Resolution>
<v9:CopiesToPrint>1</v9:CopiesToPrint>
<v9:Parts>
<v9:DocumentPartSequenceNumber>1</v9:DocumentPartSequenceNumber>
<v9:Image>iVBORw0KGgoAAAANSUhEUgAAAyAAAASwAQAAAAAryhMIAAAagEl</v9:Image>
</v9:Parts>
</v9:Label>
</v9:CompletedPackageDetails>
</v9:CompletedShipmentDetail>
</v9:ProcessShipmentReply>
</soapenv:Body>
How could I set value for the below element like
<v9:Severity>SUCCESS</v9:Severity>
<v9:Source>ship</v9:Source>
I know how to extract data from XML and I think it is also possible to set value for the XML element using XMLDocument class. Looking for guidance.
If you know how to select a value, then you probably know how to update one too.
XmlDocument doc = new XmlDocument();
doc.Load(...);
XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable);
nsMgr.AddNamespace("v9", "http://fedex.com/ws/ship/v9");
XmlNode severityNode = doc.SelectSingleNode("//v9:Severity", nsMgr);
severityNode.innerText = "FAILURE";
The important thing to know is that the <v9:Severity> node has an inner text() node, so in the example above you can't use the Node.Value property. To do that you would do something like this instead:
XmlNode severityTextNode = doc.SelectSingleNode("//v9:Severity/text()", nsMgr);
severityTextNode.Value = "FAILURE";
Note the subtle differences.
Do an XPath to the superior node using the XMLDocument class and add the 2 extra nodes on the tree.

Categories