extract cdata using xslt

2019-02-20 08:47发布

Below is the xml that has CDATA section

<?xml version="1.0" encoding="ISO-8859-1"?>
<character>
<name>
<role>Indiana Jones</role>
<actor>Harrison Ford</actor>
<part>protagonist</part>
<![CDATA[  <film>Indiana Jones and the Kingdom of the Crystal Skull</film>]]>
</name>
</character>

For above xml i need to rip off the CDATA and add new element under the existing element "film" , so the final output will be :

<?xml version="1.0" encoding="ISO-8859-1"?>
<character>
<name>
<role>Indiana Jones</role>
<actor>Harrison Ford</actor>
<part>protagonist</part>
<film>Indiana Jones and the Kingdom of the Crystal Skull</film>
<Language>English</Language>
</name>
</character>

Is this can be done using XSLT?

标签: xslt
5条回答
祖国的老花朵
2楼-- · 2019-02-20 09:28

First, the fact that your input XML has "CDATA" is in one sense irrelevant... the XSLT can't tell whether it's CDATA or not. What's key about your input XML is that you have escaped markup <film>...</film>, and you want to turn it into a real element.

If you know that the escaped element will always have a certain name ('film'), and you know where it occurs, you can strip it and replace it easily:

   <xsl:template match="text()[contains(., '&lt;film>')]">
      <film>
         <xsl:value-of select="substring-before(substring-after(., '&lt;film>'),
              '&lt;/film>')"/>
      </film>
   </xsl:template>

If you don't know in advance where the escaped tags will occur and what the element names are, you could use XSLT 2.0's <xsl:analyze-string> to find and replace them. But as Alejandro pointed out, general parsing of XML using regular expressions can get very messy. It would only be feasible if you know the markup will be simple.

查看更多
走好不送
3楼-- · 2019-02-20 09:31

Since the film element in the CDATA block appears to be well-formed, you could use disable-output-escaping. If you match of the name/text(), select value-of with DOE and then insert the Language element immediately following.

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"  />

<!--Identity template simply copies content forward -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>


<xsl:template match="name/text()">
    <!--disable-output-escaping will prevent the "film" element from being escaped.
    Since it appears to be well-formed you should be safe, but no guarentees -->
    <xsl:value-of select="." disable-output-escaping="yes" />
    <Language>English</Language>
</xsl:template>

</xsl:stylesheet>
查看更多
Animai°情兽
4楼-- · 2019-02-20 09:33

A slightly modified identify function should work.

Given this XML:

<?xml version="1.0" encoding="ISO-8859-1"?>
<character>
    <name>
        <role>Indiana Jones</role>
        <actor>Harrison Ford</actor>
        <part>protagonist</part>
        <![CDATA[  <film>Indiana Jones and the Kingdom of the Crystal Skull</film>]]>
    </name>
</character>

Using this XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">

    <xsl:output method="xml" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="*">
        <xsl:copy>
            <xsl:apply-templates select="*" />
            <xsl:value-of select="text()" disable-output-escaping="yes"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

Will produce this output:

<?xml version="1.0" encoding="UTF-8"?>
<character>
   <name>
      <role>Indiana Jones</role>
      <actor>Harrison Ford</actor>
      <part>protagonist</part>
          <film>Indiana Jones and the Kingdom of the Crystal Skull</film>
    </name>
</character>

(Tested using Saxon-HE 9.3.0.5 in oXygen 12.2.)

查看更多
爷的心禁止访问
5楼-- · 2019-02-20 09:36

I was dealing with something similar and I found a good solution so I thought of sharing it with you, but this one is for NSXMLParser.

If you're using NSXMLParser there's a delegate method called foundCDATA which can look like this:

- (void)parser:(NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock{
    if (!parseElement) {
        return;
    }
    if (parsedElementData==nil) {
        parsedElementData = [[NSMutableData alloc] init];
    }
    [parsedElementData appendData:CDATABlock];

    //Grabs the whole content in CDATABlock.
    NSMutableString *content = [[NSMutableString alloc] initWithData:CDATABlock encoding:NSUTF8StringEncoding];

 }

Now add this prewritten class to your project. Then import it to the parser class you want to use it in:

#import NSString_stripHTML

Now simply you can add the following line to foundCDATAmethod:

NSString *strippedContent;
strippedContent = [content strippedHtml];

Now you will have the stripped text without any extra characters. You can substring whatever you want from this stripped text.

查看更多
冷血范
6楼-- · 2019-02-20 09:43

Another way to solve this which would give you some more control over the transformation is to use Andrew Welsh LexEv XMLReader. This gives you the possibility to process CDATA sections as markup among other things.

查看更多
登录 后发表回答