How to create different "xsd namespace" for stylesheet and result? - xslt

Is it possible to transform the xsd namespace to something different in the result?
the input namespace of the xsl:stylesheet element looks like:
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
and the output namespace of the output root element should look like:
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
I tried to manually asssign the second namespace to the root element of my output, however then the XSLT transformation does not recognize the elements prefixed with "xsd" anymore, as it seems to overwrite the original namespace.
Changing the prefix and making an alias is also not an option: also in the output (RDF/XML) the prefix of the element should be called "xsd".
Thank you for helping.
EDIT:
here are the relevant parts of the two files:
Input File (XSD):
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
should become
Output File (RDF/XML):
<rdf:RDF xmlns:xsd="http://www.w3.org/2001/XMLSchema#">

If you want to transform all elements in the namespace http://www.w3.org/2001/XMLSchema to be in the namespace http://www.w3.org/2001/XMLSchema# then use
<xsl:template match="xsd:*">
<xsl:element name="{name()}" namespace="http://www.w3.org/2001/XMLSchema#">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
If that does not help then please show us a sample of the input XML you have and the corresponding output sample you want to create.

ANSWER:
It took me some hours but eventually I figured out a solution on my own:
I firstly had to exclude the prefix of the stylesheet from the results file:
<xsl:stylesheet version="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xsd">
Then I created a new XSD namespace for the root element of the output:
<xsl:namespace name="xsd" select="'http://www.w3.org/2001/XMLSchema#'"/>
Anyway, thank you for helping.

Related

Allowing an attribute-set to set a namespace required by an attribute

I understand that xsl:attribute-set exists to allow a set of XML attributes to be grouped under a single name, which can then then easily be applied to several similar elements at a later date.
I understand that namespaces are not attributes and cannot be set using this.
However in Saxon9.8EE I note this works and I was wondering if this is safe to use:
<xsl:attribute-set name="swbml.ir" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:attribute name="version">4-2</xsl:attribute>
<xsl:attribute name="xsi:schemaLocation">http://www.fpml.org/2005/FpML-4-2 /path/to/swbml-ird-main-4-2.xsd</xsl:attribute>
</xsl:attribute-set>
By adding the xsi namespace to the xsl:attribute-set itself, it applies this namespace to any element using the swbml.ir attribute set.
(of course it has to because one of the attributes sits in the xsi namespace)
So this:
<SWBML xmlns="http://www.fpml.org/2005/FpML-4-2" xsl:use-attribute-sets="swbml.ir">
Results in:
<SWBML xmlns="http://www.fpml.org/2005/FpML-4-2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="4-2"
xsi:schemaLocation="http://www.fpml.org/2005/FpML-4-2 /path/to/swbml-ird-main-4-2.xsd">
This is exactly what I want. But it feels like I might be stretching the intended use-case for attribute sets?
Specifically if I try to go one step further and add xmlns="http://www.fpml.org/2005/FpML-4-2" like so:
<xsl:attribute-set name="swbml.ir" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.fpml.org/2005/FpML-4-2">
The default xmlns is not applied to <SWBML> - which is kinda what I expect.
So - is the rule that attribute sets will add any namespace that is required in order to qualify any attribute the set contains, BUT will not add any other namespace? Or have I strayed into undefined territory?
Your understanding is basically correct, in that if there is content bound to a namespace, and you include it in your output, then the namespace will come along for the ride. However, the fact that you happen to have declared it on the attribute-set is not critical. It could be declared in other places in the stylesheet, such as on the xsl:stylesheet element, to be in-scope and referenced in the attribute-set.
Building upon the examples that you posed, you could move the declaration of the xsi namespace prefix out of the xsl:attribute-set and up to the xsl:stylesheet element, and it would still appear in your output if the attribute-set were applied to the element:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
exclude-result-prefixes="xsi">
<xsl:output method="xml"/>
<xsl:attribute-set name="swbml.ir">
<xsl:attribute name="version">4-2</xsl:attribute>
<xsl:attribute name="xsi:schemaLocation">http://www.fpml.org/2005/FpML-4-2 /path/to/swbml-ird-main-4-2.xsd</xsl:attribute>
</xsl:attribute-set>
<xsl:template match="/">
<SWBML xmlns="http://www.fpml.org/2005/FpML-4-2" xsl:use-attribute-sets="swbml.ir"/>
</xsl:template>
</xsl:stylesheet>
And it would not appear in the output if the attribute-set is not applied to the content:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
exclude-result-prefixes="xsi">
<xsl:output method="xml"/>
<xsl:attribute-set name="swbml.ir">
<xsl:attribute name="version">4-2</xsl:attribute>
<xsl:attribute name="xsi:schemaLocation">http://www.fpml.org/2005/FpML-4-2 /path/to/swbml-ird-main-4-2.xsd</xsl:attribute>
</xsl:attribute-set>
<xsl:template match="/">
<SWBML xmlns="http://www.fpml.org/2005/FpML-4-2" />
</xsl:template>
</xsl:stylesheet>
Note that I used exclude-result-prefixes in both examples to ensure that the xsi namepsace is pruned from the output if unused. Otherwise, the in-scope namespace might come along for the ride in the output, even if it were not applied to any content.
Yes, this will work: as #MadsHansen points out, when you use <xsl:attribute name="p:u"/> the only thing that really matters is that the prefix p is declared somewhere - on the xsl:attribute element itself, or on one of its ancestors. If it's convenient to declare it at the level of the xsl:attribute-set itself, then fine, do that.
A thing to watch out for here is that this doesn't apply to QName-valued attributes. If you want to do
<xsl:attribute name="xsi:type">xs:date</xsl:attribute>
then you can get the prefix xsi declared in the result document simply by having it in-scope for the xsl:attribute instruction, but for the xs prefix you need to work a bit harder (because the XSLT processor doesn't know that the attribute value xs:date is a QName). In this case you need to explicitly ensure that some containing element in the result tree declares the xs namespace.

XSLT: for-each loop with key not returning all nodes

I am a novice XSLT developer. I have been asked to fix an issue on a project where the original developer is no longer with us. In the XSLT, there is a for-each loop using a key and a count
<xsl:for-each select="ns0:BOM[count(. | key('subsat', ns0:BomText01)[1]) = 1][ns0:BomText01]">
...
This is the key:
<xsl:key name="subsat" match="ns0:Parts/ns0:BOM[ns0:FindNum!='0']" use="ns0:BomText01" />
In the XML file being transformed, there are two sibling nodes that represent sub-parts:
<ns0:BOM referentId="10000:65091335:65359080">
<ns0:BomText01>3069260-303-SUB0027</ns0:BomText01>
<ns0:ItemNumber>My_part_1</ns0:ItemNumber>
<ns0:ItemType>Part</ns0:ItemType>
<ns0:Qty>67</ns0:Qty>
</ns0:BOM>
<ns0:BOM referentId="10000:65102551:86713230">
<ns0:BomText01>3069260-303-SUB0027</ns0:BomText01>
<ns0:ItemNumber>My_part_2</ns0:ItemNumber>
<ns0:ItemType>Part</ns0:ItemType>
<ns0:Qty>67</ns0:Qty>
</ns0:BOM>
However, the loop is only picking up the first node (My_part_1). I suspect it's because of the count=1 but I really don't know. And I don't know how to modify it. Ideas? If I need to include more data, let me know.
Assuming that the relevant part of your XSLT looks something like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="ns0" version="1.0">
<xsl:key name="subsat" match="ns0:BOM[ns0:FindNum!='0']" use="ns0:BomText01"/>
<xsl:template match="ns0:Parts">
<xsl:for-each
select="ns0:BOM[count(. | key('subsat', ns0:BomText01)[1]) = 1][ns0:BomText01]">
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
It will only print the first of the elements because it is selecting the BOM elements which have an unique BomText01 value. That's the expected result.
If the BomText01 is an ID field (as it seems it is) and you expected to get both result (perhaps, because their ItemNumber contains different values), the error is possibly in your source (which assigned equal IDs when it should not have done so).
If you change one of those values in the source, you should be able to select both and verify this.

Counting elements that are generated in XSLT1

I'm trying to count the elements my transformation generates (must use XLST1). For example, my transformation creates:
<Parent>
<ElementX Att1="2"/>
<ElementY Att1="1"/>
<ElementZ Att1="6"/>
</Parent>
I need to print 3 within the same transformation, because there are 3 child elements.
Can this be done?
Thanks.
It would help a lot if you provide some extract of your XSLT.
I cn't give you a XSLT code without it. I'll try to give some "way" to the answer :
One solution could be to store the output into a nodeset (use the XSLT 1.0 extension which provides the nodeset() function) and apply the XPath count() function on this variable. After that just output your variable with xsl:value-of, and your count result the same way.
Here is a demo how to do this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="vrtfPass1">
<xsl:apply-templates/>
</xsl:variable>
<xsl:value-of select="count(ext:node-set($vrtfPass1)/*/*)"/>
</xsl:template>
<xsl:template match="/*">
<Parent>
<ElementX Att1="2"/>
<ElementY Att1="1"/>
<ElementZ Att1="6"/>
</Parent>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on any XML document (not used in this Demo), the wanted, correct result is produced:
3
Explanation:
A general way to process the result of the transformation (in a single transformation), is to organize it in two passes where we save the result of the first pass in a variable.
In the second pass we access the result and do the additional processing.
Do note that in XSLT 1.0 if the variable that captures the result of the first pass is of the infamous RTF (Result Tree Fragment) type and needs to be converted to a regular tree in order of any nodes inside this tree to be accessible (xsl:copy-of and string() are still allowed on an RTF).
This conversion to a regular tree is done by an extension function, which most often has the name node-set and always belongs to a vendor-defined namespace. In this demo we are using the node-set() extension function that belongs to the EXSLT namespace -- because most XSLT 1.0 processors implement EXSLT.
For more information on multi-pass processing, see this: Two phase processing: Do not output empty tags from phase-1 XSLT 2.0 processing

adding multiple filters

As I have mentioned in this post:
dynamic multiple filters in xsl
Basically, I want to apply multiple filters to my xml using "for loop" and these filters are dynamic which are coming from some other xml
sth like this:
foreach(list/field[#ProgramCategory=$Country][not(contain(#Program,$State1][not(contain(#Program,$State2][not(contain(#Program,$State3][not(contain(#Program,$Staten])
The problem is that I can get n no. of states which I am getting through for loop of other xml.
I cannot use document() function as suggested by Dimitre so I was thinking of achieving it by:
<xsl:variable name="allprograms">
<xsl:for-each select="/list2/field2">
<xsl:text disable-output-escaping="yes">[not(contains(#Program,'</xsl:text><xsl:value-of select="#ProgramID"></xsl:value-of><xsl:text disable-output-escaping="yes">'))]</xsl:text>
</xsl:for-each>
</xsl:variable>
gives me something like this:
[not(contains(#Program,'Virginia'))][not(contains(#Program,'Texas'))][not(contains(#Program,'Florida'))]
I want to use this above value as a filter in the for loop below and I am not sure how to achieve that
<xsl:for-each="list/field[not(contains(#Program,'Virginia'))][not(contains(#Program,'Texas'))][not(contains(#Program,'Florida'))]">
Before this I also have a for loop to filter United States
xsl:for-each="list/field $allprograms">
<xsl:value-of select="#ows_ID" />
</xsl:for-each>
I want my answer to be 1082, 1088..
I can add the xml here too if there is any confusion..
Jack,
From the previous solution you just need to add to this:
<xsl:param name="pFilteredStates">
<state>Virginia</state>
<state>Texas</state>
<state>Florida</state>
</xsl:param>
the following (changing the current variable definition that relies on the document() function):
<xsl:variable name="vFiltered" select=
"ext:node-set($pFilteredStates)/*
"/>
Where the "ext:" prefix needs to be bound to this namespace (this is the EXSLT namespace -- if your XSLT processor doesn't implement exslt:node-set() then you need to find what xxx:node-set() extension it implements, or tell us what is your XSLT processor and people will provide this information):
"http://exslt.org/common"
So, your <xsl:stylesheet> may look like the following:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
I still recommend that the $pFilteredStates parameter should be passed by the initiator of the transformation -- in which case you can delete the definition of $vFiltered and replace every reference to it with $pFilteredStates` and the transformation should work OK.

Ignore name space with t: prefix

We have XML file like below...
<?xml version='1.0'?>
<T0020 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.safersys.org/namespaces/T0020V1 T0020V1.xsd"
xmlns="http://www.safersys.org/namespaces/T0020V1">
<IRP_ACCOUNT>
<IRP_CARRIER_ID_NUMBER>1213561</IRP_CARRIER_ID_NUMBER>
<IRP_BASE_COUNTRY>US</IRP_BASE_COUNTRY>
<IRP_BASE_STATE>AL</IRP_BASE_STATE>
<IRP_ACCOUNT_NUMBER>15485</IRP_ACCOUNT_NUMBER>
<IRP_ACCOUNT_TYPE>I</IRP_ACCOUNT_TYPE>
<IRP_STATUS_CODE>0</IRP_STATUS_CODE>
<IRP_STATUS_DATE>2004-02-23</IRP_STATUS_DATE>
<IRP_UPDATE_DATE>2007-03-09</IRP_UPDATE_DATE>
<IRP_NAME>
<NAME_TYPE>LG</NAME_TYPE>
<NAME>WILLIAMS TODD</NAME>
<IRP_ADDRESS>
<ADDRESS_TYPE>MA</ADDRESS_TYPE>
<STREET_LINE_1>P O BOX 1210</STREET_LINE_1>
<STREET_LINE_2/>
<CITY>MARION</CITY>
<STATE>AL</STATE>
<ZIP_CODE>36756</ZIP_CODE>
<COUNTY/>
<COLONIA/>
<COUNTRY>US</COUNTRY>
</IRP_ADDRESS>
</IRP_NAME>
</IRP_ACCOUNT>
</T0020>
In order to Insert this XML data to database ,we have used two XSLT.
First XSLT will remove name space from XML file and convert this XML to some intermediate
XML(say Process.xml) file on some temporary location.
then we were taking that intermediate xml(without namespace lines) and applied another XSL
to map xml field to Database.
Then we have found solution and we have used only one XSLT which does bode [1] Remove namespace and [2] Mapping XML field to Database to insert data.
Our final style sheet contain following lines
xmlns:t="http://www.safersys.org/namespaces/T0020V1">
and we used following to map field to Database
<xsl:template match="/">
<xsl:element name="T0020">
<xsl:apply-templates select="t:T0020/t:IRP_ACCOUNT" />
</xsl:element>
</xsl:template>
how did our problem solved with this approach ?Any consequences with using this ?
I have searched about this but not getting the functionality.
Thanks in Advance..
I don't see any problems with your approach.
XSLT mandates a fully qualified name for a correct matching, so using a prefixed namespace in your XSLT is the right solution; this is why you solved your problem.