xslt expects element instead of xsl:for-each - xslt

Since I am a beginner in xslt/xsd-programming, I am using XMLSpy to create a xml2xml transformation. For both xml I have got a xsd. Unfortunately, the following code piece is not valid.
<xsl:template match="/">
<table xsi:noNamespaceSchemaLocation="table.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:for-each select="table/body/line">
<row>
</row>
</xsl:for-each>
</table>
</xsl:template>
The error message says that the row element is expected after table.
Details (translated): element <xsl:for-each> was not expected of type {anonymous} of element <table>.
The problem can be solved by removing the reference to the xsd or removing the for-each statement.
However, I cannot figure out what is wrong. To my understanding the for-each-loop should just repeat the <row> tags for each line in the first xml.
Here is part of the xsd of the target.
<xs:element name="table">
<xs:complexType>
<xs:sequence>
<xs:element ref="row" maxOccurs="unbounded"/>
<xs:element ref="Metadata" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>

I suspect that Altova is using the presence of the attribute xsi:noNamespaceSchemaLocation="table.xsd" as a signal meaning "please validate this element against the schema in table.xsd"; which is not what you wanted, because of course it's not valid against that schema, as it contains XSLT instructions to create the required elements, rather than containing the required elements themselves.
To work around this, try generating the attribute using xsl:attribute:
<table xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:attribute name="xsi:noNamespaceSchemaLocation">table.xsd</xsl:attribute>
<xsl:for-each select="table/body/line">
<row/>
</xsl:for-each>
</table>

Related

XSLT: Copy child elements of a complex type only once

I have the following input structure:
<complexType name="InvoiceType">
<xs:element name="AdressIn" type="AdressType"/>
<xs:element name="AdressOut" type="AdressType" />
<xs:element name="Partner" type="PartnerType" />
<xs:element name="Date" type="DateType"/>
</complexType>
All the referenced types (AdressType,PartnerType,DateType) are also contained in that document as complex types (besides many other).
What i am trying to do is to copy the types that are used within a "InvoiceType"
to a new document.
My Problem is, the AdressType is used more then once within InvoiceType, and thus it appears more then once in my new document. How can i prevent that?
I need something like "if that is already pocessed ->skip" but that is not declarativ... maybe xslt is not the right way to achive this.
Thanx for any help!
Edit:
My XSLT i am using so far looks like that (modified to please the simple example)
<xsl:template match="xs:complexType">
<xsl:for-each select="./xs:element">
<xsl:variable name="elementType"><xsl:value-of select="#type"></xsl:value-of></xsl:variable>
<xsl:copy-of copy-namespaces="no" select="/xs:schema/xs:complexType[#name=$elementType]"/>
</xsl:for-each>
I am applying that template for the InvoceType. Basically i am going threw its contents, see what types are referenced, look them up in the document via their name and copy them.
Instead of
<xsl:for-each select="./xs:element">
<xsl:variable name="elementType"><xsl:value-of select="#type"></xsl:value-of></xsl:variable>
<xsl:copy-of copy-namespaces="no" select="/xs:schema/xs:complexType[#name=$elementType]"/>
</xsl:for-each>
use
<xsl:copy-of select="/xs:schema/xs:complexType[#name=current()/xs:element/#type]"/>
or define a key
<xsl:key name="complex-types" match="xs:schema/xs:complexType" use="#name"/>
and then you can use
<xsl:copy-of select="key('complex-types', xs:element/#type)"/>

Extracting data from XSD in XSL

I am transforming an XML file that needs to generate some elements based on the valid enumeration options defined in the XSD.
Suppose I have an XSD that declares a type and an element something like this:
<xs:simpleType name="optionType" nillable="true">
<xs:restriction base="xs:string">
<xs:maxLength value="50"/>
<xs:enumeration value="USERCHOICE">
</xs:enumeration>
<xs:enumeration value="DEFAULT">
</xs:enumeration>
</xs:restriction>
</xs:simpleType>
...
<xs:element name="chosenOption" type='optionType'/>
...
<xs:element name="availableOption" type='optionType'/>
The input will only contain the chosen option, so you can imagine it looks like this:
<options>
<chosenOption>USERCHOICE</chosenOption>
</options>
I need to have an output that looks like this:
<options>
<chosenOption>USERCHOICE</chosenOption> <!-- This comes from incoming XML -->
<!-- This must be a list of ALL possible values for this element, as defined in XSD -->
<availableOptions>
<availableOption>USERCHOICE</availableOption>
<availableOption>DEFAULT</availableOption>
</availableOptions>
</options>
Is there a way to have the XSL extract the enumeration values USERCHOICE and DEFAULT from the XSD and produce them in the output?
This will run on WebSphere 6 and will be used by an XSLT 1.0 engine. :(
(The schema file does not change often but it will change now and then and I'd rather only have to update the schema file instead of update the schema file and XSLT)
Here's a prototype that assumes that your input XML and XSD are as simple as the samples above. To be tweaked according to ways in which they may vary. If you need help with that tweaking, let me know.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="1.0">
<xsl:variable name="xsd" select="document('mySchema.xsd')"/>
<xsl:template match="/options">
<xsl:copy>
<xsl:for-each select="*">
<xsl:variable name="eltName" select="local-name()"/>
<xsl:copy-of select="." />
<availableOptions>
<xsl:variable name="optionType"
select="$xsd//xs:element[#name = $eltName]/#type"/>
<xsl:apply-templates
select="$xsd//xs:simpleType[#name = $optionType]/
xs:restriction/xs:enumeration"/>
</availableOptions>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="xs:enumeration">
<availableOption><xsl:value-of select="#value" /></availableOption>
</xsl:template>
</xsl:stylesheet>

XSLT change page

I am trying to figure out how to change the page with my xslt transformation, by clicking on an element in the table.
I have looked around and couln't find an answer, here is a simple code and I would like to know how to change the page and desplay something:
I don't think it is necessary for you to read the code except for the the final one, the XSLT. I have added the other sections of the code in case they also need some changing.
XSD:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.w3schools.com"
xmlns="http://www.w3schools.com">
<xs:element name="Table">
<xs:complexType>
<xs:sequence maxOccurs="unbounded">
<xs:element ref="Person"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Person">
<xs:complexType>
<xs:all maxOccurs="1">
<xs:element ref="Name"/>
<xs:element minOccurs="1" ref="Age"/>
</xs:all>
</xs:complexType>
</xs:element>
<xs:element name="Name">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="1" ref="theName"/>
<xs:element maxOccurs="1" ref="Description"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="theName" type="xs:string"/>
<xs:element name="Description" type="xs:string"/>
<xs:element abstract="false" name="Age" type="xs:positiveInteger"/>
</xs:schema>
So I created two elements, theName and the age. Name is composed of the String TheName and the String Description. Age is just a positiveinteger. Now I created the XML file which just chooses two different People:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="try.xsl"?>
<Table xmlns="http://www.w3schools.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3schools.com try.xsd">
<Person>
<Name>
<theName>Thomas</theName>
<Description>He is a nice guy</Description>
</Name>
<Age>10</Age>
</Person>
<Person>
<Name>
<theName>Peter</theName>
<Description>He is good at swimming</Description>
</Name>
<Age>12</Age>
</Person>
</Table>
My transformation now is to make a table with the name of the people in function of there age:
XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ws="http://www.w3schools.com"
version="1.0">
<xsl:template match="/">
<html>
<table border="2">
<tr bgcolor="red">
<th>Name</th>
<th>Age</th>
</tr>
<xsl:for-each select="ws:Table/ws:Person">
<tr>
<td><xsl:value-of select="ws:Name/ws:theName"/></td>
<td><xsl:value-of select="ws:Age"/></td>
</tr>
</xsl:for-each>
</table>
This is where I need help, how do I change the page that I am on and go on a page which simply displays the description of the person, I want to do this by clicking on the name of the person. I don't want to open a new window, just change the page.
</html>
</xsl:template>
</xsl:stylesheet>
In your XSLT code, generate HTML that contains a hyperlink to the relevant page. This could be a traditional <a href="..."> hyperlink, or any element with an onclick attribute.

transforming with xsl an xml schema template to an other xml schema template

can anyone give me a paradigm of transforming an xml schema template like
<xs:element name="carareWrap">
<xs:annotation>
<xs:documentation xml:lang="en">The CARARE wrapper element. It wraps CARARE elements.</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="unbounded" ref="carare"/>
</xs:sequence>
</xs:complexType>
</xs:element>
to an other xml schema template with xsl?
the other xml schema could be anything you can do.. I just need to have somwthing to start with...
can anyone give me a paradigm of transforming an xml schema template like ... to an other xml schema template?
An XML Schema is just a an XML document with declared namespace uri http://www.w3.org/2001/XMLSchema. Therefore, you can apply XSLT as usual.
For instance, you have a source schema like this:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="carareWrap">
<xs:annotation>
<xs:documentation xml:lang="en">The CARARE wrapper element. It wraps CARARE elements.</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="unbounded" ref="carare"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
And (for example) you want to remove the attributes of reference elements only. You can apply the following transform:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
version="1.0">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="xs:element[#ref]">
<xsl:copy>
<xsl:copy-of select="#ref"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The result will be:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="carareWrap">
<xs:annotation>
<xs:documentation xml:lang="en">The CARARE wrapper element. It wraps CARARE elements.</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element ref="carare" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Notice
The needing of the declaration of the namespace of the input document in the XSLT
The usage of the identity transform to copy the input document as is and override the elements as by requirements.
There is nothing special about trating XSD documents. They are just XML.
Since you do not specify what changes you want to make, here is a sample XSLT stylesheet that changes a random detail (the value of minOccurs in this case)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
<!-- the identity template copyies everything as-is -->
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*" />
</xsl:copy>
</xsl:template>
<!-- ...unless there is a more specific template available -->
<xsl:template match="
xs:element[#name = 'carareWrap']//xs:element[#ref = 'carare' and #minOccurs = 1]/#minOccurs
">
<xsl:attribute name="{name()}">2</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Output
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="carareWrap">
<xs:annotation>
<xs:documentation xml:lang="en">The CARARE wrapper element. It wraps CARARE elements.</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="2" maxOccurs="unbounded" ref="carare"></xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
A few things to notice:
the namespace declaration xmlns:xs="http://www.w3.org/2001/XMLSchema" so the xs prefix is available in the XSLT stylesheet
the use of the identity template copy everything that is not handled otherwise
the use of a complex match expression to pick a specific node
the use of an attribute value template and the name() function to copy the attribute name: name="{name()}"

Remove unused elements from XML schema using XSLT

I'm looking for a way (if it's even possible) of using an XSL transform of an XSD document to remove unused elements. This comes up a lot in my job where a company will define an XSD with absolutely everything in it, but then they will want to create a cut-down version for a single root element within it.
To explain further, I might have an XSD like the following:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="RootElement">
<xs:complexType>
<xs:sequence>
<xs:element ref="ChildElement"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="ChildElement"/>
<xs:element name="UnusedElement"/>
</xs:schema>
What I would like to be able to do is to set up an XSL where I provide the starting element (in this case RootElement) and it will copy over all dependent elements but omit the unused ones. In the above example, if I passed in RootElement I'd expect to see RootElement and ChildElement included but UnusedElement omitted.
(When I say "provide the starting element", I'm quite happy to crack open the stylesheet and type xsl:template match="RootElement" where required.)
This would obviously have to be recursive, so would navigate the entire structure defined below the starting element, and any element in that schema that was not used would be discarded.
(Of course, it would be even better if it could do the same in any imported schemas!)
I've searched Google extensively and can't find anything on this - I'm not sure if that means it's not possible or not.
Thanks!
Edit: Actually I probably should clarify and say that I would like to remove unused elements AND types, so it would follow both ref="childElement" and type="someType" links.
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" >
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="ptopElementName" select="'RootElement'"/>
<xsl:variable name="vTop" select=
"/*/xs:element[#name=$ptopElementName]"/>
<xsl:variable name="vNames"
select="$vTop/descendant-or-self::*/#name"/>
<xsl:variable name="vRefs"
select="$vTop/descendant-or-self::*/#ref"/>
<xsl:variable name="vTypes"
select="$vTop/descendant-or-self::*/#type"/>
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="xs:element">
<xsl:if test=
"#name=$vNames
or
#name=$vRefs
or
ancestor-or-self::*[#name=$ptopElementName]">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
<xsl:template match="xs:complexType|xs:simpleType">
<xsl:if test=
"#name=$vTypes
or
ancestor-or-self::*[#name=$ptopElementName]">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="RootElement">
<xs:complexType>
<xs:sequence>
<xs:element ref="ChildElement"/>
</xs:sequence>
</xs:complexType></xs:element>
<xs:element name="ChildElement"/>
<xs:element name="UnusedElement"/>
</xs:schema>
produces the wanted, corect result:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="RootElement">
<xs:complexType>
<xs:sequence>
<xs:element ref="ChildElement"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="ChildElement"/>
</xs:schema>