Why simple XPATH doesn't work
I have an XML (in BizTalk) that looks like
<ns0:Provide xmlns:ns0="http1" xmlns:ns1="http2" xmlns:ns2="http://schemas.microsoft.com/2003/10/Serialization/">
<ns0:providerRequest>
<ns1:Header>
<ns1:Operation>Get_RU_PatchData</ns1:Operation>
<ns1:RequestId>f6bbeb27-1bfd-4d9c-90e4-d195baf8ca60</ns1:RequestId>
<ns1:SendDate>2004-02-14T21:44:14</ns1:SendDate>
<ns1:SenderSystemName>temperat iras</ns1:SenderSystemName>
</ns1:Header>
<ns1:Parameters>
<ns1:Parameter>
<ns1:Name>turbine corripuit</ns1:Name>
<ns1:Value>regemque dedit</ns1:Value>
</ns1:Parameter>
</ns1:Parameters>
</ns0:providerRequest>
</ns0:Provide>
I try to get the value of the only Parameter in Parameters. The question is why a statement like this
st开发者_运维百科ring(/*[local-name()='Provide' and namespace-uri()='http1']/*[local-name()='providerRequest' and namespace-uri()='http1']/*[local-name()='Parameters' and namespace-uri()='http2'][1]/*[local-name()='Parameter' and namespace-uri()='http2']/*[local-name()='Name'])
work fine while this
string(/Provide/providerRequest/Parameters[1]/Parameter/Name)
gives me nothing? Is there a way not to create such monstrous statements with namespaces?
Depending on your XPath implementation you can register XML namespaces with shortcuts so that you can write the XPath expression like that (given that you registered http1
as h1
and http2
as h2
.
string(/h1:Provide/h1:providerRequest/h2:Parameters[1]/h2:Parameter/h2:Name)
Just declare the namespaces in the XSLT document as well, and then you can use:
string(/ns0:Provide/ns0:providerRequest/ns1:Parameters[1]/ns1:Parameter/ns1:Name)
By just looking at the local names, you effectively ignore namespaces at all - why did you add them in the first place then?
I like to put all my constants, including XPaths, in a static class and then reference that in my BizTalk project. This has a number of advantages, including:
- It makes it easier to unit test and edit those constants;
- It makes the expressions shapes shorter and easier to read.
This doesn't solve the problem of long XPaths, although you could always use String.Format()
to make them more readable. However, it does have the advantages described above...
I know this has been a while but what worked for me is to NOT reference the root node (that contains the namespaces) in your xpath, then it works clean (e.g //Parameter[1]). But it seems you have namespaces on each node, so if it's possible to not qualify all the nodes but the root in your schema, this would help. There's something about mixing namespaces in an xpath that BizTalk doesn't like. Hope this helps someone.
There's nothing really different with the way BizTalk uses Xpath than e.g. .Net
or Java
parsers - as soon as an xml document uses namespaces, you'll need to specify the namespace (or alias) when making references elements.
Biztalk circumvents the need for a XmlNamespaceManager by using namespace agnostic xpath, which as you've noted, is rather verbose (but then again, it is generated and usually hidden from sight).
If you are confident that the element names are unique in your xml, you can drop the namespace-uri()
bit from your paths, i.e.
string(/*[local-name()='Provide']/*[local-name()='providerRequest']
/*[local-name()='Parameters'][1]/*[local-name()='Parameter']
/*[local-name()='Name'])
And if you are really confident about the uniqueness of your element names, you can short circuit the path:
string(//*[local-name()='SomeUniqueElementName']
But be wary - for large xml documents, I've encountered performance issues with double slash xpath navigation.
精彩评论