我希望能够生成一个完整的XML文件,给定一组的XPath映射。
输入可在两个映射指定:(1)一个其中列出了XPath表达式和值; 和(2),其限定相应的命名空间的其他。
/create/article[1]/id => 1
/create/article[1]/description => bar
/create/article[1]/name[1] => foo
/create/article[1]/price[1]/amount => 00.00
/create/article[1]/price[1]/currency => USD
/create/article[2]/id => 2
/create/article[2]/description => some name
/create/article[2]/name[1] => some description
/create/article[2]/price[1]/amount => 00.01
/create/article[2]/price[1]/currency => USD
对于命名空间:
/create => xmlns:ns1='http://predic8.com/wsdl/material/ArticleService/1/
/create/article => xmlns:ns1='http://predic8.com/material/1/‘
/create/article/price => xmlns:ns1='http://predic8.com/common/1/‘
/create/article/id => xmlns:ns1='http://predic8.com/material/1/'
也请注意,重要的是,我也处理的XPath表达式的属性 ,以及。 例如:我也应该能够处理的属性,如:
/create/article/@type => richtext
那么最终的输出应该是这个样子:
<ns1:create xmlns:ns1='http://predic8.com/wsdl/material/ArticleService/1/'>
<ns1:article xmlns:ns1='http://predic8.com/material/1/‘ type='richtext'>
<name>foo</name>
<description>bar</description>
<ns1:price xmlns:ns1='http://predic8.com/common/1/'>
<amount>00.00</amount>
<currency>USD</currency>
</ns1:price>
<ns1:id xmlns:ns1='http://predic8.com/material/1/'>1</ns1:id>
</ns1:article>
<ns1:article xmlns:ns1='http://predic8.com/material/2/‘ type='richtext'>
<name>some name</name>
<description>some description</description>
<ns1:price xmlns:ns1='http://predic8.com/common/2/'>
<amount>00.01</amount>
<currency>USD</currency>
</ns1:price>
<ns1:id xmlns:ns1='http://predic8.com/material/2/'>2</ns1:id>
</ns1:article>
</ns1:create>
PS:这是一个以前的更详细的问题, 问题问,虽然由于一系列进一步的要求和说明,我建议问一个更广泛的问题,以解决我的需求。
还要注意,我在Java中实现这一点。 所以不管是基于Java或XSLT为基础的解决方案都希望能完全可以接受的。 日Thnx。
此外,注意:我真的很想找一个通用的解决方案。 上面示出的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:key name="kNSFor" match="namespace" use="@of"/>
<xsl:variable name="vStylesheet" select="document('')"/>
<xsl:variable name="vPop" as="element()*">
<item path="/create/article/@type">richtext</item>
<item path="/create/article/@lang">en-us</item>
<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>
<namespace of="create" prefix="ns1:"
url="http://predic8.com/wsdl/material/ArticleService/1/"/>
<namespace of="article" prefix="ns1:"
url="xmlns:ns1='http://predic8.com/material/1/"/>
<namespace of="@lang" prefix="xml:"
url="http://www.w3.org/XML/1998/namespace"/>
<namespace of="price" prefix="ns1:"
url="xmlns:ns1='http://predic8.com/material/1/"/>
<namespace of="id" prefix="ns1:"
url="xmlns:ns1='http://predic8.com/material/1/"/>
</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:variable name="vLocal-name" select=
"substring-before(concat(current-grouping-key(), '['), '[')"/>
<xsl:variable name="vNamespace"
select="key('kNSFor', $vLocal-name, $vStylesheet)"/>
<xsl:choose>
<xsl:when test="starts-with($vLocal-name, '@')">
<xsl:attribute name=
"{$vNamespace/@prefix}{substring($vLocal-name,2)}"
namespace="{$vNamespace/@url}">
<xsl:value-of select=
"substring(
substring-after(current-group(), current-grouping-key()),
2
)"/>
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{$vNamespace/@prefix}{$vLocal-name}"
namespace="{$vNamespace/@url}">
<xsl:sequence select=
"my:subTree(for $s in current-group()
return
concat('/',substring-after(substring($s, 2),'/'))
)
"/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</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文档(未使用)被施加这种转变中,想要的,正确的结果产生 :
<ns1:create xmlns:ns1="http://predic8.com/wsdl/material/ArticleService/1/">
<ns1:article xmlns:ns1="xmlns:ns1='http://predic8.com/material/1/" type="richtext"
xml:lang="en-us"/>
<ns1:article xmlns:ns1="xmlns:ns1='http://predic8.com/material/1/">
<ns1:id>1</ns1:id>
<description>bar</description>
<name>foo</name>
<ns1:price>
<amount>00.00</amount>
<currency>USD</currency>
</ns1:price>
<ns1:price>
<amount>11.11</amount>
<currency>AUD</currency>
</ns1:price>
</ns1:article>
<ns1:article xmlns:ns1="xmlns:ns1='http://predic8.com/material/1/">
<ns1:id>2</ns1:id>
<description>some name</description>
<name>some description</name>
<ns1:price>
<amount>00.01</amount>
<currency>USD</currency>
</ns1:price>
</ns1:article>
</ns1:create>
说明 :
一个合理的假设是,在整个生成的文档使用相同的任意两个元素local-name()
属于同一个命名空间-这包括主要的大多数现实世界中的XML文档。
命名空间规范遵循的路径规范。 甲nsmespace说明书的形式为: <namespace of="target element's local-name" prefix="wanted prefix" url="namespace-uri"/>
生成与在元素前xsl:element
,适当的名称空间规范使用由创建的索引选择的xsl:key
。 从这个命名空间规范它的值prefix
和url
属性在指定使用xsl:element
指令完整的元素名称和元素的命名空间URI的值。
有趣的问题。 让我们假设你的输入设置的XPath表达式满足一些reasonsable约束,例如,如果有一个X /文章[2]再有也(其前面)的X /条[1]。 让我们把问题的命名空间部分一侧的时刻。
我们去的XSLT 2.0解决方案:我们会在表单的输入开始
<paths>
<path value="1">/create/article[1]/id</path>
<path value="bar">/create/article[1]/description</path>
</paths>
然后我们把它变成
<paths>
<path value="1"><step>create</step><step>article[1]</step><step>id</step></path>
...
</paths>
现在,我们将调用一个函数,它做的第一步分组,并递归调用自己做就下一步分组:
<xsl:function name="f:group">
<xsl:param name="paths" as="element(path)*"/>
<xsl:param name="step" as="xs:integer"/>
<xsl:for-each-group select="$paths" group-by="step[$step]">
<xsl:element name="{replace(current-grouping-key(), '\[.*', '')}">
<xsl:choose>
<xsl:when test="count(current-group) gt 1">
<xsl:sequence select="f:group(current-group(), $step+1)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="current-group()[1]/@value"/>
</xsl:otherwise>
</xsl:choose>
</xsl:element>
</xsl:for-each-group>
</xsl:function>
这是未经测试,并且有可能是你必须调整得到它的工作细节。 但我认为基本的方法应该工作。
问题的命名空间部分也许是最好通过预处理的路径添加一个命名空间属性的每个步骤元件列表解决; 这可随后在xsl中使用:元素指令把元素在正确的命名空间。