以微弱的方式编辑澄清。系统通过XSL处理器将XML转换为另一种XML。
我正在为具有插件卡的机器做一些XSL规则。这些卡将向主要主机提供他们自己的XSL规则,然后主要将这些规则合并为一个超级XSL文件以便自己处理,并且还将其发送到已经HTTP到机器上的Web浏览器。浏览器用于设置机器的配置项,XSL用于更改和/或隐藏和/或取消隐藏某些XML。为了简化我的查询,我发明了一个小例子。
当用户将项目配置为B类型(可选择A,B,C等几种类型)时,接下来的两个项目也将被更改(以某种方式)。在真实交易中,有一个属性“hidden”设置为true或false,以及设置的子元素。真实事物中的下两个项目将被隐藏,并且子元素也会被更改。
当用户将项目从类型B更改为类型A时,我需要确定哪些其他节点需要将其“隐藏”属性设置为false。用户将根据需要更改子元素。
所有这些都是“循环”,并且是有序的,因此如果一个节点设置为B类,并且它是最后一个节点(没有后续兄弟节点),则受影响的节点是该集合中的第一个节点。 (在XPath术语中,如果node [4]是B类,则节点[1]和[2]将被隐藏和更改)。
所以,对于我的例子,我有一个输入XML:
<topline>
<midline type="A" name="mid1" hidden="false"><source name="off"/></midline>
<midline type="B" name="mid2" hidden="false"><source name="input 1"/></midline>
<midline type="A" name="mid3" hidden="false"><source name="off"/></midline>
<midline type="A" name="mid4" hidden="false"><source name="off"/></midline>
</topline>
并且XSL会将其更改为:
<topline>
<midline type="A" name="mid1" hidden="false"><source name="off"/></midline>
<midline type="B" name="mid2" hidden="false"><source name="input 1"/></midline>
<midline type="A" name="mid3" hidden="true"><source name="input 1"/></midline>
<midline type="A" name="mid4" hidden="true"><source name="input 1"/></midline>
</topline>
现在,如果用户改变主意,并将mid2更改为类型A:
<topline>
<midline type="A" name="mid1" hidden="false"><source name="off"/></midline>
<midline type="A" name="mid2" hidden="false"><source name="input 1"/></midline>
<midline type="A" name="mid3" hidden="true"><source name="input 1"/></midline>
<midline type="A" name="mid4" hidden="true"><source name="input 1"/></midline>
</topline>
然后XSL将取消隐藏在mid2的兄弟之后的循环,因此结果应为:
<topline>
<midline type="A" name="mid1" hidden="false"><source name="off"/></midline>
<midline type="A" name="mid2" hidden="false"><source name="input 1"/></midline>
<midline type="A" name="mid3" hidden="false"><source name="input 1"/></midline>
<midline type="A" name="mid4" hidden="false"><source name="input 1"/></midline>
</topline>
这是我正在努力的第二步。我所做的解决方法是,对我来说,相当难看,但也许是不可避免的,因为我想要达到的目标并不是XSL友好。
我做了什么:
<xsl:for-each select="topline">
<xsl:for-each select="midline">
<xsl:variable name="masterPosition" select="position()"/>
<xsl:choose>
<xsl:when test="@type='B'>
hide the next two nodes. This is easy:
translate($masterPosition, '1234', '2341')
works nicely.
</xsl:when>
<xsl:otherwise>
<xsl:variable name="prior1" select="translate(masterPosition, '1234', '4123')"/>
<xsl:variable name="test1" select="../midline[$prior1]/source[1]/@name='off'"/>
this second line doesn't work: I only get the first node, always.
So instead I have
<xsl:variable name="test2">
<xsl:choice>
<xsl:when test="$masterPosition='1'"><xsl:value-of select="../midline[4]/source"/></xsl:when>
and so on for the other masterPositions
</xsl:choice>
</xsl:variable>
and this is repeated for a few other variables, one each for each relevant
prior position and for the fields I need to change. I then use these
variables to change the XML - there's something in the main machine's
processing to enable this, I believe it is non-standard, so please ignore:
(At least it doesn't run against Xalan).
<set key={$test2}/@hidden, value="false")/>
</xsl:otherwise>
</xsl:choose>
<xsl:for-each>
</xsl:for-each>
有没有更优雅的方式来做到这一点你能想到的?如果没有,请不要担心,我相信我的黑客会工作,也不应该消耗太多的MIPS。
我无法使用<xsl:key>
,因为我们的系统没有应对:我们有多个XSL源被合并为一个,而且一个贪婪的脚本(我的控制之外)根本无法理解{{1如果有使用密钥的解决方案,我就无法使用它。
由于 理查德
答案 0 :(得分:1)
根据模板匹配,这是一种无需键的方法:
<!-- start with the identity transform -->
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*" />
</xsl:copy>
</xsl:template>
<!-- <midline>s that have two preceding siblings -->
<xsl:template match="midline[@type='A'][count(preceding-sibling::midline) > 1]">
<xsl:apply-templates select="." mode="source">
<xsl:with-param name="circular" select="
preceding-sibling::midline[position() <= 2]
" />
</xsl:apply-templates>
</xsl:template>
<!-- <midline>s that have one preceding sibling -->
<xsl:template match="midline[@type='A'][count(preceding-sibling::midline) = 1]">
<xsl:apply-templates select="." mode="source">
<xsl:with-param name="circular" select="
(preceding-sibling::midline|../midline[last()])
" />
</xsl:apply-templates>
</xsl:template>
<!-- <midline>s that have no preceding sibling -->
<xsl:template match="midline[@type='A'][count(preceding-sibling::midline) = 0]">
<xsl:apply-templates select="." mode="source">
<xsl:with-param name="circular" select="
../midline[position() > last() - 2]
" />
</xsl:apply-templates>
</xsl:template>
<!-- this template creates a new node with the wanted source -->
<xsl:template match="midline" mode="source">
<xsl:param name="circular" select="." />
<xsl:variable name="prevB" select="$circular[@type = 'B' and not(@mode = off)]" />
<xsl:copy>
<xsl:copy-of select="@*" />
<xsl:attribute name="hidden">
<xsl:value-of select="boolean($prevB)" />
</xsl:attribute>
<xsl:choose>
<xsl:when test="$prevB">
<xsl:copy-of select="$prevB/source" />
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="source" />
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
对于您的样本输入,输出将是:
<topline>
<midline type="A" name="mid1" hidden="false"><source name="off"/></midline>
<midline type="B" name="mid2" hidden="false"><source name="input 1"/></midline>
<midline type="A" name="mid3" hidden="true"><source name="input 1"/></midline>
<midline type="A" name="mid4" hidden="true"><source name="input 1"/></midline>
</topline>
请注意,您的描述与您的示例输出相矛盾 - 这是严格遵循您的描述时所获得的,除非我误解了您。
<xsl:with-param>
用于传递两个节点,即“循环前兄弟节点”(或@type=B'
个节点的空节点集)。
模板<xsl:template match="midline" mode="source">
使用该参数:如果它在那里,它会检查您的情况(即其中一个是@type='B'
而不是@mode='off'
)。
如果此类节点存在,则会复制其<source>
,否则会复制原始<source>
。
答案 1 :(得分:1)
要进行逆向转换,您需要:
将此转换应用于以下(提供的)XML,其中用户刚刚将type='B'
更改为type='A'
:
<topline>
<midline type="A" name="mid1" hidden="false">
<source name="off"/>
</midline>
<midline type="A" name="mid2" hidden="false">
<source name="input 1"/>
</midline>
<midline type="A" name="mid3" hidden="true">
<source name="input 1"/>
</midline>
<midline type="A" name="mid4" hidden="true">
<source name="input 1"/>
</midline>
</topline>
生成了想要的,正确的结果(为前B类型的下两个圆形兄弟姐妹设置了hidden="false"
):
<topline>
<midline type="A" name="mid1" hidden="false">
<source name="off"/>
</midline>
<midline type="A" name="mid2" hidden="false">
<source name="input 1"/>
</midline>
<midline type="A" name="mid3" hidden="false">
<source name="input 1"/>
</midline>
<midline type="A" name="mid4" hidden="false">
<source name="input 1"/>
</midline>
</topline>
使用此源XML文件(真正的循环兄弟):
<topline>
<midline type="A" name="mid1" hidden="true">
<source name="off"/>
</midline>
<midline type="A" name="mid2" hidden="false">
<source name="input 1"/>
</midline>
<midline type="A" name="mid3" hidden="false">
<source name="input 1"/>
</midline>
<midline type="A" name="mid4" hidden="true">
<source name="input 1"/>
</midline>
</topline>
再次生成想要的正确结果。
请注意:使用XPath mod
运算符来确定圆形兄弟位置。