Blaj Bogdan
Blaj Bogdan

Reputation: 455

xml to plain text using xsl without hardcoding

I've transformed an XML to plain text using xsl, I want to output the text of the nodes and attributes of it. I want the xsl to be generic, without containing the name of the node. I managed to do this. But when I have an empty node in xml it will be written in the output. I don't want that. How can I stop writting an empty node or attribute using xsl? I am looking for an implementation where it does not check for the name of a node but for all the nodes.

I have the following XML.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<run xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <tests>
        <test name="a" attr1="" attr2="" attr3="an_value">
            <usecase name="name1">
                <description>Some description</description>
            </usecase>
            <usecase name="name2">
                <description>Descripton1</description>
            </usecase>
            <usecase name="name3">
                <description>Descripton2</description>
            </usecase>
        </test>
    </tests>
    <vip>
        <file name="b">
            <stat wins="1"/>
            <justifications/>
        </file>
        <file name="c">
            <stat wins="2" />
            <justifications/>
        </file>
    </vip>
</run>

I have the following XSL.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" indent="no"/>


  <xsl:strip-space elements="*"/>
    <xsl:template match="node()"> 
<xsl:value-of select="local-name()"/> 
  <xsl:text> </xsl:text>
    <xsl:value-of select="text()" />
        <xsl:call-template name="attributes"/> 
  <xsl:text>
  </xsl:text>
      <xsl:apply-templates select=" node()"/>
  </xsl:template>


  <xsl:template name="attributes">
  <xsl:for-each select="@*">
<xsl:value-of select="local-name()"/> 
  <xsl:text> : </xsl:text>
    <xsl:value-of select="."/>
  <xsl:text> </xsl:text>
  </xsl:for-each>
  </xsl:template>


  </xsl:stylesheet>

Output:

run 
  tests 
  test name : a attr1 :  attr2 :  attr3 : an_value 
  usecase name : name1 
  description Some description

  usecase name : name2 
  description Descripton1

  usecase name : name3 
  description Descripton2

  vip 
  file name : b 
  stat wins : 1 
  justifications 
  file name : c 
  stat wins : 2 
  justifications 

what xsl command I can use to not display the node justifications and the attributes attr1 and attr2 if they are all empty without hardcoding the xsl?

Desired output

run 
  tests 
  test name : a attr3 : an_value 
  usecase name : name1 
  description Some description

  usecase name : name2 
  description Descripton1

  usecase name : name3 
  description Descripton2

  vip 
  file name : b 
  stat wins : 1 

  file name : c 
  stat wins : 2 

EDIT: I updated the xsl to this

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" indent="no"/>
    <xsl:variable name="empty_string" select="''" />

  <xsl:strip-space elements="*"/>
    <xsl:template match="node()"> 
                <xsl:value-of select="local-name()"/> 
                  <xsl:text> </xsl:text>
                    <xsl:value-of select="text()" />
                    <xsl:call-template name="attributes"/> 
                  <xsl:text>
                  </xsl:text>
      <xsl:apply-templates select=" node()"/>
  </xsl:template>


  <xsl:template name="attributes">
  <xsl:for-each select="@*">
            <xsl:if test="normalize-space(.) != $empty_string">
            <xsl:value-of select="local-name()"/> 
              <xsl:text> : </xsl:text>
                <xsl:value-of select="."/>
              <xsl:text> </xsl:text>
       </xsl:if>
  </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

I am still looking on a condition to not write a node if it has no attributes and no childs. In that wasy the justification node will not be displayed.

Upvotes: 1

Views: 645

Answers (2)

michael.hor257k
michael.hor257k

Reputation: 116993

The example is somewhat ambiguous, and the logic that needs to be applied here is not entirely clear. I am guessing you want to do something like:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>

<xsl:template match="*[@*|node()]"> 
    <xsl:value-of select="name()"/> 
    <xsl:text> </xsl:text>
    <xsl:apply-templates select="@*[string()]|text()"/> 
    <xsl:text>&#10;</xsl:text>
    <xsl:apply-templates select="*"/> 
</xsl:template>

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

</xsl:stylesheet>

Personally, I wouldn't use space as a delimiter, because it can easily appear as part of a value (and so can a line feed, actually).

Upvotes: 1

Sebastien
Sebastien

Reputation: 2714

Here's a way you could do it:

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

  <xsl:output method="text"/>

  <xsl:template match="node()">
    <xsl:if test="text() or node() or @*">
      <xsl:text>&#xa;</xsl:text>
      <xsl:value-of select="local-name()"/>
      <xsl:text> </xsl:text>
      <xsl:value-of select="normalize-space(text())"/>
    </xsl:if>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:template>

  <xsl:template match="@*">
    <xsl:if test=".!=''">
      <xsl:value-of select="local-name()"/>
      <xsl:text> : </xsl:text>
      <xsl:value-of select="."/>
      <xsl:text> </xsl:text>
    </xsl:if>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:template>

</xsl:stylesheet>

See it working here: https://xsltfiddle.liberty-development.net/bEzknt1/1

Upvotes: 0

Related Questions