D
daldridge
I have a unique-elements/sorting question (who doesn't?), but haven't
yet been able to get appropriate template/select/for-each processing
working. I don't fully grok the Muenchian technique yet (still an XSLT
n00b), but I'm not sure that's the way to go anyway...
What I'm trying to accomplish is generation of XML Schema output from a
given XML input, with "xs:import" elements in the output with the
"namespace" and "schemaLocation" attributes corresponding to a unique
set of text values found in the input file. (I don't need assistance
with the required <xsl:element> output, as I have a solution for that
figured out. Just the appropriate select/group/for-each processing, as
the xs:import's must be unique...)
If I were to put the select statement in words, I think this is what I
want to accomplish:
"determine the set of unique text values of all elements named
'PropertyClassName', which have a parent of
'PropertyClassSpec/PropertyClassVec/PropertyClassNames' or
'PropertyClassSpec/PropertyClass'".
Here is a sample input .XML file:
<?xml version="1.0" encoding="UTF-8"?>
<PropertyClassSpec xmlns="http://someDomain/PropertyClassSpec"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://someDomain/PropertyClassSpec
PropertyClassSpec.xsd"
ID="12345" Name="_TestSpec">
<PropertyClass Index="0">
<Name>SomePropertyClass</Name>
<PropertyClassName>_TestSubSpec1</PropertyClassName>
</PropertyClass>
<PropertyClassVec Index="1">
<Name>VectorOfPropertyClasses</Name>
<PropertyClassNames>
<PropertyClassName>_TestSubSpec1</PropertyClassName>
<PropertyClassName>_TestSubSpec1</PropertyClassName>
<PropertyClassName>_TestSubSpec2</PropertyClassName>
<PropertyClassName>_TestSubSpec3</PropertyClassName>
</PropertyClassNames>
<ReserveSize>100</ReserveSize>
</PropertyClassVec>
<SomeOtherTopLevelElement>
<PropertyClassName>DontCatchThisOne</PropertyClassName>
</SomeOtherTopLevelElement>
<PropertyClassName>DontGrabThisOneEither</PropertyClassName>
</PropertyClassSpec>
Given that input, I'd want three xs:imports: one for _TestSubSpec1,
_TestSubSpec2, and _TestSubSpec3.
Here's the (not-quite-working) transform I have thus far (note: I'm
using Saxon8B, so feel free to offer XSLT 2.0 solutions/suggestions,
though v1.0 is fine, too):
<?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"
version="2.0">
<xslutput method="xml" version="1.0" encoding="UTF-8"
indent="yes"/>
<!-- Consume any input we don't understand (no output generated) -->
<xsl:template match="*"/>
<!-- Root xs:schema element generation -->
<xsl:template match="PropertyClassSpec">
<xsl:element name="xs:schema">
<xsl:attribute name="elementFormDefault" select="'qualified'"/>
<xsl:attribute name="targetNamespace"
select="concat('http://someDomain/', @Name)"/>
<xsl:namespace name="xs"
select="'http://www.w3.org/2001/XMLSchema'"/>
<!-- I *think* this is an appropriate for-each selection...? -->
<xsl:for-each
select="PropertyClass/PropertyClassName |
PropertyClassVec/PropertyClassNames/PropertyClassName">
<xsl:sort order="ascending" select="text()"/>
<!-- Try using a remainder-based comparison I found via a
Google search. Maybe Muenchian would work, if I ever figure that out?
-->
<xsl:variable name="propertyClassName" select="text()"/>
<xsl:variable name="remainder"
select="following-sibling[text()=$propertyClassName]"/>
<xsl:if test="not($remainder)">
<xsl:element name="xs:import">
<xsl:attribute name="namespace"
select="concat('http://someDomain/',
$propertyClassName)"/>
<xsl:attribute name="schemaLocation"
select="concat($propertyClassName, '.xsd')"/>
</xsl:element>
</xsl:if>
</xsl:for-each>
<!-- The schema, as aside from the namespace and imports
determined above, has a single top-level element which is a sequence.
All further template matching/process will generate elements for this
sequence. -->
<xsl:element name="xs:element">
<xsl:attribute name="name" select="@Name"/>
<xsl:element name="xs:complexType">
<xsl:element name="xs:sequence">
<xsl:apply-templates/>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:template>
<... other template matching and processing here, to generate child
elements under the sequence element generated above ...>
</xsl:stylesheet>
Thank you for any suggestions/solutions you might be able to provide!
Cheers,
David
yet been able to get appropriate template/select/for-each processing
working. I don't fully grok the Muenchian technique yet (still an XSLT
n00b), but I'm not sure that's the way to go anyway...
What I'm trying to accomplish is generation of XML Schema output from a
given XML input, with "xs:import" elements in the output with the
"namespace" and "schemaLocation" attributes corresponding to a unique
set of text values found in the input file. (I don't need assistance
with the required <xsl:element> output, as I have a solution for that
figured out. Just the appropriate select/group/for-each processing, as
the xs:import's must be unique...)
If I were to put the select statement in words, I think this is what I
want to accomplish:
"determine the set of unique text values of all elements named
'PropertyClassName', which have a parent of
'PropertyClassSpec/PropertyClassVec/PropertyClassNames' or
'PropertyClassSpec/PropertyClass'".
Here is a sample input .XML file:
<?xml version="1.0" encoding="UTF-8"?>
<PropertyClassSpec xmlns="http://someDomain/PropertyClassSpec"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://someDomain/PropertyClassSpec
PropertyClassSpec.xsd"
ID="12345" Name="_TestSpec">
<PropertyClass Index="0">
<Name>SomePropertyClass</Name>
<PropertyClassName>_TestSubSpec1</PropertyClassName>
</PropertyClass>
<PropertyClassVec Index="1">
<Name>VectorOfPropertyClasses</Name>
<PropertyClassNames>
<PropertyClassName>_TestSubSpec1</PropertyClassName>
<PropertyClassName>_TestSubSpec1</PropertyClassName>
<PropertyClassName>_TestSubSpec2</PropertyClassName>
<PropertyClassName>_TestSubSpec3</PropertyClassName>
</PropertyClassNames>
<ReserveSize>100</ReserveSize>
</PropertyClassVec>
<SomeOtherTopLevelElement>
<PropertyClassName>DontCatchThisOne</PropertyClassName>
</SomeOtherTopLevelElement>
<PropertyClassName>DontGrabThisOneEither</PropertyClassName>
</PropertyClassSpec>
Given that input, I'd want three xs:imports: one for _TestSubSpec1,
_TestSubSpec2, and _TestSubSpec3.
Here's the (not-quite-working) transform I have thus far (note: I'm
using Saxon8B, so feel free to offer XSLT 2.0 solutions/suggestions,
though v1.0 is fine, too):
<?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"
version="2.0">
<xslutput method="xml" version="1.0" encoding="UTF-8"
indent="yes"/>
<!-- Consume any input we don't understand (no output generated) -->
<xsl:template match="*"/>
<!-- Root xs:schema element generation -->
<xsl:template match="PropertyClassSpec">
<xsl:element name="xs:schema">
<xsl:attribute name="elementFormDefault" select="'qualified'"/>
<xsl:attribute name="targetNamespace"
select="concat('http://someDomain/', @Name)"/>
<xsl:namespace name="xs"
select="'http://www.w3.org/2001/XMLSchema'"/>
<!-- I *think* this is an appropriate for-each selection...? -->
<xsl:for-each
select="PropertyClass/PropertyClassName |
PropertyClassVec/PropertyClassNames/PropertyClassName">
<xsl:sort order="ascending" select="text()"/>
<!-- Try using a remainder-based comparison I found via a
Google search. Maybe Muenchian would work, if I ever figure that out?
-->
<xsl:variable name="propertyClassName" select="text()"/>
<xsl:variable name="remainder"
select="following-sibling[text()=$propertyClassName]"/>
<xsl:if test="not($remainder)">
<xsl:element name="xs:import">
<xsl:attribute name="namespace"
select="concat('http://someDomain/',
$propertyClassName)"/>
<xsl:attribute name="schemaLocation"
select="concat($propertyClassName, '.xsd')"/>
</xsl:element>
</xsl:if>
</xsl:for-each>
<!-- The schema, as aside from the namespace and imports
determined above, has a single top-level element which is a sequence.
All further template matching/process will generate elements for this
sequence. -->
<xsl:element name="xs:element">
<xsl:attribute name="name" select="@Name"/>
<xsl:element name="xs:complexType">
<xsl:element name="xs:sequence">
<xsl:apply-templates/>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:template>
<... other template matching and processing here, to generate child
elements under the sequence element generated above ...>
</xsl:stylesheet>
Thank you for any suggestions/solutions you might be able to provide!
Cheers,
David