Add namespace declaration to xml element using xquery - xquery

I would like to ask about the way to add namespace declaration into an xml element of BaseX database using xquery.
for example I have the following xml
<mynode xmlns:b2t=".."></mynode>
And I want ta add one more namespace for example xmlns:b3t=""
in order to get an xml like this
<mynode xmlns:b2t=".." xmlns:b3t=".."></mynode>

XQuery Update provides no means to add namespace declarations in an existing document. However, namespace declarations will automatically be included if you insert elements or attributes with namespaces.

Related

Is it possible to dynamically declare a default namespace from an external variable?

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.

XQuery: How to set default namespace with a computed namespace constructor?

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")} { }
}

xquery - compare xsi:type with different namespace prefix

declare namespace xf = "http://example.com"
declare namespace my = "http://example.com"
How can I compare something like
<1 xsi:type="xf:myDef">
<1 xsi:type="my:myDef">
These 2 elements above are equals, however comparing the attribute as string it fails.
How can compare both prefix and type, resolving the URI?
Thanks
As noted above:
In the W3 XML specification there is no special interpretation for values of attributes (with the exception of the few global attributes in the XML namespace, such as xml:space, xml:lang, etc. The standard XPath 2.0 deep-equal() function considers equal elements or attributes names that are in the same namespace regardless of the prefix. Not so for attribute values. XML happened before XSD and is unaware of it.

.Net Inline Schema/XML Programmatic Generation for Excel

I have a set of data rendered using ASP.Net (VB.Net) to a web page. I now want to export that data to XML. I have created some code to generate a schema, however, I don't know what to do next. I want to have the schema be in-line with the XML data, and I would like the compiler to check to make sure that the data I'm entering for the XML content validates against the included schema. Anyone know of a way to do this? The idea is for me to be able to open the resultant file in Excel with fields of the correct type.
I've build XML documents before, and this is my first schema document I've created programmatically. However, I've never worked with inline schema's, much less used them to strongly-type the XML being added to the document.
I've read over the following, which were quite helpful, but neither of which addressed the issue I mention above:
http://www.aspfree.com/c/a/XML/Generating-XML-Schema-Dynamically-Using-VBNET-2005-Essentials/
http://blogs.msdn.com/b/kaevans/archive/2007/06/05/inline-an-xml-schema-into-your-xml-document.aspx
I have no idea what you mean by "... I would like the compiler to check to make sure that the data I'm entering for the XML content validates against the included schema."
The compiler never checks that. If you want to validate your XML Document against a schema programmatically, you should probably use http://msdn.microsoft.com/en-us/library/system.xml.schema.xmlschemavalidator.aspx.
But for inlining the schema with your document, you sort of answered your own question. The second link in your question, to http://blogs.msdn.com/b/kaevans/archive/2007/06/05/inline-an-xml-schema-into-your-xml-document.aspx, is exactly what you are trying to do.
You can think of an inline XML Schema as a document-within-a-document. Well, using Kirk's example, the outermost document is more of a container which uses the undefined namespace (no schema). His example uses a document root of "DerekDoc" that belongs to the undefined namespace. You can name yours whatever you want.
Inside that root are essentially two documents. One is the inline XML Schema. You would just add it as a child element of the root. The other is the XML document that you intended to conform to the XML Schema. You will need to use the xmlns attribute to set this element to the namespace defined by your XML Schema (the target namespace of the schema).
It might work (I haven't tried it) to set the root element to the target namespace of the schema, but it might be harder for clients to validate the document since it's a forward reference.

XML: A namespace prefix is claimed to be not declared when it fact it is

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.

Resources