Home > Back-end >  using XSLT apply-template to descendents with different names
using XSLT apply-template to descendents with different names

Time:10-21

I am trying to use apply-templates for elements encapsulated within parent element but has different element name. In my example I want to apply it to /Author/Name/ elements and choose either of one whichever has a value. This task is continuation of another question I have asked. I am using the following XML

<?xml version="1.0" encoding="UTF-8"?>
<Author>
<Info>
     <Name>
         <FirstName>#3     bb***</FirstName>
        <LastName>test</LastName>
     </Name>
    <Input>
        <Item>##### 3     ??***</Item>
    </Input>
</Info>
<Custom>Test</Custom>
</Author>

I am applying the following XSLT 1.0 using java. In above example I want template applied to firstName and lastName but show value in firstName since it is the first with a value.

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:variable name="allowed-start-chars">abcdefghijklmnopqrstuvwxyz</xsl:variable>
    <xsl:variable name="allowed-follow-chars">0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ ?abcdefghijklmnopqrstuvwxyz-&apos;.,/@&amp;()! </xsl:variable>

    <xsl:template match="/">
        <html>
            <body>
             <!-- Not sure how to use `xsl:choose` just added to show my intentions-->
   <xsl:choose>
      <xsl:when test="..">
                <div>
                    <xsl:apply-templates  select="Author/Name/FirstName"/>
                </div>
     </xsl:when>
  </xsl:choose>
            </body>
        </html>
    </xsl:template>
   <!--used pipe to apply for two different elements but it does not work-->
    <xsl:template match="FirstName|LastName">
        <!-- find the first character eligible to be starting character -->
        <xsl:variable name="start-chars" select="translate(., translate(., $allowed-start-chars, ''), '')"/>
        <xsl:variable name="start-char" select="substring($start-chars, 1, 1)"/>
        <!-- get text after the chosen starting character -->
        <xsl:variable name="tail" select="substring-after(., $start-char)"/>
        <!-- remove unwanted characters from tail -->
        <xsl:variable name="fo" select="translate($tail, translate($tail, $allowed-follow-chars, ''), '')"/>        <xsl:choose>
            <!--only show if character remain after removing bad ones, and not empty-->
            <xsl:when test="string-length($start-char) &gt; 0 and normalize-space(concat($start-char, $fo))">
                <xsl:value-of select="normalize-space(concat($start-char, $fo))"/>
            </xsl:when>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

The Java code is Stylizer class from Oracle tuorial page example, please refer the link for full code. I am running command-line sending xml and xslt files.

   File stylesheet = new File(argv[0]);
            File datafile = new File(argv[1]);

            DocumentBuilder builder = factory.newDocumentBuilder();
            document = builder.parse(datafile);

            // Use a Transformer for output
            TransformerFactory tFactory = TransformerFactory.newInstance();
            StreamSource stylesource = new StreamSource(stylesheet);
            Transformer transformer = tFactory.newTransformer(stylesource);

            DOMSource source = new DOMSource(document);
            StreamResult result = new StreamResult(System.out);
            transformer.transform(source, result);

I would appreciate again help on this one. As I am learning XSLT and I am also not sure what performance issue using approach

CodePudding user response:

From the context of:

<xsl:template match="/">

you can do:

<xsl:apply-templates select="(Author/Info/Name/FirstName | Author/Info/Name/LastName)[text()][1]"/>

to select the first element of the two that has a child text node. If there are only FirstName and LastName, you can shorten this to:

<xsl:apply-templates select="Author/Info/Name/*[text()][1]"/>

Note that your attempt:

<xsl:apply-templates  select="Author/Name/FirstName"/>

cannot work because Name is not a child of Author.

  • Related