Hello,
In the spirit of talk radio, I'm a first-time poster, long-time listener. This site has been a gold mine of XSLT resources. Thanks. This will sound like a SOAP question, but the solution is using XSLT.
I am calling legacy SOAP rpc/encoded services from a newer platform that does not support the entire SOAP rpc spec. I need to transform xml formatted with multireference accessors(href/id) to a more traditional inline xml. Our COTS software expects one child under the SOAP Body, and multireference rpc payloads contain multiple children under the SOAP Body. I am trying to transform this:
<env:Envelope xmlns:env="
http://schemas.xmlsoap.org/soap/envelope/" xmlns:tns="
http://target.org" env:encodingStyle="
http://schemas.xmlsoap.org/soap/encoding/">
<env:Body>
<tns:legacyRpcServiceResponse>
<result href="#ID1"/>
</tns:legacyRpcServiceResponse>
<tns:LookupTableResult id="ID1" xsi:type="tns:LookupTableResult">
<returnCode xsi:type="xsd:string">0</returnCode>
<returnMessage xsi:type="xsd:string" xsi:nil="1"/>
<tableArray href="#ID2"/>
</tns:LookupTableResult>
<tns:ArrayOfTableArray id="ID2" xsi:type="enc:Array" enc:arrayType="tns:TableArray[2]">
<item href="#ID3"/>
<item href="#ID4"/>
</tns:ArrayOfTableArray>
<tns:TableArray id="ID3" xsi:type="tns:TableArray">
<lastChangeDt xsi:type="xsd:string">2009-05-12-13.13.42.145</lastChangeDt>
<tableName xsi:type="xsd:string">NameTable</tableName>
</tns:TableArray>
<tns:TableArray id="ID4" xsi:type="tns:TableArray">
<lastChangeDt xsi:type="xsd:string"/>
<tableName xsi:type="xsd:string">JobTable</tableName>
</tns:TableArray>
</env:Body>
</env:Envelope>
to this inline version:
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="
http://schemas.xmlsoap.org/soap/envelope/" xmlns:tns="
http://target.org" env:encodingStyle="
http://schemas.xmlsoap.org/soap/encoding/">
<env:Body>
<tns:legacyRpcServiceResponse>
<result>
<tns:LookupTableResult type="tns:LookupTableResult">
<returnCode type="xsd:string">0</returnCode>
<returnMessage type="xsd:string" nil="1"/>
<tableArray>
<tns:ArrayOfTableArray type="enc:Array" arrayType="tns:TableArray[2]">
<item>
<tns:TableArray type="tns:TableArray">
<lastChangeDt type="xsd:string">2009-05-12-13.13.42.145</lastChangeDt>
<tableName type="xsd:string">NameTable</tableName>
</tns:TableArray>
</item>
<item>
<tns:TableArray type="tns:TableArray">
<lastChangeDt type="xsd:string"/>
<tableName type="xsd:string">JobTable</tableName>
</tns:TableArray>
</item>
</tns:ArrayOfTableArray>
</tableArray>
</tns:LookupTableResult>
</result>
</tns:legacyRpcServiceResponse>
</env:Body>
</env:Envelope>
My XSLT below uses templates to copy all nodes. When it encounters a node containing an href attribute, it copies the nodeset with the matching id attribute. I had to use a mode to keep from copying the elements with an id attribute twice. Some of the services I call can contain 1,000's of references. The bulk of my xslt is spent selecting the elements referenced by the href. I have saved a ton of time using a key, but believe that there is still a lot of room for improvement. Are there tricks out there to:
1) select an element by id from a distinct large list of elements?
2) stop processing the document once I've finished processing the first child of the soap:Body ?
My xslt is:
<xsl:stylesheet version="1.0" xmlns:xs="
http://www.w3.org/2001/XMLSchema" xmlns:xsl="
http://www.w3.org/1999/XSL/Transform">
<xsl:key name="linkableItems" match="//*[@id]" use="@id"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="@*[name()='id']|@*[name()='href']"/>
<xsl:template match="node()" mode="normalize">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node()[@href]">
<xsl:copy>
<xsl:apply-templates select="key('linkableItems',substring-after(current()/@href,'#'))" mode="normalize"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node()[@id]"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Thank you.