This solution requires that you streamline the XPATH input information a bit and enable two-step transformation. The first transformation will write a stylesheet to be executed in the second transformation. Thus, the client needs to make two calls to the XSLT engine. Let us know if this is a problem.
Step one
Please reorganize your XPATH information in an XML document, for example. This should not be difficult to do, and even an XSLT script can be written to complete the task.
<paths> <rule> <match>article[1]/id[1]</match> <namespaces> <namespace prefix="ns1">http://predic8.com/wsdl/material/ArticleService/1/</namespace> </namespaces> <replacement>1</replacement> </rule> <rule> <match>article[1]/description[1]</match> <namespaces/> <replacement>bar</replacement> </rule> ... etc ... </paths>
Solution Constraints
In the above rules document, we are limited to:
- The match is an implicit prefix expression: / create / '. Do not state this explicitly.
- All matches must begin as article [n], where n is a sequence number.
- We cannot have zero rules.
- Any prefixes you use in the match except s11 = "http://schemas.xmlsoap.org/soap/envelope/" and ns1 = "http://predic8.com/wsdl/material/ArticleService/1/". (Note: I do not think this is true so that namespaces end with "/", but are not sure about it) are defined in node namespaces.
The above document is the first step conversion. Apply this document to this style ...
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:step2="http://www.w3.org/1999/XSL/Transform-step2" xmlns:s11="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://predic8.com/wsdl/material/ArticleService/1/" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes='xsl'> <xsl:output method="xml" indent="yes" encoding="UTF-8" /> <xsl:namespace-alias stylesheet-prefix="step2" result-prefix="xsl"/> <xsl:template match="/"> <step2:stylesheet version="2.0"> <step2:output method="xml" indent="yes" encoding="UTF-8" /> <step2:variable name="replicated-template" as="element()*"> <step2:apply-templates select="/" mode="replication" /> </step2:variable> <step2:template match="@*|node()" mode="replication"> <step2:copy> <step2:apply-templates select="@*|node()" mode="replication" /> </step2:copy> </step2:template> <step2:template match="/s11:Envelope/s11:Body/ns1:create/article" mode="replication"> <step2:variable name="replicant" select="." /> <step2:for-each select="for $i in 1 to {max(for $m in /paths/rule/match return xs:integer(substring-before(substring-after($m,'article['),']')))} return $i"> <step2:for-each select="$replicant"> <step2:copy> <step2:apply-templates select="@*|node()" mode="replication" /> </step2:copy> </step2:for-each> </step2:for-each> </step2:template> <step2:template match="@*|node()"> <step2:copy> <step2:apply-templates select="@*|node()"/> </step2:copy> </step2:template> <step2:template match="/"> <step2:apply-templates select="$replicated-template" /> </step2:template> <xsl:apply-templates select="paths/rule" /> </step2:stylesheet> </xsl:template> <xsl:template match="rule"> <step2:template match="s11:Envelope/s11:Body/ns1:create/{match}"> <xsl:for-each select="namespaces/namespace"> <xsl:namespace name="{@prefix}" select="." /> </xsl:for-each> <step2:copy> <step2:apply-templates select="@*"/> <step2:value-of select="'{replacement}'"/> <step2:apply-templates select="*"/> </step2:copy> </step2:template> </xsl:template> </xsl:stylesheet>
Second step
Apply your soap envelope file as an input to the stylesheet that was displayed from the first step. The result is an original soap document that changes as needed. This is an example of the style sheet of the second step, in which, for simplicity of illustration, only the first rule is considered (/ create / article [1] / id => 1).
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:s11="http://schemas.xmlsoap.org/soap/envelope/" version="2.0"> <xsl:output method="xml" indent="yes" encoding="UTF-8"/> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template xmlns:ns1="http://predic8.com/wsdl/material/ArticleService/1/" match="/s11:Envelope/s11:Body/ns1:create[1]/article[1]/id[1]"> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:value-of select="'1'"/> <xsl:apply-templates select="*"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
Additional restrictions for solution
- The template document must contain at least one / s 11: Envelope / s11: Body / ns1: create / article. Only the node article is replicated (deeply) in accordance with the requirements of the rules. In addition, it can be any structure.
- The template document cannot contain sub-levels s11: Envelope / s11: Body / ns1: create node.
Description
You will notice that your XPATH expressions are not much removed from the pattern matching condition. Therefore, itβs not so difficult to write a style sheet that re-expresses your XPATH and substitution values ββas template rules. When writing styles styles, xsl: namespace-alias allows us to eliminate "xsl:" as an instruction and "xsl:" as the intended output. When XSLT 3.0 arrives, we are likely to be able to reduce this algorithm by one step, as this will allow us to dynamically evaluate XPATH, which is actually the zero point of your problem. But for now, we should be content with a two-step process.
The second style sheet is a two-phase conversion. The first stage replicates the template from the article level, as many times as necessary by the rules. The second phase analyzes this replicated template and applies dynamic rules that replace text values ββas specified in XPATH.
UPDATE
My initial message was incorrect. Thanks to Demetrius for pointing out the error. Please find the updated solution above.
After thinking
If the two-step solution is too complicated and you are running the wintel platform, you might consider buying a commercial version of Saxon. I believe the commercial version has a dynamic XPATH evaluation function. I cannot give you such a solution because I do not have a commercial version. I believe that a solution using the evaluation function () would be much simpler. XSLT is just a hobby for me. But if you use XSLT for business purposes, the price is reasonable.