There is a well known Identity Transform sample code in the XQuery wikibook
But it works well only with no namespace documents, or explicitly declaring the namespaces with the same prefixes used in the document about to be processed.
If you don't declare the namespaces (with the same prefixes), you get an error:
Cannot compile xquery: err:XPST0081 No namespace defined for prefix
xsd [at line 15, column 12]
is there a way to write an Identity Transform in XQuery, that can automatically handle the namespaces and prefixes, avoiding the explicit declaration of namespaces?
EDIT:
This is the code from the Wikibook:
(: return a deep copy of the element and all sub elements :)
declare function local:copy($element as element()) as element() {
element {node-name($element)}
{$element/#*,
for $child in $element/node()
return
if ($child instance of element())
then local:copy($child)
else $child
}
};
In my case, I don't know the namespaces or prefixes in the document to be processed, so the element { } { } construct fails if the namespace and prefix are not declared in the XQuery.
To reproduce it, just copy/paste and run it with a document that uses prefixed namespaces.
I think the answer is no. If you want to process namespaced elements, then those namespaces need to be declared.
Based on the error message, all you need to do is declare the xsd namespace at the top of your XQuery document.
Declare namespace xsd="http://www.w3.org/2001/XMLSchema";
If this doesn't do it, then posting your XQuery would greatly help us understand what the issue is.
Related
For all XQuery queries I execute, I define a default element namespace, like so:
declare default element namespace 'http://example.com';
(: rest of the query :)
But to hardcode this namespace is kind of cumbersome. Can I dynamically define a default namespace, from an external variable?
It tried:
declare default element namespace $namespace;
(: error: Expecting quote, found '$'. :)
.. and:
declare variable $namespace external;
declare default element namespace $namespace;
(: error: Default declarations must be declared first. :)
... but they don't work, unfortunately.
I'm currently using BaseX 7.7.2, by the way.
Unfortunately, it is only possible to statically declare default namespaces, as the grammar only allows for a URILiteral in this place. The default element namespace is in the static context of the query.
However, you can build QNames out of a variable containing the namespace dynamically (from a variable). You can build new elements with such QNames with computed element constructors. The XPath navigation, however, would be more involved, because you will have to filter namespaces explicitly.
It's generally assumed that if you know the local names of the elements in the source document statically, then you will also know their namespaces statically. This assumption is not always correct; you sometimes encounter a family of namespaces (e.g. different versions of a namespace) that use the same local names; but XQuery (and XSLT) aren't well designed to handle this scenario.
So if you know the names of the elements statically, and use path expressions like invoice/customer/address, then the names in this path are simply a shorthand for the full expanded names, and it's assumed that the shorthand can be resolved by the compiler. That's why the namespace declarations are all fixed statically.
If you don't know the names of the elements you are querying statically, then you're probably writing a query that uses wildcards (e.g. child::*) rather than explicit local names like "invoice". In that case the default namespace declaration never comes into play anyway.
So the only situation I can see where you have trouble is the scenario above where multiple namespaces use similar sets of local names. Most people advise against using namespaces that way, for that very reason, but unfortunately not everyone has followed this advice.
Setting a default namespace with a direct element constructor is straightforward. Ex:
<map xmlns="http://www.w3.org/2013/XSL/json"/>
The above direct constructor outputs the exact same element, as expected. However, if I try to do the same with computed element and namespace constructors, I'm out of luck:
element {"map"} {
namespace {""} { "http://www.w3.org/2013/XSL/json" }
}
The above throws a Duplicate namespace declaration: '' error in BaseX 8.2; and a XTDE0440: Cannot output a namespace node for the default namespace when the element is in no namespace in Saxon-HE 9.6.
There's no problem if I pass a prefix. The following works well:
element {"map"} {
namespace { "e" } {"http://www.w3.org/2013/XSL/json"}
}
In the above case, neither BaseX nor Saxon complain. But I want to set the namespace as the default namespace.
Regarding computed namespace constructors, the XQuery 3.0 spec says:
If the constructor specifies a PrefixExpr, the prefix expression is evaluated as follows:
b. If the result is the empty sequence or a zero-length xs:string or xs:untypedAtomic value, the new namespace node has no name (such a namespace node represents a binding for the default namespace).
The spec also provides a similar example of a "computed namespace constructor with an empty prefix":
namespace { "" } {"http://a.example.com" }
Why then, the error messages? Apparently, the computed namespace constructor tries to re-declare a namespace already declared by the computed element constructor. On the other hand, the direct constructor would be directly initializing the element's namespace to my choice. But this is just my guess. My only certainty is my puzzlement.
In any case, is there a way to obtain with computed constructors the same result achievable with the direct constructor?
The basic reason is that the name of an element (that is, the prefix, local, and uri parts of the name) are determined by the element constructor itself, and not by the dynamic content that is added to the element.
When you do this:
element {"map"} {
namespace {""} { "http://www.w3.org/2013/XSL/json" }
}
You are creating an element with no prefix and no namespace, and then you are giving it a namespace node that binds the default namespace to something other than "no namespace"; you can't have it both ways. Adding a namespace dynamically to the content of an element will never change the element name.
I haven't tested this in saxon or basex, but using XQuery3 in Marklogic, this works:
element {fn:QName("http://www.w3.org/2013/XSL/json", "map")} { }
However, that alone probably won't actually do what you want.
In XQuery, computed constructors do not inherit default namespaces from parent elements; the first part is always the (fully qualified) QName of the element. You can use a string there, but it's being coerced into a QName - if that string has no prefix then you're explicitly asking for an element with no namespace (unless you've declared a default element namespace, in which case, you're asking for that). This means that even if you set the default namespace on an element, any children for which you don't specify a default namespace in the constructor will explicitly set it back to "".
By far the simplest way to work with computed constructors is to declare your namespaces at the top of the file, and then use prefixes on every element name. This does lead to XML with prefixes on every element though. Personally, I like how obviously unambiguous that is, but many people find it overly verbose, especially in XML that uses only one namespace.
You can also use the construct I used above for every element, which will do more or less the same thing, but without using prefixes. If you do this, while you have to specify the namespace on every element where it's constructed in your code, the resulting XML should only specify it on the top-level element, with child elements inheriting it like you'd expect (although it's possible this will depend on your XQuery processor.)
Alternatively, you can declare the default element namespace and then only specify a namespace for elements that differ from that. Personally, I find this confusing and prone to hard to spot errors, though.
I tried to add a comment but it wouldn't let me put in newlines.
As Will Goring mentioned in his answer, children elements do not inherit the default namespace from the parent. One way to make this easier is to declare the namespace and use that in the children elements.
declare namespace json = "http://www.w3.org/2013/XSL/json";
element {fn:QName("http://www.w3.org/2013/XSL/json", "map")} {
element json:child { }
}
becomes
<map xmlns="http://www.w3.org/2013/XSL/json"><child/></map>
where child has the same default namespace.
It's equivalent to:
element {fn:QName("http://www.w3.org/2013/XSL/json", "map")} {
element {fn:QName("http://www.w3.org/2013/XSL/json", "child")} { }
}
Short version: How do I do nested property expansion in an XQuery in SoapUI Pro 5, where the outer property is a reference to the ResponseAsXML of a previous test step, and the inner property comes from a properties file?
Example:
My test steps look like this:
Call AddCustomer, which also adds an email address, and returns customerId.
Use property transfer to store the customerId in a properties file called EmailProperties.
Call AddEmailToCustomer, which adds a second email to the same customer and returns a new emailId.
Call GetEmailsForCustomer, which returns both emails for the customer.
The REST endpoints for step 3 and 4 look like this:
POST/GET http://myEndpoint.com/customers/{customerId}/emails
When trying to verify step 4, I wrote an XQuery that loops through the Response (since we can't guarantee what order the emails will be in the response) to find the email added in step 3. In my XQuery, if I hardcode the customerId in the namespace like this it works fine:
for $email in //emails
where $email/id/text()='${AddEmailToCustomer#ResponseAsXml#declare namespace ns1='http://myEndpoint.com/customers/1234/emails'; //ns1:Response[1]/ns1:id[1]}'
But if I try to use the customerId from the properties file like this:
for $email in //emails
where $email/id/text()='${AddEmailToCustomer#ResponseAsXml#declare namespace ns1='http://myEndpoint.com/customers/${#EmailProperties#customerId}/emails'; //ns1:Response[1]/ns1:id[1]}'
I get an error about how it can't find the expected child nodes:
...Exception:org.custommonkey.xmlunit.Diff[different] Expected presence of child nodes to be 'true' but was 'false'...
How can I get this to work?
You can use wildcards for namespaces to greatly simplify your XPaths:
where $email/id/text()='${AddEmailToCustomer#ResponseAsXml#//*:Response[1]/*:id[1]}'
As #SiKing recommends you can use wildcards for namespaces to simplify XPath, this is enough if you don't care about check the namespace (which is the most usual behavior). However I think that you put the namespace in a property in order to check possible different namespaces due you can try declaring your XQuery Match assertion as follows:
declare namespace ns1='http://myEndpoint.com/customers/${#EmailProperties#customerId}/emails';
<XQueryResult>
{
for $email in //ns1:emails
where $email/id/text()='${AddEmailToCustomer#Response#//ns1:Response[1]/ns1:id[1]}'
return $email
}
</XQueryResult>
Please note that if EmailProperties is a property testStep then you must use ${EmailProperties#customerId} instead of ${#EmailProperties#customerId}, also in this case I prefer to use #Response instead of #ResponseAsXml.
Hope this helps,
We've got a web service that returns a very simple XML.
<?xml version="1.0"?>
<t:RequestResult xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://our.website.com/ns/" xmlns:t="http://our.website.com/ns/">
<t:Result>No candy for you today.</t:Result>
<t:Success>false</t:Success>
</t:RequestResult>
The caller gets this XML no problem, using XMLHTTP.
But XPath queries don't work agains this XML because of "Reference to undeclared namespace prefix: 't'"
Why so? I'd say the 't' prefix is somewhat declared.
Is this document invalid in any way?
In case you wonder why we had to use XmlNamespaceDeclarations to add namespace prefixes in the first place, that is because otherwise the resulting document cannod be queried upon becase it has a target namespace but hasn't got a prefix for it, so XPath ignores node names because they don't belong to requested (empty) namespace, and we don't wan't to use constructions like "//*[namespace-uri()='http://our.website.com/ns' and local-name()='RequestResult']".
You've already answered the question, but it's worth understanding why this is.
The namespace that an element is in cannot be determined solely by the namespace prefix. To find what namespace an element named t:foo is in, you have to search up the ancestor-or-self axis until you find the nearest node that defines the namespace for t:. For instance:
<t:one xmlns:t="ns-one">
<t:one>
<t:two xmlns:t="ns-two">
<t:two/>
</t:two>
</t:one>
</t:one>
In that document, every element whose name is one is in the ns-one namespace, and every element whose name is two is in the ns-two namespace. You can tell that the deepest element in that document is in ns-two not because t: intrinsically means ns-two, but because if you search up the ancestor-or-self axis, the first element that you hit with an xmlns:t attribute on it - its parent - tells you the namespace.
Given that, which nodes should the XPath expression //t:* match? It's impossible to say, because what namespace t: is mapped to changes throughout the document.
Also, namespace prefixes are temporary, but namespaces are permanent. If you know that one is in ns-one, you really, truly don't care whether its prefix is t: or x: or if it has no prefix at all and just an xmlns attribute.
When you're querying an XML document with XPath, you need a way of specifying what namespace a given element is in. And that's what SelectionNamespaces in a DOMDocument, or a namespace manager in C#, or whatever are for: they tell you what namespaces the prefixes in your XPath queries represent. So if I've set the prefix a: to ns-one, the XPath //a:one will find me all of the elements named one in the ns-one namespace, irrespective of what the actual prefix they're using in the document I'm searching is.
This is a little counterintuitive when you're first learning it, but really, it's the only way that makes any sense at all.
Surprisingly enough (for me), this is the default behaviour for XPath. By default, namespace prefixes are not allowed in an XPath query.
To resolve this, one must register desired prefixes with SelectionNamespaces property of the DOMObject.
objXML.setProperty("SelectionNamespaces", "xmlns:t='http://our.website.com/ns/'")
After that one can use expressions qualified with t: in XPath queries.
This also resolvers the original problem that forced us to use XmlNamespaceDeclarations in the first place.
I am a Java developer who tries Flex. Here is my problem:
I behave actionScript objects as hashmap but when the object do not have the property it gives exception: No such variable.
Here I expect it gave me null, instead of giving exception. So do you know is there a way to handle it, namely check if the property is defined for object.
trace( obj["2008-02"] ) // gives exception
Use something along the lines of
if (myObject.hasOwnProperty("propertyName"))
to check if the property exists.
Edit: Also take a look here.
hasOwnProperty() doesn't work correctly with inheritance, static properties, or dictionaries.
You should use
if ("propertyName" in myObject)
instead.
try
if ( obj["2008-02"] != null ) { then do something }
it is null, but you can't output null. you can also try converting it to a string for the purposes of a trace().