Using xslt 1.0 (BizTalk 2016) I'm looking for a generic way to select the namespace of any valid xml document
For example, I have the following xml document:
<?xml version="1.0" encoding="utf-8"?>
<PortfolioActivation xmlns="http://www.random.com/bo/request/portfolioactivation">
<Portfolio>
<ExternalId>PRT-00000450</ExternalId>
<InternalId>c8b0239c-1e98-e911-a8b1-00224800449b</InternalId>
<Version>8627558</Version>
<Type>001</Type>
</Portfolio>
</PortfolioActivation>
Given that the Root element value could be anything, what would be the xpath to select the value of the namespace i.e http://www.random.com/bo/request/portfolioactivation
I had hoped "/*/#xmlns" would work but it doesn't.
The namespace of the outermost element can be found using namespace-uri(/*).
Alternatively, the default namespace that's in scope for the outermost element is /*/namespace::*[name()=''].
These aren't the same thing. Consider
<p:root xmlns="a.ns" xmlns:p="b.ns"/>
The first expression will give you "b.ns", the second will give you "a.ns". It's not clear from your question which you want.
Note that namespaces are not attributes in the XDM data model, so you never access them using the attribute axis. #xmlns will therefore never work.
Related
I have the following XML:
<envelope>
<action>INSERT</action>
<auditId>123</auditId>
<payload class="vendor">
<fizz buzz="3"/>
</payload>
</envelope>
I am trying to write an XPath expression that will pluck out vendor (value for the payload's class attribute) or whatever its value is.
My best attempts are:
/dataEnvelope/payload[#class="vendor"]#class
But this requires the expression to already know that vendor is the value of the attribute. But if the XML is:
<dataEnvelope>
<action>INSERT</action>
<auditId>123</auditId>
<payload class="foobar">
<fizz buzz="3"/>
</payload>
</dataEnvelope>
Then I want the expression to pluck out the foobar. Any ideas where I'm going awry?
If you need #class value from payload node, you can use
/dataEnvelope/payload[#class]/#class
or just
/dataEnvelope/payload/#class
At first, your two XML files are out-of-sync - one references envelope and the other references dataEnvelope. So exchange one for the other, if necessary.
So, to get the attribute value of payload, you can use an XPath expression like this which uses a child's attribute value to be more specific:
/envelope/payload[fizz[#buzz='3']]/#class
Output is:
vendor
If the document element can/will change, then you can keep the XPath more generic and select the value of the class attribute from the payload element that is a child of any element:
/*/payload/#class
If you know that it will always be a child of envelope, then this would be more specific(but the above would still work):
/envelope/payload/#class
<?xml version="1.0" encoding="UTF-8"?>
<serviceOfferings xmlns="http://www.abc.com/aaa" xmlns:ns2="http://www.w3.org/2005/Atom">
<serviceOffering type="provider">
<links>
<link title="Service Provider" type="application/xml" rel="self" href="https://www.yahoo.com"/>
<link rel="create" href="https://www.google.com/create"/>
</links>
</serviceOffering>
</serviceOfferings>
How do I get the href value for the link which attribute rel is create
The target element is in a default namespace. First, you'll need to register that namespace with your XPath engine. How you do this depends on the tool you're using, which means it's outside the scope of this question (since you haven't told us how you're evaluting your expressions).
Let's assume you've registered the namespace to a prefix called aaa. Select the desired link like this:
//aaa:link[#rel='create']
Or, more specifically:
/*/*/*/aaa:link[#rel='create']
For completeness, in most cases this can also be achieved without registering a namespace, at the cost of reduced readability:
/*/*/*/*[name()='link' and #rel='create']
This selects all elements whose name is "link" (regardless of the namespace they belong to) and that have a rel attribute whose string value is "create", and that are grand-grand-children of the top element of the XML document.
I would like to select an attribute based on its parent element.
One way to do it is the following:
<xsl:template match="#Name[name(..) = 'EntityType' and namespace-uri(..)= 'http://schemas.microsoft.com/ado/2008/09/edm']">
Is it possible to do it in a shorter way - something similar to (whic does not work)
#Name[../edm:EntiyType]
Namespace prefixes are already defined in the document (I can for example select //*/edm:EntityType)
Thanks,
Matra
Depending how you apply your templates the following should work
<xsl:template match="edm:EntiyType/#Name">
I have firexpath, and it doesn't seem to be working with xpath. Even something simple //div returns no results. Even if I click on an existing node, say "copy XPath" and then paste that XPath into filter input box, it says "no nodes found". //*[name()='div'] does work though. Am I missing a namespace or something? Here is what root tag looks like (it's a valid XHTML):
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us" class="ff ff3">
I didn't find a support forum for FireXPath, so I'm posting it here.
In case you cannot register a namespace (for the default namespace) and then prefix every element name in the XPath expression with the registered prefix, then you can use an XPath expression like this:
/*[name()='x']/*[name()='y']/*[name()='z']
In case there are elements belonging to other namespaces (than the default namespace), you'll have to use a more specific XPath expression:
/*[name()='x' and namespace-uri()='http://www.w3.org/1999/xhtml']
/*[name()='y' and namespace-uri()='http://www.w3.org/1999/xhtml']
/*[name()='z' and namespace-uri()='http://www.w3.org/1999/xhtml']
If you could have registered the default namespace and the prefix was (say) "p", then the above would be equivalent to a now simpler expression:
/p:x/p:y/p:z
I have not used firexpath but it looks as though the default namespace xmlns="http://www.w3.org/1999/xhtml" is preventing xpath from finding the div as the div element inside the element which defies xmlns with be prefixed with that namespace.
You therefore would need to register the namespace with firexpath using some sort of method call??? then //div should work or your expression is fine as well, if you were wanting to consider namespaces in the expression you could include a check for the namespace like so
//*[name()='div' and namespace-uri()='http://www.w3.org/1999/xhtml']
EDIT:
I have downloaded firexpath which now is called firepath and it doesn't look possible to register a namespace so it looks like you will have to the name() method
I'm using Nokogiri::XML to parse responses from Amazon SimpleDB. The response is something like:
<SelectResponse xmlns="http://sdb.amazonaws.com/doc/2007-11-07/">
<SelectResult>
<Item>
<Attribute><Name>Foo</Name><Value>42</Value></Attribute>
<Attribute><Name>Bar</Name><Value>XYZ</Value></Attribute>
</Item>
</SelectResult>
</SelectResponse>
If I just hand the response straight over to Nokogiri, all XPath queries (e.g. doc/"//Item/Attribute[Name='Foo']/Value") return an empty array. But if I remove the xmlns attribute from the SelectResponse tag, it works perfectly.
Is there some extra thing I need to do to account for the namespace declaration? This workaround feels horribly like a hack.
That XPath query looks for elements that are not in any namespace. You need to tell your XPath processor that you are looking for elements in the http://sdb.amazonaws.com/doc/2007-11-07/ namespace.
One way to do that with Nokogiri is:
doc = Nokogiri::XML.parse(...)
doc.xpath("//aws:Item/aws:Attribute[Name='Foo']/aws:Value", {"aws" => "http://sdb.amazonaws.com/doc/2007-11-07/"})
I found "Namespaces in XML" really helpful in understanding what's going on.
Basically if you have a namespace defined via xmlns=, you must use a namespace in your XPath searches.
So in your case, you could do one of three things:
Remove the xmlns attribute from the root SearchResponse. In that case your original, namespace-less XPath query will work.
Use the default namespace in your XPath query:
doc/"//xmlns:Item/xmlns:Attribute[xmlns:Name='Foo']/xmlns:Value"
Define a custom namespace in the second argument of the xpath method and use that in your query, as shown in hrnt's solution above.