英文:
XSL Get all processing instructions between current element and previous element
问题
<?xml version="1.0" encoding="UTF-8"?>
<root>
<data>
<?xml-multiple comment?>
<?format-string comment?>
<comment>hello</comment>
<?format-string comment?>
<comment>goodbye</comment>
<text>bar</text>
<?xml-multiple colors?>
</data>
</root>
英文:
Given the following sample xml:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<data>
<?format-number id?>
<id>123</id>
<?xml-multiple comment?>
<?format-string comment?>
<comment>hello</comment>
<?format-string comment?>
<comment>goodbye</comment>
<text>bar</text>
</data>
</root>
I need to write an XSL that copies a given element, as well as all preceding processing instructions until the previous element (either sibling or parent). The copy of processing instructions should be generic (it doesn't matter what the instructions are, as long as they logically "belong" to the element (i.e. think of them as annotations on the subsequent element, and there may be multiple of them). It also doesn't matter what the previous element is (again, it could be the same element name, a different element name, or a parent element).
An XSL might look something like:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsl:stylesheet xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output byte-order-mark="no" encoding="UTF-8" indent="yes" method="xml"
omit-xml-declaration="yes" />
<xsl:template match="/">
<root>
<xsl:for-each select="/root/data">
<data>
<xsl:for-each select="comment">
<!-- some select usage of preceding-sibling and processing-instruction() ? -->
<xsl:copy-of select="???" />
<xsl:copy-of select="." />
<foo>bar</foo>
</xsl:for-each>
<xsl:for-each select="text">
<xsl:copy-of select="." />
</xsl:for-each>
</data>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
Based on the above, the result should be:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<data>
<?xml-multiple comment?>
<?format-string comment?>
<comment>hello</comment>
<?format-string comment?>
<comment>goodbye</comment>
<text>bar</text>
</data>
</root>
Update
Additional context. I have some existing xsl that was written/tested in a way that targets specific elements as it transforms (i.e. heavy use of for-each select="element"
). That xsl was generated from MapForce (a graphical mapping tool), but doesn't copy over xml processing instructions!. Ideally I could augment this existing xsl in a way that is relatively unobtrusive (i.e. adding some xsl right before each copy-of
inside the for-each
that copies that particular elements processing instructions.
Basically, the trick is to include a snippet that says "inside a for-each
that selects an element, first copy all over the current element's processing instructions (those that come before the current element, but after the previous element), then do whatever logic was already in there, i.e. more copy-of
, etc.
Update 2
After further testing of the key
based answer below, another edge-case popped up in which there are at times "orphaned" processing instructions, i.e. those which don't annotate a following-sibling
element, but come at the end, i.e. <?xml-multiple colors?>
below.
<?xml version="1.0" encoding="UTF-8"?>
<root>
<data>
<?format-number id?>
<id>123</id>
<?xml-multiple comment?>
<?format-string comment?>
<comment>hello</comment>
<?format-string comment?>
<comment>goodbye</comment>
<?format-string text?>
<text>bar</text>
<?xml-multiple colors?>
</data>
</root>
In this case, the result should be
<?xml version="1.0" encoding="UTF-8"?>
<root>
<data>
<?xml-multiple comment?>
<?format-string comment?>
<comment>hello</comment>
<?format-string comment?>
<comment>goodbye</comment>
<text>bar</text>
<?xml-multiple colors?>
</data>
</root>
This is tricky because it doesn't have a following-sibling
, rather, it has no "target" element. Along these lines, I also realized that the processing instructions "data" (i.e. if format-number
is the name, id
is the data) is really the thing that ties it to a target. In this respect, what I really want is to do a copy-of processing-instructions based on the element they target. In other words, I think I want to create the key based on the node referenced in the processing instruction vs the following-sibling.
<xsl:copy-of select="key('pi', generate-id('foo'))" />
would stamp out processing-instructions that were indexed because they were found as:
<?format-number foo?>
<id>123</id>
答案1
得分: 1
我不确定我完全理解你的描述。也许类似这样的东西可以适合你:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/root">
<root>
<xsl:for-each select="data">
<data>
<xsl:for-each-group select="* | processing-instruction()" group-ending-with="*">
<xsl:if test="current-group()[last()][self::comment]">
<xsl:copy-of select="current-group()" />
</xsl:if>
</xsl:for-each-group>
<xsl:copy-of select="text" />
</data>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
英文:
I am not sure I fully follow your description. Perhaps something like this can work for you:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/root">
<root>
<xsl:for-each select="data">
<data>
<xsl:for-each-group select="* | processing-instruction()" group-ending-with="*">
<xsl:if test="current-group()[last()][self::comment]">
<xsl:copy-of select="current-group()" />
</xsl:if>
</xsl:for-each-group>
<xsl:copy-of select="text" />
</data>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
答案2
得分: 1
这里有一种不同的方法可以实现相同的结果,你可能会发现更方便适应你情况的方法:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="pi" match="processing-instruction()" use="generate-id(following-sibling::*[1])" />
<xsl:template match="/root">
<root>
<xsl:for-each select="data">
<data>
<xsl:for-each select="comment">
<xsl:copy-of select="key('pi', generate-id())" />
<xsl:copy-of select="." />
</xsl:for-each>
</data>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
英文:
Here's a different method to achieve the same result, which you may find more convenient to adapt to your situation:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="pi" match="processing-instruction()" use="generate-id(following-sibling::*[1])" />
<xsl:template match="/root">
<root>
<xsl:for-each select="data">
<data>
<xsl:for-each select="comment">
<xsl:copy-of select="key('pi', generate-id())" />
<xsl:copy-of select="." />
</xsl:for-each>
</data>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论