Help with identifying unique text elements

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">

<xsl:eek:utput 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
 
R

roy axenov

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...)
Here is a sample input .XML file:
[XML]

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):

You seem to have xmlns problems, among others. Anyway, try
toying with the following, it just might do what you need:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:f="http://someDomain/PropertyClassSpec"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
version="1.0">
<xsl:eek:utput
method="xml"
version="1.0"
encoding="UTF-8"/>
<xsl:template match="/">
<xsl:element name="xs:schema">
<xsl:attribute
name="elementFormDefault">qualified</xsl:attribute>
<xsl:attribute
name="targetNamespace"><xsl:value-of select="
concat('http://someDomain/',@Name)
"/></xsl:attribute>
<xsl:apply-templates select="
descendant::f:propertyClass/f:propertyClassName|
descendant::f:propertyClassVec/f:propertyClassNames
/f:propertyClassName">
<xsl:sort order="ascending" select="text()"/>
</xsl:apply-templates>
</xsl:element>
</xsl:template>
<xsl:template
match="//f:propertyClass/f:propertyClassName|
//f:propertyClassVec/f:propertyClassNames
/f:propertyClassName">
<xsl:if
test="generate-id(.)=
generate-id((//f:propertyClass/f:propertyClassName|
//f:propertyClassVec/f:propertyClassNames
/f:propertyClassName)[text()=current()/text()][1])">
<xsl:element name="xs:import">
<xsl:attribute
name="namespace"><xsl:value-of select="
concat('http://someDomain/',text())
"/></xsl:attribute>
<xsl:attribute name="schemaLocation"><xsl:value-of
select="concat(text(), '.xsd')"/></xsl:attribute>
</xsl:element>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top