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?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Gertone (Geert Bormans)Information 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!
Gertone (Geert Bormans)Information 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
Gertone (Geert Bormans)Information 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
Introduction to Web Design

Develop a strong foundation and understanding of web design by learning HTML, CSS, and additional tools to help you develop your own website.

tnapolitanoAuthor Commented:
Gertone, thanks for all that. Let me try this out and I'll get back to you.
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.
Gertone (Geert Bormans)Information ArchitectCommented:
welcome

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
tnapolitanoAuthor Commented:
I accept this answer. You can close it out.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Web Languages and Standards

From novice to tech pro — start learning today.