什么是填充(或产生)从XPath表达式的映射XML模板文件的最好方法?
的要求,我们将需要开始使用模板(因为这可能包含在XPath表达式没有其他捕获的信息)。
例如,起始模板可能是:
<s11:Envelope xmlns:s11='http://schemas.xmlsoap.org/soap/envelope/'>
<ns1:create xmlns:ns1='http://predic8.com/wsdl/material/ArticleService/1/'>
<article xmlns:ns1='http://predic8.com/material/1/'>
<name>?XXX?</name>
<description>?XXX?</description>
<price xmlns:ns1='http://predic8.com/common/1/'>
<amount>?999.99?</amount>
<currency xmlns:ns1='http://predic8.com/common/1/'>???</currency>
</price>
<id xmlns:ns1='http://predic8.com/material/1/'>???</id>
</article>
</ns1:create>
</s11:Body>
</s11:Envelope>
然后,我们提供的,是这样的:
expression: /create/article[1]/id => 1
expression: /create/article[1]/description => bar
expression: /create/article[1]/name[1] => foo
expression: /create/article[1]/price[1]/amount => 00.00
expression: /create/article[1]/price[1]/currency => USD
expression: /create/article[2]/id => 2
expression: /create/article[2]/description => some name
expression: /create/article[2]/name[1] => some description
expression: /create/article[2]/price[1]/amount => 00.01
expression: /create/article[2]/price[1]/currency => USD
然后,我们应该产生:
<ns1:create xmlns:ns1='http://predic8.com/wsdl/material/ArticleService/1/'>
<article xmlns:ns1='http://predic8.com/material/1/'>
<name xmlns:ns1='http://predic8.com/material/1/'>foo</name>
<description>bar</description>
<price xmlns:ns1='http://predic8.com/common/1/'>
<amount>00.00</amount>
<currency xmlns:ns1='http://predic8.com/common/1/'>USD</currency>
</price>
<id xmlns:ns1='http://predic8.com/material/1/'>1</id>
</article>
<article xmlns:ns1='http://predic8.com/material/2/'>
<name>some name</name>
<description>some description</description>
<price xmlns:ns1='http://predic8.com/common/2/'>
<amount>00.01</amount>
<currency xmlns:ns1='http://predic8.com/common/2/'>USD</currency>
</price>
<id xmlns:ns1='http://predic8.com/material/2/'>2</id>
</article>
</ns1:create>
我用Java实现,但我宁愿一个基于XSLT的解决方案,如果一个是可能的。
PS:这个问题的另一种相反的问题,我最近问。
这种转变从“表达式”有想要的结果的结构的XML文档创建-它仍然是这个结果转化为最终的结果 :
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="my:my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vPop" as="element()*">
<item path="/create/article[1]/id">1</item>
<item path="/create/article[1]/description">bar</item>
<item path="/create/article[1]/name[1]">foo</item>
<item path="/create/article[1]/price[1]/amount">00.00</item>
<item path="/create/article[1]/price[1]/currency">USD</item>
<item path="/create/article[1]/price[2]/amount">11.11</item>
<item path="/create/article[1]/price[2]/currency">AUD</item>
<item path="/create/article[2]/id">2</item>
<item path="/create/article[2]/description">some name</item>
<item path="/create/article[2]/name[1]">some description</item>
<item path="/create/article[2]/price[1]/amount">00.01</item>
<item path="/create/article[2]/price[1]/currency">USD</item>
</xsl:variable>
<xsl:template match="/">
<xsl:sequence select="my:subTree($vPop/@path/concat(.,'/',string(..)))"/>
</xsl:template>
<xsl:function name="my:subTree" as="node()*">
<xsl:param name="pPaths" as="xs:string*"/>
<xsl:for-each-group select="$pPaths"
group-adjacent=
"substring-before(substring-after(concat(., '/'), '/'), '/')">
<xsl:if test="current-grouping-key()">
<xsl:choose>
<xsl:when test=
"substring-after(current-group()[1], current-grouping-key())">
<xsl:element name=
"{substring-before(concat(current-grouping-key(), '['), '[')}">
<xsl:sequence select=
"my:subTree(for $s in current-group()
return
concat('/',substring-after(substring($s, 2),'/'))
)
"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="current-grouping-key()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:for-each-group>
</xsl:function>
</xsl:stylesheet>
当在任何XML文档(未使用)被施加这种转化,其结果是 :
<create>
<article>
<id>1</id>
<description>bar</description>
<name>foo</name>
<price>
<amount>00.00</amount>
<currency>USD</currency>
</price>
<price>
<amount>11.11</amount>
<currency>AUD</currency>
</price>
</article>
<article>
<id>2</id>
<description>some name</description>
<name>some description</name>
<price>
<amount>00.01</amount>
<currency>USD</currency>
</price>
</article>
</create>
注意 :
你需要变换“表情”您将得到进入在这一转变中使用的格式 - 这是容易和简单。
在最后的改造,你需要每一个节点复制“原样”(使用标识规则),除应生成的顶级节点在"http://predic8.com/wsdl/material/ArticleService/1/"
命名空间。 需要注意的是目前在“模板”的其他命名空间中未使用,可以安全地ommitted。
此解决方案需要你稍微重新组织你的XPATH输入信息,并允许2步改造。 第一变换将写入样式表,这将在第二变换被执行 - 因此,客户端需要执行XSLT引擎的两个调用。 让我们知道这是一个问题。
第一步
请重新组织你的XPATH信息转换成XML文档,像这样。 它不应该是很难做到的,甚至是XSLT脚本可以写做的工作。
<paths>
<rule>
<match>article[1]/id[1]</match>
<namespaces>
<namespace prefix="ns1">http://predic8.com/wsdl/material/ArticleService/1/</namespace>
<!-- The namespace node declares a namespace that is used in the match expression.
There can be many of these. It is not required to define the s11: namespace,
nor the ns1 namespace. -->
</namespaces>
<replacement>1</replacement>
</rule>
<rule>
<match>article[1]/description[1]</match>
<namespaces/>
<replacement>bar</replacement>
</rule>
... etc ...
</paths>
解决方案限制
在上述规则的文件,我们约束,使得:
- 匹配被隐含前缀“表达:/创建/”。 不要把明确。
- 所有比赛必须开始像文章[n],其中n是一些序号。
- 我们不能让零条规则。
- 你在比赛中使用前缀,比其他S11 =“http://schemas.xmlsoap.org/soap/envelope/”和NS1 =“http://predic8.com/wsdl/material/ArticleService/1/” 。 (注:我不认为这是有效的命名空间中“/”结尾 - 但不知道这一点),在命名空间节点定义。
以上是输入文件到步骤一个变换。 本文件适用于这个样式表...
<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>
第二步
应用您的SOAP信封的文件,作为输入文件,将样式表这是从第一步输出。 其结果是原来的SOAP文档,根据需要改变。 这是一步两个样式表的一个样本,只用第一规则(/创建/条[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>
更多解决方案限制
- 模板文件必须包含至少一个/ S11:信封/ S11:身体/ NS1:创建/条。 只有文章节点被复制(深)本所要求的规则。 除了比它可以是任何结构。
- 模板文件不能包含S11的嵌套层次:信封/ S11:身体/ NS1:创建节点。
说明
你会发现,你的XPath表达式不远处模板的匹配条件删除。 因此,这是不是太困难编写样式表从而重新表达你的XPath和重置价值为模板规则。 当编写一个样式表书写样式表的XSL:命名空间别名使我们能够消除歧义“的xsl:”作为指令和“XSL:”如预期输出。 当XSLT 3.0出现时,我们都安静可能能够减少这种算法为一步,因为这将允许动态XPATH评价,这真的是你的问题的要点。 但是现在我们必须满足于2步骤的过程。
第二样式表是两相转变。 第一阶段从复制的文章水平的模板,如需要通过规则很多次。 第二阶段分析此复制模板,并且应用动态规则通过的XPath所示代文本值。
UPDATE
我原来的职位是错误的。 由于Dimitre您指出的错误。 请上面找到更新的解决方案。
经过深思熟虑
如果两步solultion是太复杂,您是在wintel的平台上运行,你可以考虑购买撒克逊的商业版本。 我认为,商业版本具有动态XPATH评价函数。 我不能给你这样的解决方案,因为我没有商业版本。 我想用评价()函数会被很多简单的解决方案。 XSLT只是我的一个爱好。 但是,如果你正在使用XSLT为经营宗旨,价格合理安静。