cancel
Showing results for 
Search instead for 
Did you mean: 

XSLT Help

Former Member
0 Kudos

Hello experts,

Do you know what XSLT would maintain my message structure with a complete deep copy yet convert a special node of escaped xml string into normal XML format?

I have a scenario where SAP is sending an IDOC with some extra XML in a segment. This XML can vary in structure so I cannot define it inside the PI Data Type. However, PI always escapes this text. In this example file I want "myxmlnode" to be without the &lt ; and instead have <:

I want my XSLT mapping to run after GUI mapping to turn:

<?xml version="1.0" encoding="UTF-8"?>

<mynode1 xmlns="http://asdf"

date="2014-10-28T07:14:40">

  <mynode2 blahblah="asdf2">

  </mynode2>

<myxmlnode>&lt;foo xmlns:xsi=&quot;http://www.w3.org/2001/xmlschema-instance" xmlns:xsd=&quot;http://www.w3.org/2001/xmlschema"><foo2 comment=&quot;blah blah blah&quot; &gt;&lt;/foo&gt;</myxmlnode>

</mynode1>

Into:

<?xml version="1.0" encoding="UTF-8"?>

<mynode1 xmlns="http://asdf"

date="2014-10-28T07:14:40">

  <mynode2 blahblah="asdf2">

  </mynode2>

  <myxmlnode><foo xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns:xsd="http://www.w3.org/2001/xmlschema"><foo2 comment="blah blah blah" ></foo></myxmlnode>

</mynode1>

I'm trying with the disable-output-escaping option but something is not working right:

<?xml version="1.0" encoding="utf-8"?>

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

      <xsl:output method="xml" indent="no" omit-xml-declaration="yes"/>

      <xsl:strip-space elements="*"/>

      <xsl:template match="@* | node()">

        <xsl:copy>

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

        </xsl:copy>

      </xsl:template>

      <xsl:template match="text()">

        <xsl:copy>

            <xsl:value-of select="." disable-output-escaping="yes"/>

        </xsl:copy>

      </xsl:template>

    </xsl:stylesheet>

Many thanks for your advice,

Aaron

Accepted Solutions (1)

Accepted Solutions (1)

iaki_vila
Active Contributor
0 Kudos

Hi Aaron,

You can do doing two XSLs in cascade.

The first one to have all your XML escaped correctly inside one XML tag.

If I have your XML like a XML admitted:


<?xml version="1.0" encoding="UTF-8"?>

<mynode1 xmlns="http://asdf" date="2014-10-28T07:14:40">

    <mynode2 blahblah="asdf2">

  </mynode2>

    <myxmlnode>&lt;foo xmlns:xsi=&quot;http://www.w3.org/2001/xmlschema-instance" xmlns:xsd=&quot;http://www.w3.org/2001/xmlschema"><foo comment=&quot;blah blah blah&quot;/&gt;&lt;/foo&gt;</myxmlnode>

</mynode1>

From here (Converting XML to escaped text in XSLT - Stack Overflow) and with a little changes in the last when:


<?xml version="1.0" encoding="UTF-8"?>

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

    <xsl:output method="xml" encoding="UTF-8" omit-xml-declaration="no" indent="no"/>

    <xsl:template match="/">

        <TestElement>

            <xsl:apply-templates mode="escape"/>

        </TestElement>

    </xsl:template>

    <xsl:template match="*" mode="escape">

        <!-- Begin opening tag -->

        <xsl:text>&lt;</xsl:text>

        <xsl:value-of select="name()"/>

        <!-- Namespaces -->

        <xsl:for-each select="namespace::*">

            <xsl:text> xmlns</xsl:text>

            <xsl:if test="name() != ''">

                <xsl:text>:</xsl:text>

                <xsl:value-of select="name()"/>

            </xsl:if>

            <xsl:text>='</xsl:text>

            <xsl:call-template name="escape-xml">

                <xsl:with-param name="text" select="."/>

            </xsl:call-template>

            <xsl:text>'</xsl:text>

        </xsl:for-each>

        <!-- Attributes -->

        <xsl:for-each select="@*">

            <xsl:text> </xsl:text>

            <xsl:value-of select="name()"/>

            <xsl:text>='</xsl:text>

            <xsl:call-template name="escape-xml">

                <xsl:with-param name="text" select="."/>

            </xsl:call-template>

            <xsl:text>'</xsl:text>

        </xsl:for-each>

        <!-- End opening tag -->

        <xsl:text>&gt;</xsl:text>

        <!-- Content (child elements, text nodes, and PIs) -->

        <xsl:apply-templates select="node()" mode="escape"/>

        <!-- Closing tag -->

        <xsl:text>&lt;/</xsl:text>

        <xsl:value-of select="name()"/>

        <xsl:text>&gt;</xsl:text>

    </xsl:template>

    <xsl:template match="text()" mode="escape">

        <xsl:call-template name="escape-xml">

            <xsl:with-param name="text" select="."/>

        </xsl:call-template>

    </xsl:template>

    <xsl:template match="processing-instruction()" mode="escape">

        <xsl:text>&lt;?</xsl:text>

        <xsl:value-of select="name()"/>

        <xsl:text> </xsl:text>

        <xsl:call-template name="escape-xml">

            <xsl:with-param name="text" select="."/>

        </xsl:call-template>

        <xsl:text>?&gt;</xsl:text>

    </xsl:template>

    <xsl:template name="escape-xml">

        <xsl:param name="text"/>

        <xsl:if test="$text != ''">

            <xsl:variable name="head" select="substring($text, 1, 1)"/>

            <xsl:variable name="tail" select="substring($text, 2)"/>

            <xsl:choose>

                <xsl:when test="$head = '&amp;'">&amp;amp;</xsl:when>

                <xsl:when test="$head = '&lt;'">&lt;</xsl:when>

                <xsl:when test="$head = '&gt;'">&gt;</xsl:when>

                <xsl:when test="$head = '&quot;'">&quot;</xsl:when>

                <xsl:when test="$head = &quot;&apos;&quot;">&amp;apos;</xsl:when>

                <xsl:otherwise>

                    <xsl:value-of select="$head"/>

                </xsl:otherwise>

            </xsl:choose>

            <xsl:call-template name="escape-xml">

                <xsl:with-param name="text" select="$tail"/>

            </xsl:call-template>

        </xsl:if>

    </xsl:template>

</xsl:stylesheet>

You will get this XML:


<?xml version="1.0" encoding="UTF-8"?><TestElement>&lt;mynode1 xmlns='http://asdf' xmlns:xml='http://www.w3.org/XML/1998/namespace' date='2014-10-28T07:14:40'&gt;&lt;mynode2 xmlns='http://asdf' xmlns:xml='http://www.w3.org/XML/1998/namespace' blahblah='asdf2'&gt;

  &lt;/mynode2&gt;&lt;myxmlnode xmlns='http://asdf' xmlns:xml='http://www.w3.org/XML/1998/namespace'><foo xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns:xsd="http://www.w3.org/2001/xmlschema"&gt;&lt;foo comment="blah blah blah"/&gt;&lt;/foo&gt;&lt;/myxmlnode&gt;&lt;/mynode1&gt;</TestElement>

Then you can do now the second XSL:


<?xml version="1.0" encoding="utf-8"?>

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

    <xsl:output method="xml" indent="no" omit-xml-declaration="no"/>

    <xsl:strip-space elements="*"/>

    <xsl:template match="text()|@*">

        <xsl:value-of select="." disable-output-escaping="yes"/>

    </xsl:template>

</xsl:stylesheet>

And you will get this XML:


<?xml version="1.0" encoding="UTF-8"?>

<mynode1 xmlns="http://asdf" xmlns:xml="http://www.w3.org/XML/1998/namespace" date="2014-10-28T07:14:40">

    <mynode2 xmlns="http://asdf" xmlns:xml="http://www.w3.org/XML/1998/namespace" blahblah="asdf2">

  </mynode2>

    <myxmlnode xmlns="http://asdf" xmlns:xml="http://www.w3.org/XML/1998/namespace">

        <foo xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns:xsd="http://www.w3.org/2001/xmlschema">

            <foo comment="blah blah blah"/>

        </foo>

    </myxmlnode>

</mynode1>

Hope this helps.

Regards.

Answers (2)

Answers (2)

Former Member
0 Kudos

Thank you both for the excellent guidance, I will try both methods.

engswee
Active Contributor
0 Kudos

Hi Aaron

If the output has XML special characters in "myxmlnode" that is not in escape mode, wouldn't the output be a non well-formed document?

How is the output used in the target application? Why does it need the content that deviates from well-formed XML syntax?

If you want the special characters to be intact, another possibility is to put it in a CDATA section.

Rgds

Eng Swee

Former Member
0 Kudos

Hi Eng,

Lets assume it will be well formed, I mistakenly left out the closing for </foo2> in that simple example. Fixing that small point I think it is well formed. (just tested at http://www.xmlvalidation.com/)

<?xml version="1.0" encoding="UTF-8"?>

<mynode1 xmlns="http://asdf"

date="2014-10-28T07:14:40">

  <mynode2 blahblah="asdf2">

  </mynode2>

  <myxmlnode><foo xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns:xsd="http://www.w3.org/2001/xmlschema"><foo2 comment="blah blah blah" /></foo></myxmlnode>

</mynode1>

I know it would be better to put this section in base64 encoding or enclose in CDATA, but these are the requirements given to me at the moment.

Any ideas using XSLT or an existing Java bean / file adapter module?

engswee
Active Contributor
0 Kudos

Hi Aaron

Not sure if there is an existing adapter module for this.

My two cents would be to have a Java mapping after the graphical mapping (cos I'm more familiar with Java than XSLT)

Here's the approach for the Java mapping:

i) Convert input Stream to a String

ii) Use replaceAll() method of String to replace all occurrences of the escape sequence to normal XML format, i.e. &gt to >, &lt to <, etc

iii) Convert modified String to output Stream

There are only a handful of special characters, so logic shouldn't be too complex.

Rgds

Eng Swee

Former Member
0 Kudos

Thanks for your suggestion. I don't have any development environment setup for Java mapping so it seems like that would take a long time to get established to do something so small.

engswee
Active Contributor
0 Kudos

Try this nifty trick then

It's not a complex logic, so you might want to consider it if no one else comes back with an alternative.

engswee
Active Contributor
0 Kudos

Try out this piece of code, using the technique I mentioned above


public void transform(TransformationInput input, TransformationOutput output) throws StreamTransformationException {

  try {

   InputStream is = input.getInputPayload().getInputStream();

   StringBuilder sb = new StringBuilder();

   BufferedReader br = new BufferedReader(new InputStreamReader(is)); 

   String lineContent;

   while ((lineContent = br.readLine()) != null) {

    sb.append(lineContent).append("\n");

   }

   br.close();

   String data = sb.toString();

  

   data = data.replaceAll("&lt;", "<");

   data = data.replaceAll("&gt;", ">");

   data = data.replaceAll("&quot;", "\"");

   data = data.replaceAll("&amp;", "&");

   data = data.replaceAll("&apos;", "'");

  

   OutputStream os = output.getOutputPayload().getOutputStream();

   os.write(data.getBytes("UTF-8")); 

  } catch (Exception e) {

   throw new StreamTransformationException("Exception: " + e.getMessage(), e); 

  }

}