S
Scott Sauyet
I'm trying to select a subset of a WSDL document using XSLT and a
simple text document describing the high-level elements to include. I
have it working fine for selecting the services, bindings, portTypes,
and messages to use. But I'm stumped on how to get the xs:simpleType
and xs:complexType elements filtered properly. I am not trying to do
this in a generic way. The WSDL is generated by a tool in our backend
system, and I know a fair bit about its structure, although I have no
control over it.
I am neither a novice nor an expert with XSLT. I don't use it much,
though, and I'm sure it shows. Any advice would be appreciated. If I
have to resort to doing this in code, I will, but I thought XSLT would
be up to the job...
I posted a sample WSDL with only one high-level service in it. It
represents both the input and the expected output format:
http://scott.sauyet.com/issues/2007-05-24/temp.wsdl
I import the text file (a whitespace separated list of service names)
and store it in a string simply by
<!DOCTYPE xsl:stylesheet [
<!ENTITY service-list SYSTEM "services.txt">
]>
<xsl:variable name="list">
<xsl:call-template name="normalize">
<xsl:with-param name="data">&service-list;</xsl:with-
param>
</xsl:call-template>
</xsl:variable>
<xsl:template name="normalize">
<xslaram name="data" />
<xsl:value-of select="concat(normalize-space($data), ' ')" />
</xsl:template>
And I use a similar technique to define "service-list", which is the
same list with "Service" appended to each name.
Inside my main template, I can then use this to define the services to
choose:
<xsl:for-each select="wsdl:service">
<xsl:if test="contains($service-list, @name)">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
And I can select portTypes just as simply:
<xsl:for-each select="wsdlortType">
<xsl:if test="contains($list, @name)">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
Bindings are slightly more complex:
<xsl:for-each select="wsdl:binding">
<xsl:variable name="bindName" select="concat('tns:', @name)"/>
<xsl:if test="count(../wsdl:service/wsdlort[@binding=
$bindName and contains($service-list, ../@name)]) > 0">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
And messages are getting scary:
<xsl:for-each select="wsdl:message">
<xsl:variable name="messName" select="concat('tns:', @name)"/>
<xsl:if test="count(../wsdlortType/wsdlperation/
wsdl:input[@message=$messName and contains($list, ../../@name)] | ../
wsdlortType/wsdlperation/wsdlutput[@message=$messName and
contains($list, ../../@name)]) > 0">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
But I can't really figure out any way to get to, say, the complexTypes
required. What I want, in essence, is to display a complex type C
if there is a message M with a part P where P/@type = C.name and there
exists a portType T with an operation O with an input I where I/
@message = M/@name. (And this is a simplification?!) But I can't see
how to express that in XPath.
Any suggestions?
Thanks,
-- Scott Sauyet
simple text document describing the high-level elements to include. I
have it working fine for selecting the services, bindings, portTypes,
and messages to use. But I'm stumped on how to get the xs:simpleType
and xs:complexType elements filtered properly. I am not trying to do
this in a generic way. The WSDL is generated by a tool in our backend
system, and I know a fair bit about its structure, although I have no
control over it.
I am neither a novice nor an expert with XSLT. I don't use it much,
though, and I'm sure it shows. Any advice would be appreciated. If I
have to resort to doing this in code, I will, but I thought XSLT would
be up to the job...
I posted a sample WSDL with only one high-level service in it. It
represents both the input and the expected output format:
http://scott.sauyet.com/issues/2007-05-24/temp.wsdl
I import the text file (a whitespace separated list of service names)
and store it in a string simply by
<!DOCTYPE xsl:stylesheet [
<!ENTITY service-list SYSTEM "services.txt">
]>
<xsl:variable name="list">
<xsl:call-template name="normalize">
<xsl:with-param name="data">&service-list;</xsl:with-
param>
</xsl:call-template>
</xsl:variable>
<xsl:template name="normalize">
<xslaram name="data" />
<xsl:value-of select="concat(normalize-space($data), ' ')" />
</xsl:template>
And I use a similar technique to define "service-list", which is the
same list with "Service" appended to each name.
Inside my main template, I can then use this to define the services to
choose:
<xsl:for-each select="wsdl:service">
<xsl:if test="contains($service-list, @name)">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
And I can select portTypes just as simply:
<xsl:for-each select="wsdlortType">
<xsl:if test="contains($list, @name)">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
Bindings are slightly more complex:
<xsl:for-each select="wsdl:binding">
<xsl:variable name="bindName" select="concat('tns:', @name)"/>
<xsl:if test="count(../wsdl:service/wsdlort[@binding=
$bindName and contains($service-list, ../@name)]) > 0">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
And messages are getting scary:
<xsl:for-each select="wsdl:message">
<xsl:variable name="messName" select="concat('tns:', @name)"/>
<xsl:if test="count(../wsdlortType/wsdlperation/
wsdl:input[@message=$messName and contains($list, ../../@name)] | ../
wsdlortType/wsdlperation/wsdlutput[@message=$messName and
contains($list, ../../@name)]) > 0">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
But I can't really figure out any way to get to, say, the complexTypes
required. What I want, in essence, is to display a complex type C
if there is a message M with a part P where P/@type = C.name and there
exists a portType T with an operation O with an input I where I/
@message = M/@name. (And this is a simplification?!) But I can't see
how to express that in XPath.
Any suggestions?
Thanks,
-- Scott Sauyet