XSLT(2.0或3.0)将存储在xml中的每个逗号分隔值的整个xml复制到单独的xml文件中的方法



我有一个XML文件,它是来自web订单系统的工作订单。它包含大量订单数据,其中一个值是由多个文件路径组成的分隔字符串。我想复制完整的XML,并为每个fileURL输出一个XML,并与每个fileURL交换值(每个XML中的单个文件路径)。原因是稍后使用的工作流系统读取文件的路径并拾取它并将xml关联为元数据以进行进一步处理,但是每个文件需要一个xml)。

输入XML(包含存储路径的部分):

<rootNode> 
... 
<properties>
<property>
<name label='fileURL'>fileurl</name>
<value>\nas02OrderO10346_OP176786_X1.pdf, \nas02OrderWeborderO10346_OP176789_X2.pdf, \nas02OrderWeborderO10346_OP176795_X3.pdf, \nas02OrderWeborderO10346_OP176796_X1.pdf,
</value>   
</property>   
</properties> 
</technicalSpec> 
... 
</rootNode>

预期输出将是包含相同数据的每个fileURL的一个xml,除了属性值应该是每个副本的单个fileURL:

<rootNode> 
... 
<properties>
<property>
<name label='fileURL'>fileurl</name>
<value>\nas02OrderO10346_OP176786_X1.pdf
</value>   
</property>   
</properties> 
</technicalSpec> 
... 
</rootNode>

我知道如何将csv字符串转换为变量:

<xsl:variable name="csv" select="//property[name='fileurl']/value"></xsl:variable>

我发现我可以为值做一个for-each循环:

<xsl:for-each select="tokenize($csv, ',')">

我还发现我可以复制整个xml内容:

<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>

,我知道我可以使用"result-document"在for-each循环中创建单独的输出文件。

但是我不知道如何将所有内容组合成一个工作xslt(如果可能的话)来创建每个csv值一个xml。

这个问题是结构相同的问题,将谷歌的XML项目的参数值虽然我会避免将它标记为一个重复的,因为它可能不是显而易见的初学者如何把这个问题的答案,您的需求。

这种方法的本质是:

<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="/">
<xsl:variable name="root" select="/*"/>
<xsl:for-each select="tokenize(//value, ',')!normalize-space()">
<xsl:result-document href="{position()}.xml">
<xsl:apply-templates select="$root">
<xsl:with-param name="current-file" select="."/>
</xsl:apply-templates>
</xsl:result-document>
</xsl:for-each>
</xsl:template>
<xsl:template match="value">
<xsl:param name="current-file"/>
<value>{$current-file}</value>
</xsl:template>

请注意,这取决于内置模板规则以不变的方式复制参数值的事实(它们实际上表现得像隧道参数)。当然,你也可以显式地将其声明为隧道参数。

我将使用隧道参数和xsl:mode,给定XSLT 3:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:output method="xml" indent="yes"/>

<xsl:template match="/">
<xsl:variable name="main-root" select="/"/>
<xsl:for-each select="tokenize(rootNode/technicalSpec/properties/property[name[@label = 'fileURL' and . = 'fileurl']]/value, ',s*')[normalize-space()]">
<xsl:result-document href="{substring-before(., '.pdf')}.xml">
<xsl:apply-templates select="$main-root/*">
<xsl:with-param name="url" select="." tunnel="yes"/>
</xsl:apply-templates>        
</xsl:result-document>
</xsl:for-each>
</xsl:template>

<xsl:template match="property[name[@label = 'fileURL' and . = 'fileurl']]/value">
<xsl:param name="url" tunnel="yes"/>
<xsl:copy>{$url}</xsl:copy>
</xsl:template>
<xsl:mode on-no-match="shallow-copy"/>
</xsl:stylesheet>

这个问题已经有三个答案,它们都做同样的事情:标记value元素,为每个标记创建result-document,并将当前标记作为模板匹配value的参数应用模板。

我建议用另一种方法,我认为这种方法更简单:

XSLT 3.0

<xsl:stylesheet version="3.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
expand-text="yes">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="/">
<xsl:variable name="root" select="."/>
<xsl:analyze-string select="//value" regex="(.+?)($|, )">
<xsl:matching-substring>
<xsl:result-document href="{position()}.xml">
<xsl:apply-templates select="$root/*"/>
</xsl:result-document>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:template>
<xsl:template match="value">
<xsl:copy>{regex-group(1)}</xsl:copy>
</xsl:template>
</xsl:stylesheet>

请注意,我假设了一个正确分隔的字符串,格式为:

<value>\nas02OrderO10346_OP176786_X1.pdf, \nas02OrderWeborderO10346_OP176789_X2.pdf, \nas02OrderWeborderO10346_OP176795_X3.pdf, \nas02OrderWeborderO10346_OP176796_X1.pdf</value>   

演示(模拟):https://xsltfiddle.liberty-development.net/3MP42Pb

实现这一点的一种方法是使用一个由xsl:apply-templates填充并带有mode属性的变量。第一步如您所料,但是要更改结果文档中的一个元素就有点棘手了。

在这种方法中,首先,我用 行创建一个到输入文档的链接
<xsl:variable name="doc" select="/" />

创建一个带有path的文件名的副本-正如您所建议的:

<xsl:variable name="csv" select="//property[name='fileurl']/value" />

这里应用的是xsl:for-each
作为输出文件名,我简单地选择了$csv字符串的当前部分(此迭代的)的最后一部分:

<xsl:variable name="result-name" select="string-join(tokenize(., '\')[position() = last()], '')" />

然后我使用一个变量,它的值由apply-templates填充,并具有提到的mode="new"属性;应用于该模式的模板;其中一个将值更改为xsl:param给出的参数:

<xsl:variable name="new-doc">
<xsl:apply-templates select="$doc" mode="new">
<xsl:with-param name="nam" select="normalize-space(.)" />
</xsl:apply-templates>
</xsl:variable>

现在,执行两个具有mode="new"属性的模板。
最后,用xsl:result-document将变量写入相应的文档:

<xsl:result-document encoding="UTF-8" href="{$result-name}">
<xsl:copy-of select="$new-doc" />
</xsl:result-document>

整个样式表看起来像这样:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="doc" select="/" />
<xsl:variable name="csv" select="//property[name='fileurl']/value" />
<xsl:template match="/">
<xsl:for-each select="tokenize($csv, ',')">
<xsl:variable name="result-name" select="string-join(tokenize(., '\')[position() = last()], '')" />            
<xsl:variable name="new-doc">
<xsl:apply-templates select="$doc" mode="new">
<xsl:with-param name="nam" select="normalize-space(.)" />
</xsl:apply-templates>
</xsl:variable>
<xsl:result-document encoding="UTF-8" href="{$result-name}">
<xsl:copy-of select="$new-doc" />
</xsl:result-document>
</xsl:for-each>
</xsl:template>
<xsl:template match="value" mode="new">
<xsl:param name="nam" />
<value><xsl:value-of select="$nam" /></value>
</xsl:template>
<!-- identity template -->
<xsl:template match="node()|@*" mode="new">
<xsl:param name="nam" />
<xsl:copy>
<xsl:apply-templates select="node()|@*" mode="new">
<xsl:with-param name="nam" select="$nam" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template> 
</xsl:stylesheet>

相关内容

  • 没有找到相关文章

最新更新