0

I have a mule flow that transforms a XML:

HTTP Listener > Logger > XSLT > Logger

This is the original message:

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope/" xmlns:pref="URI_SOAP_WS">
   <soap:Body>
      <entryXML>
         <note>
            <to>Totest</to>
            <from>Fromtest</from>
            <heading>Query</heading>
            <body>Update Windows 10</body>
         </note>
      </entryXML>
   </soap:Body>
</soap:Envelope>

I want to transform with XSLT to this:

<entryXML>
   &lt;note&gt;
    &lt;to&gt;Totest&lt;/to&gt;
    &lt;from&gt;Fromtest&lt;/from&gt;
    &lt;heading&gt;Query&lt;/heading&gt;
    &lt;body&gt;Update Windows 10&lt;/body&gt;
    &lt;/note&gt;
<entryXML>

I tried with this template:

<xsl:stylesheet version="3.0" xmlns:saxon="http://saxon.sf.net/"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:pref="URI_SOAP_WS">

    <xsl:output method="xml" version="1.0" encoding="UTF-8"
        indent="yes" />
    <xsl:strip-space elements="*" />

    <xsl:template match="/*">
        <xsl:value-of select="serialize(.)" />
    </xsl:template>

</xsl:stylesheet>

But it transform from <entryXML> to </soap:Envolve>.

How can I transform only the content of <entryXML></entryXML>?

gtx911
  • 1,189
  • 4
  • 25
  • 46
  • 1
    Did you review this question and answers? http://stackoverflow.com/questions/6696382/xslt-how-to-convert-xml-node-to-string ... your issue I would believe is that you are using a 1.0 stylesheet and trying to use the serialze() function which is 3.0 – Kevin Brown Feb 23 '16 at 19:13

1 Answers1

1

The following solution is based on the answer by jelovirt that Kevin Brown has linked to. You could use the XSLT 3.0 (XPath 3.0) serialize function or the Saxon extension function that is called saxon:serialize, but the solution below is more portable because it works with XSLT 1.0.

Start with an identity template to copy everything that is not matched by a more specific template. In your example, this would match the outer SOAP elements.

Then match the entryXML element as the starting point of the special serialization mode. Any content inside entryXML will be processed in a mode other than the default, and only templates with this mode can be matched against the input.

XSLT Stylesheet

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

    <xsl:output method="xml" encoding="UTF-8" indent="yes" />

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

    <xsl:template match="entryXML">
        <xsl:copy>
            <xsl:variable name="nodestring">
                <xsl:apply-templates select="@*|node()" mode="serialize"/>
            </xsl:variable>
            <xsl:value-of select="$nodestring"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*" mode="serialize">
        <xsl:text>&lt;</xsl:text>
        <xsl:value-of select="name()"/>
        <xsl:text>&gt;</xsl:text>
        <xsl:apply-templates mode="serialize"/>
        <xsl:text>&lt;/</xsl:text>
        <xsl:value-of select="name()"/>
        <xsl:text>&gt;</xsl:text>
    </xsl:template>

    <xsl:template match="text()" mode="serialize">
        <xsl:value-of select="."/>
    </xsl:template>

</xsl:transform>

XML Output

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope/"
               xmlns:pref="URI_SOAP_WS"
               xmlns:saxon="http://saxon.sf.net/">
   <soap:Body>
      <entryXML>
         &lt;note&gt;
            &lt;to&gt;Tove&lt;/to&gt;
            &lt;from&gt;Jani&lt;/from&gt;
            &lt;heading&gt;Reminder&lt;/heading&gt;
            &lt;body&gt;Don't forget me this weekend!&lt;/body&gt;
         &lt;/note&gt;
      </entryXML>
   </soap:Body>
</soap:Envelope>

EDIT 1

The approach above does not serialize attributes in the message, if there are any. If you also need to preserve attributes, e.g. in a message like

<entryXML>
     <note>
        <to with="love">Tove</to>
            ^^^^^^^^^^^               attribute

you would need to add a template along the lines of

<xsl:template match="@*" mode="serialize">
    <xsl:text> </xsl:text>
    <xsl:value-of select="name()"/>
    <xsl:text>="</xsl:text>
    <xsl:value-of select="."/>
    <xsl:text>"</xsl:text>
</xsl:template>

and also change the template that matches * in the serialize mode:

<xsl:template match="*" mode="serialize">
  <xsl:text>&lt;</xsl:text>
  <xsl:value-of select="name()"/>
  <xsl:apply-templates select="@*" mode="serialize"/>
  <xsl:text>&gt;</xsl:text>
  <xsl:apply-templates mode="serialize"/>
  <xsl:text>&lt;/</xsl:text>
  <xsl:value-of select="name()"/>
  <xsl:text>&gt;</xsl:text>
</xsl:template>

EDIT 2

Caution: The solution above is XSLT 1.0 only, but it also has shortcomings and there is no guarantee that it will serialize correctly in every possible case.

It works in a simple case like yours, but robust, general serialization "requires more effort", as Martin Honnen has put it. See e.g. Evan Lenz's stylesheet for a more sophisticated approach.

Alternatively, you can modify your original XSLT 3.0 stylesheet to (borrowing some of the ideas above)

<xsl:stylesheet version="3.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="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="entryXML/*">
            <xsl:value-of select="serialize(.)" />
    </xsl:template>

</xsl:stylesheet>

which will also lead to a more reliable serialization.

Community
  • 1
  • 1
Mathias Müller
  • 22,203
  • 13
  • 58
  • 75
  • Thanks, it works fine ! But I don't understand the last answer. About, the XML message, the information was different, I've just edited to prevent confusions. – gtx911 Feb 23 '16 at 20:27
  • @mikedev What do you mean by the last answer? My addition about attributes? – Mathias Müller Feb 23 '16 at 20:28
  • While obviously an XSLT 1.0 solution is nice, however, I think you should really point out the shortcomings of the few templates, like lack of proper escaping of characters like the ampersand or the less than sign, for instance, to start with. I am afraid, proper XML serialization requires more effort, see http://lenzconsulting.com/xml-to-string/. – Martin Honnen Feb 23 '16 at 20:49
  • @MartinHonnen I will add a note about that, too. Thanks for pointing it out. – Mathias Müller Feb 23 '16 at 20:51
  • @MartinHonnen I have edited my answer. Please let me know if you agree. – Mathias Müller Feb 23 '16 at 21:05