0

I am working with some XML inside of XSLT. I want to save a section of it in a content node. I left the CDATA node out of this example.

When I grab the xml like this it's escaped ie. &lt; = <

<content name="test”>
      <xsl:copy-of select="//content[@name='something']/node()" />
</content>

But I need to do some processing on the data before I store it in a content node. I have an xsl:for-each call and it loops saving sections. However, when I call a similar command I can't get the XML to escape.

<xsl:for-each select="exsl:node-set($xml)//data">
    <content name="test">
              <xsl:copy-of select="./node()" />
    </content>

I've put CDATA nodes around it and outputted the content, but then I have issues in the system with double escaping. I really need this copy-of call to output escaped XML.

I really want something like:

<content name="test">
          &lt;data&gt;Some data&lt;\data&gt;
        </content>

Input would be something like this:

<root>
<data>Some data</data>
<data>more data</data>
</root>

This a simplification of the data. There would be additional xml nodes in the data node.

Joshua Hedges
  • 307
  • 4
  • 16
  • When it comes to CDATA and "escaped XML" is better to show input/stylesheet/desired-output because people tend to refer to very different things in this topic. – Alejandro Mar 22 '19 at 15:49
  • updated. I really just want the special characters escaped. <>" – Joshua Hedges Mar 22 '19 at 15:55
  • What's your input ? – Vebbie Mar 22 '19 at 15:59
  • It looks like your are stringifying a document fragment to be consumed by another application that doesn't understand XML. Without the input source it's dificult to tell. My advise is to remove the offending application from the chain. – Alejandro Mar 22 '19 at 16:10
  • I put in some mock input. I think it has something to do with the call to exsl:node-set($xml) – Joshua Hedges Mar 22 '19 at 16:14
  • Well, the fragment of the stylesheet where the `$xml` variable is declared may be important. – Alejandro Mar 22 '19 at 16:19
  • So which XSLT version, which XSLT processor do you use? XSLT 3 with XPath 3 supports the `serialize` function which can serialize XML nodes to strings e.g. ``, see https://xsltfiddle.liberty-development.net/pPzifpn. – Martin Honnen Mar 22 '19 at 16:19
  • Martin is on to something. I think it's an issue with serialization. Unfortunately, I'm in like XSLT 1.0 and I can't change that. I guess I have to figure out how to call a custom serialization function. – Joshua Hedges Mar 22 '19 at 16:29
  • So which XSLT 1 processor exactly do you use? – Martin Honnen Mar 22 '19 at 16:37
  • It's a version of 1.0 but it's really hard for me to tell because it's embedded in a software suite. – Joshua Hedges Mar 22 '19 at 17:30
  • @JoshuaHedges How to find out: https://stackoverflow.com/questions/25244370/how-can-i-check-which-xslt-processor-is-being-used-in-solr/25245033#25245033 – michael.hor257k Mar 22 '19 at 19:56
  • Thanks Mike, it's libxslt 1.0 – Joshua Hedges Mar 22 '19 at 20:06

2 Answers2

3

Evan Lenz has an XSLT 1 based implementation of XML serialization at http://lenzconsulting.com/xml-to-string/: if you use that you have e.g.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

  <xsl:import href="http://lenzconsulting.com/xml-to-string/xml-to-string.xsl"/>

  <xsl:output indent="yes" cdata-section-elements="content"/>

  <xsl:template match="/">
      <xsl:apply-templates/>
  </xsl:template>

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

  <xsl:template match="data">
      <content>
          <xsl:apply-templates select="." mode="xml-to-string"/>
      </content>      
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/pPzifpn/4

If the processor is libxslt then it might allow you to implement an extension function, for instance PHP allows you to call PHP functions and the DOMDocument in PHP has a saveXML function to serialize, so you can call that from XSLT:

<?php

function serializeNode($node) {
  return $node[0]->ownerDocument->saveXML($node[0]);
}

$xml = <<<EOT
<root>
<data>Some data</data>
<data>more data</data>
</root>
EOT;

$xsl = <<<EOT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:php="http://php.net/xsl"
    exclude-result-prefixes="php"
    version="1.0">

  <xsl:output indent="yes" cdata-section-elements="content"/>

  <xsl:template match="/">
      <xsl:apply-templates/>
  </xsl:template>

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

  <xsl:template match="data">
      <content>
          <xsl:value-of select="php:function('serializeNode', .)"/>
      </content>      
  </xsl:template>

</xsl:stylesheet>
EOT;

$doc = new DOMDocument();
$doc->loadXML($xml);

$proc = new XSLTProcessor();
$proc->registerPHPFunctions('serializeNode');

$sheet = new DOMDocument();
$sheet->loadXML($xsl);

$proc->importStylesheet($sheet);

echo $proc->transformToXml($doc);

?>
Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
  • I appreciate the answer but with my requirements it's complicated for me to use code from a 3rd party. – Joshua Hedges Mar 22 '19 at 18:11
  • 1
    Using code from an answer of StackOverflow would not be using code from a 3rd party? Anyway, try to find out whether the processor/enviroment has any extension function to solve that or easily allows you to write one that performs the serialization. – Martin Honnen Mar 22 '19 at 19:03
  • From Evan Lenz copyright notice: `Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:...` keep, reproduce the notice and don't make claims about Evan endorsement – Alejandro Mar 22 '19 at 19:22
0
<!-- XSL 1.0 -->
<xsl:template match="*|@*|text()|comment()" mode="copy">
    <xsl:param name="_-"><!--<">-do-not-modify-this--></xsl:param>
    <xsl:variable name="ch" select="document('')//*[@name='_-']/comment()"/>
    <xsl:variable name="lt" select="substring($ch, 1, 1)"/>
    <xsl:variable name="qq" select="substring($ch, 2, 1)"/>
    <xsl:variable name="gt" select="substring($ch, 3, 1)"/>
    <xsl:choose>
        <xsl:when test="self::*">
            <xsl:value-of select="concat($lt, name())"/>
            <xsl:apply-templates select="@*" mode="copy"/>
            <xsl:variable name="ns" select="namespace-uri()"/>
            <xsl:if test="$ns and not(ancestor::*[namespace-uri() = $ns])">
                <xsl:variable name="pf" select="substring-before(name(), ':')"/>
                <xsl:value-of select="concat(' xmlns:', $pf, '=', $qq, $ns, $qq)"/>
            </xsl:if>
            <xsl:value-of select="$gt"/>
            <xsl:apply-templates select="*|text()|comment()" mode="copy"/>
            <xsl:value-of select="concat($lt, '/', name(), $gt)"/>
        </xsl:when>
        <xsl:when test="self::comment()">
            <xsl:value-of select="concat($lt, '!--', ., '--', $gt)"/>
        </xsl:when>
        <xsl:when test="self::text()"><xsl:value-of select="."/></xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="concat(' ', name(), '=', $qq, ., $qq)"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>
Anton
  • 1