Local XML namespace definition not working as I expected - xslt

I'm processing an XMI document exported from ArgoUML. It has elements of the form
<UML:DataType href='http://argouml.org/profiles/uml14/default-uml14.xmi#-84-17--56-5-43645a83:11466542d86:-8000:000000000000087C'/>
which points to an item of the form
<UML:DataType xmi.id="-84-17--56-5-43645a83:11466542d86:-8000:000000000000087C"
name="Integer"
isSpecification="false"
isRoot="false"
isLeaf="false"
isAbstract="false"/>
I've already declared xmlns:UML="org.omg.xmi.namespace.UML" at the top of the xslt file. I think I should be using something like :
<xsl:variable name="typeref" select="#href"/>
<xsl:variable name="ns" select='substring-before($typeref, "#")'/>
<xsl:variable name="identifier" select='substring-after($typeref, "#")'/>
<xsl:value-of xmlns:UML="$ns"
select='//UML:DataType[#xmi.id="$identifier"]/#name'/>
to deduce that my UML attributes type is Integer but this gives me
SystemId Unknown; Line #136; Column #94; A location step was expected following the '/' or '//' token.
If I change the xmlns to AAA then I get no error but an empty tag. I'm using Xalan2 on Debian squeeze. What am I missing?

Don't mind me. Just making the classic mistake of conflating namespaces and URIs. What I really needed was <xsl:value-of select='document($ns)//UML:DataType[#xmi.id=$identifier]/#name'/>

Related

Unable to get the value of 'xml:lang' attribute

<xoe:documents xmlns:xoe="http://xxxxxx" count="1">
<xocs:doc xmlns:xocs="xxxxxx" xmlns:xsi="yyyyyyy" xsi:schemaLocation="zzzzzz">
<xocs:meta>...</xocs:meta>
<xocs:serial-item>
<!-- this line -->
<article xmlns:sa="www.google.hgy" xmlns="http://www.xyzq1.org/xml/ja/dtd" version="5.4" xml:lang="pl" docsubtype="rev">
<article-info>
</article-info>
</article>
</xocs:serial-item>
</xocs:doc>
</xoe:documents>
I am unable to get the value of 'xml:lang' attribute. Even thought I tried with the below xpath
<xsl:variable name="rootPath" select="/xoe:documents/xocs:doc/xocs:serial-item"/>
<xsl:variable name="lang" select="$rootPath/ja:article[#xml:lang]"/>
or
<xsl:variable name="lang" select="$rootPath/ja:article/#xml:lang"/>
here ja is already defined in my xslt code
xmlns:ja="http://www.xyzq1.org/xml/ja/dtd"
Can some one please help?
First, you need to declare these:
xmlns:xoe="http://xxxxxx"
xmlns:xocs="xxxxxx"
xmlns:ya="www.yahoo.mkt"
Then you can get the value of the xml:lang attribute using:
<xsl:value-of select="/xoe:documents/xocs:doc/xocs:serial-item/ya:article/#xml:lang"/>
Note that the URIs in your stylesheet namespace declarations must be the same URIs that appear in your source XML. The prefixes can be anything you like.

Scope (root node, context) of XSLT "key" Element

I have an XSLT key defined. I need to access the key from within a for-each loop, where that loop is processing a node-set that is outside the scope of where the key was defined.
Snippet, where I've marked two lines, one which works and one which does not:
<xsl:value-of select="key('name', 'use')"/> <!-- works -->
<xsl:for-each select="$outOfScopeNodeSet">
<xsl:value-of select="key('name', 'use')"/> <!-- does not work -->
</xsl:for-each>
Is there a way to access the key from within the for-each loop?
XSLT 1.0, msxsl engine.
(I could not think of a reasonable way to provide a full working example for this. I'm also not sure of the correct terminology, such as "scope" - perhaps if I knew the correct terminology I'd be able to find my answer already. If the question is not clear enough please let me know and I'll try to edit it into better shape.)
In XSLT 1.0, keys do not work across documents. It seems that your $outOfScopeNodeSet contains a node-set whose root node is different from the root node of the XML document being processed (probably created by the exsl:node-set() function?) - while the key is supposed to fetch a value from the processed XML document.
To resolve this problem, you need to return the context back to the processed XML document before calling the key() function, for example:
<xsl:variable name="root" select="/" />
<xsl:for-each select="$outOfScopeNodeSet">
<xsl:variable name="use" select="some-value" />
<xsl:for-each select="$root">
<xsl:value-of select="key('name', $use)"/>
</xsl:for-each>
</xsl:for-each>

Output multiple namespace definitions with XSLT 1.0

I need to generate the following output :
<x:Envelope xmlns:x='namespace1'>
<x:Root xmlns="namespace2">
<Header>
...
</Header>
</x:Root>
</x:Envelope>
I'm having trouble generating the default namespace for the x:Root element using xslt 1.0. I can get it to have no namespace ( but namespace2 will be specified on children of root - undesired behaviour ) or have it with a prefix :
<x:Root xmlns:x="namespace2">
but this fails schema validation. Any ideas ?
Edit : sorry for the ambiguous question and thanks for the answers. Root should be in namespace1 and Header should be in namespace2. However, the request is that namespace2 should not be declared in Header, but at Root level.
Regards,
It depends on how much of this is known statically. If you know everything statically, the literal result element
<x:Root xmlns="namespace2">..</x:Root>
will generate exactly what you want. In the more general case, you need to construct an element containing the required namespace node and then copy the namespace node:
<xsl:param name="ns">namespace2</xsl:param>
<xsl:variable name="temp">
<xsl:element name="dummy" namespace="{$ns}"/>
</xsl:variable>
...
<xsl:element name="Root">
<xsl:copy-of select="xx:node-set($temp)/namespace::*"/>
</xsl:element>
All so much easier in XSLT 2.0 with the xsl:namespace instruction.
You can't map two different namespaces to the same prefix "x". Instead, leave off the prefix for Root all together like this:
<Root xmlns="namespace2">
...
</Root>

BizTalk HL7 send pipeline error due to line breaks in empty XML elements

I am mapping to an HL7 A31 message using the BizTalk mapper. The map has several inline XSLT scripting functoids.
When the XML is put through the HL7 send pipeline, it generates an error:
The element 'ROL_11_OfficeHomeAddress' has an invalid structure
If I look at the suspended message, I can see why this has happened. The ROL_11 element is empty, and looks like this:
<ROL_11_OfficeHomeAddress>
</ROL_11_OfficeHomeAddress>
Between the opening and closing tags, there is a line break and several spaces/tabs due to indenting. This is exactly as generated by the XSLT and I believe it is the line break that is causing the error.
I could wrap the XSLT in an <xsl:if> statement to check for a value before writing the XML. However this problem is occurring in many places and it seems overkill to wrap every single element like this.
What I really want is for BizTalk to automatically convert the element to an empty one, like this:
<ROL_11_OfficeHomeAddress />
I believe this would solve the problem. Is there any way I can tell it to do that?
Things I have already tried:
Using <xsl:strip-space> but that raised its own error. I think this is because BizTalk wraps the inline XSLT in its own code and thus strip-space was specified in the wrong place.
Changing the map's grid properties to set Indent to No in the hope the whitespace would be removed. This had no effect on the XML seen in the suspended message.
Adding the registry key for legacy whitespace handling as per this guidance. Again, this appeared to have no effect at all.
If you convert your entire map into XSLT, the below will strip out newlines and whitespace and leave you with an empty tag if there isn't anything but whitespace:
<xsl:element name="ROL_11_OfficeHomeAddress">
<xsl:if test="normalize-space(ROL_11_OfficeHomeAddress)">
<xsl:value-of select="normalize-space(ROL_11_OfficeHomeAddress)" />
</xsl:if>
</xsl:element>
Edit:
Biztalk usually generates XSLT like the following in a typical 1:1 nillable element mapping
<xsl:variable name="var:v2" select="string(ns0:ROL_11_OfficeHomeAddress/#xsi:nil) = 'true'" />
<xsl:if test="string($var:v2)='true'">
<ns0:ROL_11_OfficeHomeAddress>
<xsl:attribute name="xsi:nil">
<xsl:value-of select="'true'" />
</xsl:attribute>
</ns0:ROL_11_OfficeHomeAddress>
</xsl:if>
<xsl:if test="string($var:v2)='false'">
<ns0:ROL_11_OfficeHomeAddress>
<xsl:value-of select="ROL_11_OfficeHomeAddress/text()" />
</ns0:ROL_11_OfficeHomeAddress>
</xsl:if>
So if you did use <xsl:strip-space> it would mean that the element would map to <ROL_11_OfficeHomeAddress></ROL_11_OfficeHomeAddress> if whitespace only, unless you went through the map changing it back to <xsl:element>.
What you could try is to use a call template like the below (nodeXfrm is a node)
<xsl:template name="StripElement">
<xsl:param name="nodeXfrm"></xsl:param>
<xsl:variable name="nodeName">
<xsl:value-of select="local-name($nodeXfrm)"></xsl:value-of>
</xsl:variable>
<xsl:element name="{$nodeName}">
<xsl:if test="normalize-space($nodeXfrm)!=''">
<xsl:value-of select="$nodeXfrm/text()"/>
</xsl:if>
</xsl:element>
</xsl:template>
And then within your map you can call the template for each element you need stripped in this way
<xsl:call-template name="StripElement">
<xsl:with-param name="nodeXfrm" select="ROL_11_OfficeHomeAddress"></xsl:with-param>
</xsl:call-template>
An XSLT guru might be able to do this more elegantly
I too was recently having this problem, but in BizTalk 2013. We moved everything to custom XSLT files for mapping our HL7v2. Upon upgrading to 2013, suddenly the <xsl:strip-space> that previously worked, no longer worked.
This is because BizTalk 2013 now uses the XslCompiledTransform class rather than the now obsoleted XslTransform class and it doesn't allow the <xsl:strip-space>. So I too was faced with no global way to strip the whitespace.
However, after much searching and head scratching, I found an obscure blog post with something that worked for my solution:
http://geekswithblogs.net/peterbrouwer/archive/2012/08/17/biztalk-2010ndashlegacy-whitespace-behaviour.aspx
An option in the the Host's settings, for using legacy whitespace did it for us (so far at least).

Need to create element using XSLT

I want to create a Element in xml <a:ln w="12700"> using xslt 1.0.
and this is what i did in xslt
<xsl:variable name="width-value">12700</xsl:variable>
<xsl:element name="a:ln">
<xsl:attribute name="w">
<xsl:value-of select="$width-value"/>
</xsl:attribute>
</xsl:element>
its throwing error,
You cannot call an attribute 'w''
FATAL ERROR: 'Could not compile stylesheet' javax.xml.transform.TransformerConfigurationException: Could not compile stylesheet
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl.newTemplates(TransformerFactoryImpl.java:825)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl.newTransformer(TransformerFactoryImpl.java:614)
at com.vignesh.main.convert(main.java:288)
at com.vignesh.main.main(main.java:70)
The only thing that I see is that you're missing the namespace declaration for a: If I have to make this snippet working, I must add something like this
<xsl:element name="a:ln" namespace="http://www.example.com/a/">
Perhaps you have the a namespace declaration elsewhere. I suspect the problem is due to some other part of your XSL, because this snippet looks OK to me.