Creating XSLT transform to flatten multiRef encoded SOAP message
Input is a mutliRef encoded SOAP message/document. How do you use XSLT to flatten multiRefs. Multiref nodes can be referenced used multiple times, and themselves recursively reference other multiRef nodes.
The only pieces of the structure that are safe to refer to, are multiRef elements and @id and @href attributes. Other elements or namespaces could change.
Sample input message is:
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<ns1:getAccountDTOResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns1="http://www.example.com/pw/services/PWServices">
<getAccountDTOReturn href="#id0" />
</ns1:getAccountDTOResponse>
<multiRef id="id0" soapenc:root="0"
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="ns2:Account"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns2="urn:PWServices">
<ID href="#id1" />
<accountNumber xsi:type="soapenc:string"></accountNumber>
<accountType xsi:type="soapenc:string"></accountType>
<clientData xsi:type="soapenc:Array" xsi:nil="true" />
<name xsi:type="soapenc:string"></name>
<parentRef xsi:type="soapenc:string"></parentRef>
</multiRef>
<multiRef id="id1" soapenc:root="0"
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="xsd:long"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
0</multiRef>
</soapenv:Body>
</soapenv:Envelope>
Expected output is:
<?xml version="1.0"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<ns1:getAccountDTOResponse xmlns:ns1="http://www.example.com/pw/services/PWServices"
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<getAccountDTOReturn xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns2="urn:PWServices"
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="ns2:Account">
<ns1:ID soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="xsd:long">0</ns1:ID>
<ns1:accountNumber xsi:type="soapenc:string" />
<ns1:accountType xsi:type="soapenc:string" />
<ns1:clientData xsi:type="soapenc:Array" xsi:nil="true" />
<ns1:name xsi:ty开发者_如何学运维pe="soapenc:string" />
<ns1:parentRef xsi:type="soapenc:string" />
</getAccountDTOReturn>
</ns1:getAccountDTOResponse>
</soapenv:Body>
</soapenv:Envelope>
Update: In the example above, logically, what should happen is:
On the first pass, getAccountDTOResponse contains @href="#id0", so that element is replaced with all the children multiRef element with @id="id0", with exception of.
On the second pass, @href="#id1" should be discovered, and ID element should be replaced with contents of element with @id="id1".
No multiRef elements should exists in the output. No attributes @id or @href should exists in output, if there were involved in this whole multiRef mess.
Alex, I'm not matching your output completely but here is how you can resolve your document by hrefs.
This stylesheet:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" >
<xsl:key name="multiref-by-id" match="multiRef" use="@id"/>
<xsl:template match="/">
<xsl:copy>
<xsl:apply-templates select="@*|*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[starts-with(@href, '#')]">
<xsl:copy>
<xsl:apply-templates select="@* |
key('multiref-by-id', substring-after(@href, '#'))/@* |
key('multiref-by-id', substring-after(@href, '#'))/node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@href[starts-with(., '#')] | multiRef[@id] | @soapenc:root"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Given this input:
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<ns1:getAccountDTOResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns1="http://www.example.com/pw/services/PWServices">
<getAccountDTOReturn href="#id0"/>
</ns1:getAccountDTOResponse>
<multiRef id="id0" soapenc:root="0"
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="ns2:Account"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns2="urn:PWServices">
<ID href="#id1"/>
<accountNumber xsi:type="soapenc:string"></accountNumber>
<accountType xsi:type="soapenc:string"></accountType>
<clientData xsi:type="soapenc:Array" xsi:nil="true"/>
<name xsi:type="soapenc:string"></name>
<parentRef xsi:type="soapenc:string"></parentRef>
</multiRef>
<multiRef id="id1" soapenc:root="0"
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="xsd:long"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
0
</multiRef>
</soapenv:Body>
</soapenv:Envelope>
Produces the following result:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<ns1:getAccountDTOResponse xmlns:ns1="http://www.example.com/pw/services/PWServices"
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<getAccountDTOReturn id="id0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="ns2:Account">
<ID xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns2="urn:PWServices" id="id1"
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="xsd:long">
0
</ID>
<accountNumber xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns2="urn:PWServices"
xsi:type="soapenc:string"/>
<accountType xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns2="urn:PWServices"
xsi:type="soapenc:string"/>
<clientData xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns2="urn:PWServices"
xsi:type="soapenc:Array" xsi:nil="true"/>
<name xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns2="urn:PWServices"
xsi:type="soapenc:string"/>
<parentRef xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns2="urn:PWServices"
xsi:type="soapenc:string"/>
</getAccountDTOReturn>
</ns1:getAccountDTOResponse>
</soapenv:Body>
</soapenv:Envelope>
I think that this apporach could be easily customized to fit your needs. I would like to outline that the provided stylesheet operates on @hrefs
and does not take in account any element names. Therefore it can be used flexibly without paying attention to the referring element names. However all the reference should be named multiRef
s but this can be easily customized too.
This stylesheet:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
<xsl:key name="kMultiRefById" match="multiRef" use="@id"/>
<xsl:output method="xml"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="getAccountDTOReturn">
<xsl:variable name="vRefer"
select="key('kMultiRefById',substring(@href,2))"/>
<xsl:copy>
<xsl:copy-of select="$vRefer/namespace::*"/>
<xsl:apply-templates select="$vRefer/@*|$vRefer/node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="multiRef|multiRef/@id|multiRef/@soapenc:root"/>
</xsl:stylesheet>
Output:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<ns1:getAccountDTOResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns1="http://www.example.com/pw/services/PWServices">
<getAccountDTOReturn soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="ns2:Account"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns2="urn:PWServices">
<ID href="#id1"></ID>
<accountNumber xsi:type="soapenc:string"></accountNumber>
<accountType xsi:type="soapenc:string"></accountType>
<clientData xsi:type="soapenc:Array" xsi:nil="true"></clientData>
<name xsi:type="soapenc:string"></name>
<parentRef xsi:type="soapenc:string"></parentRef>
</getAccountDTOReturn>
</ns1:getAccountDTOResponse>
</soapenv:Body>
</soapenv:Envelope>
Note: Keys for cross references. Identity rule. Empty rules for stripping. Copying namespaces nodes might not be posible for every XSLT processor, although I only know about Mozilla's TransforMiiX wich doesn't have implemented namespaces::
axis.
精彩评论