Mediazombie
Mediazombie

Reputation: 42

Transform each child node in a node

I think I am too stupid or whatever... but... how it is possible to transform a child node in a parent node if exist?

I have a xml file that looks like the following...

    <?xml version="1.0"?>
    <?xml-stylesheet href="template.xsl" type="text/xsl" ?>
    <MainChapter RSKM-Language="" EDD-Version="" xmlns:fm="fctfmns.xml">
      <Chapter>
        <Bar/>
        <Head UniqueID="CHDFFEJA">Sicherheit und Gewährleistung</Head>
        <Chapter>
          <Bar/>
          <Head>
            Erklärung der Sicherheitshinweise<MarkIdx fm:MarkerType="Index">Sicherheitshinweise</MarkIdx>
          </Head>
          <Para>
            <BodyText>Alle Sicherheitshinweise in dieser Betriebsanleitung weisen den Bediener frühzeitig auf mögliche Gefahren für Leib und Leben, auf mögliche Schäden an der Maschine und an Beistellgeräten, sowie auf fehlerfreies, produktives Arbeiten hin.</BodyText>
            <BodyText>Alle für den Bediener besonders wichtigen Stellen sind mit einem Warnzeichen versehen. Diese Informationen sind vom Bediener unbedingt zu lesen und zu befolgen.</BodyText>
          </Para>
          <Para>
            <Head>Bedeutungen:</Head>
            <Warning Class="Warning">
              <Warningtable fm:NumRows="2" fm:NumCols="2" fm:ColWidths="5201588,22571176" fm:TblFormat="Warning_Warning">
                <WarningHead fm:NumRows="1">
                  <WarningHeadRow fm:MaxHeight="66060288" fm:MinHeight="0">
                    <Signalword fm:NumCols="2" fm:ColRotation="0">
                      <Graphic>
                        <fm:Frame Type="1" Align="4" Offset="0" BaseLine="374744" Cropped="0" HasImgGroups="0" Pos="18318744,36680121,1204098,1075727,0" Style="15,15,65536,0" Color="Schwarz" Common="0,0,0,2,0,393216,6553600," GroupParent="0">
                          <fm:Inset File="I:\D-140027\D-140027\illustrationen\WARNUNG.eps" Dpi="0" Fixed="1" Flipped="0" Pos="58008,50622,1121246,995520,0" Style="15,15,65536,0" Color="Schwarz" Common="0,0,0,2,2,393216,6553600," GroupParent="0"/>
                        </fm:Frame>
                      </Graphic>WARNUNG
                    </Signalword>
                  </WarningHeadRow>
                </WarningHead>
                <WarningBody fm:NumRows="1">
                  <WarningBodyRow fm:MaxHeight="66060288" fm:MinHeight="0">
                    <WarningSymbol fm:LFTop="" fm:ColRotation="0">
                      <Graphic>
                        <fm:Frame Type="1" Align="4" Offset="0" BaseLine="1579877" Cropped="0" HasImgGroups="0" Pos="7066500,38123845,4629870,2523590,0" Style="15,15,65536,0" Color="Schwarz" Common="0,0,0,2,0,393216,6553600," GroupParent="0">
                          <fm:Inset File="I:\D-140027\D-140027\illustrationen\warning_y_electricity.eps" Dpi="0" Fixed="1" Flipped="0" Pos="980468,0,2668934,2348803,0" Style="15,7,65536,0" Color="Schwarz" Common="0,0,0,2,2,393216,6553600," GroupParent="0"/>
                        </fm:Frame>
                      </Graphic>
                    </WarningSymbol>
                    <WarningMessage fm:LFLeft="" fm:LFTop="" fm:ColRotation="0">
                      <BodyText>Elektrische Spannung! Lebensgefahr durch Stromschlag! Arbeiten an der Maschine, bei denen ein Abschalten des elektrischen Stroms in der Betriebsanleitung zwingend vorgeschrieben ist, muss:</BodyText>
                      <List>
                        <ListEntry>
                          <BodyText>entweder die Stromzuführung durch Abklemmen vom Netz oder Ziehen des Steckers unterbrochen werden,</BodyText>
                        </ListEntry>
                      </List>
                      <BodyText>oder:</BodyText>
                      <List>
                        <ListEntry>
                          <BodyText>der Hauptschalter gegen unbeabsichtigtes Einschalten gesichert werden. Dies kann mit einem Vorhängeschloss oder durch Anbringen einer Plombe am Schalter erfolgen.</BodyText>
                        </ListEntry>
                      </List>
                    </WarningMessage>
                  </WarningBodyRow>
                </WarningBody>
              </Warningtable>
            </Warning>
        </Para>
     </Chapter>
  </Chapter>
</MainChapter>

I am looking for a solution to loop trough each BodyText in a WarningMessage and if present, also through each ListEntry if a List node is present in the WarningMessage as a child node, together with the BodyText's.

A BodyText is given in each WarningMessage, but a List is only sometimes given and can have in itself some ListEntry's with one or more BodyText's.

I have tryed something till now, but can not find a working solution for this issue.

The following solution does not what it should... I get only each BodyText in a WarningMessage, but it ignores the List elements. I am trying to write a xsl:template solution who is able to transform the whole xml sentence to valid html/css.

Here is a picture of what I get and what I expect to get: http://i59.tinypic.com/33ma0qo.png

  <xsl:template match="//WarningMessage/List" name="WarnList">
    <ul>
      <xsl:for-each select="ListEntry">
        <li>
          <xsl:for-each select="BodyText">
            <xsl:value-of select="."/>
          </xsl:for-each>
        </li>
      </xsl:for-each>
    </ul>
  </xsl:template>

  <!-- Warnungen vom Typ "Danger" -->
  <xsl:template match="Warning[contains(concat(' ', @Class, ' '),' Danger ')]">
    <div class="alert alert-danger" role="alert">
      <h4><span class="glyphicon glyphicon-warning-sign"/><stong>
        <xsl:value-of select="Warningtable/WarningHead/WarningHeadRow/Signalword"/>
      </stong></h4>
        <xsl:for-each select="Warningtable/WarningBody/WarningBodyRow/WarningMessage/BodyText">
          <p>
            <xsl:value-of select="."/>
            <xsl:call-template name="WarnList"/>
          </p>
        </xsl:for-each>
    </div>
  </xsl:template>

Has anybody a hint how to write the xsl:template to get each BodyText and if present, also each List with each ListEntry? For clarification: it is possible that one WarningMessage includes one or more BodyText, List and ListEntry.

I hope it is now clear what I want to reach... because it is a bit difficult for me to describe my issue.

If it is important for clarification, here is my whole xslt file... but I only need a solution to handle the List's and ListEntrys in the WarningMessage.

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

  <xsl:output method="html" doctype-system="about:legacy-compat" />

  <xsl:template match="/">
    <html>
      <head>
        <meta charset="utf-8" />
        <title>Testausgabe</title>
        <!-- Bootstrap -->
        <link href="css/bootstrap.min.css" rel="stylesheet"></link>
        <!-- Individual Theme CSS -->
        <link href="css/theme.css" rel="stylesheet"></link>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
        <script src="js/jquery.easing.js"></script>
      </head>
      <body>
        <div class="topwrapper"></div>

        <div class="wrapper">
          <div class="container">
            <div class="row">

              <xsl:apply-templates />

            </div><!-- end row -->
          </div><!-- end container -->
        </div><!-- end wrapper -->
      <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
      <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
      <!-- Include all compiled plugins (below), or include individual files as needed -->
      <script src="js/bootstrap.min.js"></script>

        <footer class="footer">
        <div class="container">
          <div class="row">
            <img src="images/vc999_logo_94x40.png"></img>
          </div>
        </div>
        </footer>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="MarkIdx" mode="non-MarkIdx"/>

  <!-- Header-Bars formatieren -->
  <xsl:template match="Bar">
    <hr/>
  </xsl:template>

  <!-- Hauptüberschrift [h1] formatieren -->
  <xsl:template match="MainChapter/Chapter/Head">
    <h1>
      <xsl:value-of select="."/>
    </h1>
  </xsl:template>

  <!-- Überschrift Ebene 2 [h2] formatieren -->
  <xsl:template match="MainChapter/Chapter/Chapter/Head">
    <h2>
      <xsl:apply-templates select="." mode="non-MarkIdx"/>
    </h2>
  </xsl:template>

  <!-- Überschrift Ebene 3 [h3] formatieren -->
  <xsl:template match="//Para/Head | MainChapter/Chapter/Chapter/Chapter/Head">
    <h3>
      <xsl:apply-templates select="." mode="non-MarkIdx"/>
    </h3>
  </xsl:template>

  <!-- Alle BodyText's direkt innerhalb eines Para formatieren -->
  <xsl:template match="//Para/BodyText">
    <p>
      <xsl:value-of select="."/>
    </p>
  </xsl:template>

  <!-- Listenelemente direkt innerhalb eines Para formatieren -->
  <xsl:template match="//Para/List">
    <ul class="list-group">
      <xsl:for-each select="ListEntry">
        <xsl:choose>
          <xsl:when test="position() mod 2 = 0">
            <li class="list-group-item">
              <xsl:value-of select="BodyText"/>
            </li>
          </xsl:when>
          <xsl:otherwise>
            <li class="list-group-item list-group-item-grey">
              <xsl:value-of select="BodyText"/>
            </li>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:for-each>
    </ul>
  </xsl:template>

  <!-- Tabellen direkt innerhalb eines Para formatieren -->
  <xsl:template match="//Para/Table">
    <xsl:for-each select="InvisibleTable/TBody">
      <table class="table table-striped">
        <xsl:for-each select="TRow">
          <tr>
            <xsl:for-each select="TCell">
              <td>
                <xsl:for-each select="BodyText">
                  <span>
                    <xsl:value-of select="current()"/>
                  </span>
                  <br/>
                </xsl:for-each>
              </td>
            </xsl:for-each>
          </tr>
        </xsl:for-each>
      </table>
    </xsl:for-each>
  </xsl:template>

  <!-- Warnschilder direkt innerhalb eines Para formatieren -->
  <!-- List's innerhalb einer Warning -->
  <xsl:template match="//WarningMessage/List" name="WarnList">
    <ul>
      <xsl:for-each select="ListEntry">
        <li>
          <xsl:for-each select="BodyText">
            <xsl:value-of select="."/>
          </xsl:for-each>
        </li>
      </xsl:for-each>
    </ul>
  </xsl:template>

  <!-- Warnungen vom Typ "Danger" -->
  <xsl:template match="Warning[contains(concat(' ', @Class, ' '),' Danger ')]">
    <div class="alert alert-danger" role="alert">
      <h4><span class="glyphicon glyphicon-warning-sign"/><stong>
        <xsl:value-of select="Warningtable/WarningHead/WarningHeadRow/Signalword"/>
      </stong></h4>
        <xsl:for-each select="Warningtable/WarningBody/WarningBodyRow/WarningMessage/BodyText">
          <p>
            <xsl:value-of select="."/>
            <xsl:call-template name="WarnList"/>
          </p>
        </xsl:for-each>
    </div>
  </xsl:template>

  <!-- Warnungen vom Typ "Warning" -->
  <xsl:template match="Warning[contains(concat(' ', @Class, ' '),' Warning ')]">
    <div class="alert alert-warning" role="alert">
      <h4>
        <span class="glyphicon glyphicon-warning-sign"/>
        <stong>
          <xsl:value-of select="Warningtable/WarningHead/WarningHeadRow/Signalword"/>
        </stong>
      </h4>
        <xsl:for-each select="Warningtable/WarningBody/WarningBodyRow/WarningMessage/BodyText">
          <p>
            <xsl:value-of select="."/>
          </p>
        </xsl:for-each>
    </div>
  </xsl:template>

  <!-- Warnungen vom Typ "Caution" -->
  <xsl:template match="//Para/Warning[contains(concat(' ', @Class, ' '),' Caution ')]">
    <div class="alert alert-caution" role="alert">
      <h4>
        <span class="glyphicon glyphicon-warning-sign"/>
        <stong>
          <xsl:value-of select="Warningtable/WarningHead/WarningHeadRow/Signalword"/>
        </stong>
      </h4>
      <xsl:for-each select="Warningtable/WarningBody/WarningBodyRow/WarningMessage/BodyText">
        <p>
          <xsl:value-of select="."/>
        </p>
      </xsl:for-each>
    </div>
  </xsl:template>

  <!-- Infoschilder direkt innerhalb eines Para formatieren -->
  <xsl:template match="//Para/Info">
    <div class="alert alert-info" role="alert">
      <h4>
        <span class="glyphicon glyphicon-info-sign"/>
      </h4>
      <xsl:value-of select="./InfoGroup/InfoBody/InfoRow/InfoCell/BodyText"/>
    </div>
  </xsl:template>

</xsl:stylesheet>

Upvotes: 0

Views: 372

Answers (1)

Mathias M&#252;ller
Mathias M&#252;ller

Reputation: 22647

Simply change this line:

<xsl:for-each select="Warningtable/WarningBody/WarningBodyRow/WarningMessage/BodyText">

to:

<xsl:for-each select="Warningtable/WarningBody/WarningBodyRow/WarningMessage//BodyText">

The only change is the // before BodyText. Why? If you look at your input XML, only one BodyText element is an immediate descendant of WarningMessage. The others are inside List and ListEntry elements. Whereas / as a separator means going down the "child::" axis, // means finding all descendant BodyText elements.

Then, the relevant part of the output will be:

<p>
  Elektrische Spannung! Lebensgefahr durch Stromschlag! Arbeiten an der Maschine, bei denen ein Abschalten des elektrischen Stroms in der Betriebsanleitung zwingend vorgeschrieben ist, muss:
</p>
<p>entweder die Stromzuführung durch Abklemmen vom Netz oder Ziehen des Steckers unterbrochen werden,
</p>
<p>oder:</p>
<p>der Hauptschalter gegen unbeabsichtigtes Einschalten gesichert werden. Dies kann mit einem Vorhängeschloss oder durch Anbringen einer Plombe am Schalter erfolgen.
</p>

Please note:

  • For your next question, take the time to post an English XML sample, not a German one. If you expect others to help you, do not put obstacles in their way.
  • The fact that your code now produces reasonable output does not mean the code is reasonable. My answer only fixes the symptoms rather than curing the disease, if you will. For example, template matches generally should not start with // and path expressions should not be overly long.

EDIT (as a response to your comment)

Is it also possible to transform the ´List´ elements to ´ul´ tags and the
´ListEntry´ elements to ´li´ tags and display the ´BodyText´ between the ´<li>´
and ´</li>´ tag?

Yes, that's possible. But I recommend you restructure your code a bit. Start with the template that matches Warning elements:

<xsl:template match="Warning[contains(concat(' ', @Class, ' '),' Warning ')]">
    <div class="alert alert-warning" role="alert">
      <h4>
        <span class="glyphicon glyphicon-warning-sign"/>
        <stong>
          <xsl:value-of select="//Signalword"/>
        </stong>
      </h4>
        <xsl:apply-templates/>
    </div>
  </xsl:template>

Then, write additional templates that match List:

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

One that matches ListEntry:

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

And finally, one that matches BodyText:

<xsl:template match="BodyText">
  <p>
    <xsl:value-of select="."/>
  </p>
</xsl:template>

As you can see, I do not use for-each because it is absolutely not necessary. Instead, there are separate template rules for all elements. That should bring order into your code.

Upvotes: 1

Related Questions