简体   繁体   中英

Replace XML tag with text node with XSLT

I've seen similar questions asked many times here, but I'm not sure if they apply to my case or if I simply didn't understand the process.

I have an XML file which contains a list of rules:

<rules>
    <rule id="1">
        <block>
            [HTML content]
        </block>
        <block>
            [HTML content]
        </block>
        <block>
            [HTML content]
        </block>
    </rule>
    <rule id="2">
        <block>
            [HTML content]
        </block>
        <block>
            [HTML content]
        </block>
    </rule>
    [...]
</rules>

Right now my code is something like:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <xsl:template match="/rules/rule">
        [some output to create the header and title]
        <xsl:for-each select="block">
            <xsl:copy-of select="./node()" />
        </xsl:for-each>
        [some output to create the footer]
    </xsl:template>
</xsl:stylesheet>

To be clear, this is not the whole document, but rather the part that is called through AJAX when the user clicks on a rule to see the content and the code is simplified a lot, there are a few decisions that have to be made inside regarding how to display the various block elements, but right now these parts are unnecessary for my problem and only make reading the code more difficult.

In this particular case XSLT may not seem really appropriate, since I'm storing directly HTML content, but this is part of a bigger project where data are much more schematic, this is really the only part where I had to resort to this solution.

However, inside the rule tag there are tables that are numbered using the ID of the rule and a second number, such as 1-1, 1-2, ... Problem is, I have around 20 rules and plan to expand the list in a non-linear way. Right now I'd like to add a rule between rule 1 and rule 2, so I'd have to correct all the following table IDs. This already happened in the past and I'm sure will happen again in the future, so I'd like to avoid this useless extra work and replace the static number with an <id /> tag, such as in Table <id />-12: [...] and replace <id /> with the rule's ID. I tried to modify the code in this way:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <xsl:template match="/rules/rule">
        [some output to create the header and title]
        <xsl:for-each select="block">
            <xsl:copy>
                <xsl:apply-templates select="node()|@*" />
            </xsl:copy>
        </xsl:for-each>
        [some output to create the footer]
    </xsl:template>
    <xsl:template match="id"><xsl:value-of select="ancestor::rule/@id" /></xsl:template>
</xsl:stylesheet>

The substitution works, the only problem is that the <block> tags are not removed, as the previous code did, while every other tag disappears. I just have a single block of text without any HMTL tag.

I wasn't able to come up with this solution, I copied it from questions like this one , but this is the first time I encounter <xsl:copy> and <xsl:apply-templates> tags, so I'm not really sure how they work. Moreover, all the examples I found are similar to the one linked before, where an almost exact copy is wanted. I also have to add a lot of extra code around the main output and I'm not sure how this interacts with the substitution.

Edit : I also tried the variant

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

inside the for-each cycle, but I obtain the same result: text without tags, but the substitution works. It looks like the apply-templates tag removes all tags, but I don't know how to make it preserve them.

Look at the for-each loop in the solution you found:

<xsl:for-each select="block">
    <xsl:copy>
        <xsl:apply-templates select="node()|@*" />
    </xsl:copy>
</xsl:for-each>

In this loop:

  • <xsl:copy> outputs the opening tag, actually <block> .
  • <xsl:apply-templates .../> - "executes" the content of the source tag.
  • </xsl:copy> outputs the closing tag, this time </block> .

As you don't want the <block> tag, just remove <xsl:copy> and </xsl:copy> .

To prevent other tags from disappearing, add an identity template to your script.

The reason is that the built-in template for elements:

  • does not "replicate" opening and closing tags,
  • it just applies templates to their children, including text nodes.

You have to override this including the identity template .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM