XML XSLT return values for duplicate/repeating elements

Very new to XML so excuse if I don't have my terms right yet.

I've got some XML looks  like this:

<port protocol="tcp" portid="21"><state state="open" /><service name="ftp" method="table" conf="3" /></port>
<port protocol="tcp" portid="53"><state state="open" /><service name="domain" method="table" conf="3" /></port>
<port protocol="tcp" portid="80"><state state="open" /><service name="http" method="table" conf="3" /></port>
<port protocol="tcp" portid="135"><state state="open" /><service name="msrpc" method="table" conf="3" /></port>


I'm want to return all values for "service name" and display in a single cell of a table (i.e.,  ftp, domain, http, msrpc).

All I get with <td><xsl:value-of select="ports/port/service/@name" /></td> is the first value, "ftp".

What am I trying to do (by that I mean, what is the operation called)? And, better yet, how do I do it?








LVL 3
tnapolitanoAsked:
Who is Participating?
 
Geert BormansInformation ArchitectCommented:
welcome
0
 
Geert BormansInformation ArchitectCommented:
Hi tnapolitano,

This XSLT will make you a table with all the values

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="/">
        <html>
            <body>
                <xsl:apply-templates/>
            </body>
        </html>
    </xsl:template>
    <xsl:template match="ports">
        <table border="1">
            <xsl:apply-templates select="port"/>
        </table>
    </xsl:template>
    <xsl:template match="port">
        <tr>
            <td><xsl:value-of select="service/@name" /></td>
        </tr>
    </xsl:template>
</xsl:stylesheet>

Cheers!
0
 
Geert BormansInformation ArchitectCommented:
tnapolitano,

I have templates (rules based nature of XSLT) for the different elements
and inside one template I push nodesets to the templates with apply-templates
like that I create a very hierarchical model for processing the XML

the stylesheet starts working with the template match="/"
There I create the html and body tag and in the middle of that I push all the chilnodes to the templates
The element "ports" gets picked up by the template for "ports"
and there I create a table and push the port nodes to the templates
As you see now, I have created a table inside the html/body

the template match="port" will be executed for every port element inside ports
this is how I create a number of rows , one for each port

If you would want only one row, this is what you would do

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="/">
        <html>
            <body>
                <xsl:apply-templates/>
            </body>
        </html>
    </xsl:template>
    <xsl:template match="ports">
        <table border="1">
            <tr>
                <xsl:apply-templates select="port"/>
            </tr>
         </table>
    </xsl:template>
    <xsl:template match="port">
             <td><xsl:value-of select="service/@name" /></td>
    </xsl:template>
</xsl:stylesheet>

take your time to understand the examples
note that this way of programming XSLT is superior over using for-each loops

cheers

Geert
0
Cloud Class® Course: Amazon Web Services - Basic

Are you thinking about creating an Amazon Web Services account for your business? Not sure where to start? In this course you’ll get an overview of the history of AWS and take a tour of their user interface.

 
Geert BormansInformation ArchitectCommented:
tnapolitano,

from your title I assume that you only want unique values

here would be the XSLT that ignores a duplicate value "ftp" and only shows it want
I do this by being selective on the port-nodes that I push to the port template
(it should not have a value of service/@name that a preceding port element has)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="/">
        <html>
            <body>
                <xsl:apply-templates/>
            </body>
        </html>
    </xsl:template>
    <xsl:template match="ports">
        <table border="1">
            <tr>
                <xsl:apply-templates select="port[not(service/@name = preceding::port/service/@name)]"/>
            </tr>
         </table>
    </xsl:template>
    <xsl:template match="port">
             <td><xsl:value-of select="service/@name" /></td>
    </xsl:template>
</xsl:stylesheet>

I hope this is enough to get you started

Geert
0
 
tnapolitanoAuthor Commented:
Gertone, thanks for all that. Let me try this out and I'll get back to you.
0
 
tnapolitanoAuthor Commented:
Great answer, gertone. Just what I needed. Both the code to get the job done and explanations of what's going on. Thanks again.
0
 
tnapolitanoAuthor Commented:
I accept this answer. You can close it out.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.