Link to home
Start Free TrialLog in
Avatar of tnapolitano
tnapolitano

asked on

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?








Avatar of Gertone (Geert Bormans)
Gertone (Geert Bormans)
Flag of Belgium image

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!
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
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
Avatar of tnapolitano
tnapolitano

ASKER

Gertone, thanks for all that. Let me try this out and I'll get back to you.
Great answer, gertone. Just what I needed. Both the code to get the job done and explanations of what's going on. Thanks again.
ASKER CERTIFIED SOLUTION
Avatar of Gertone (Geert Bormans)
Gertone (Geert Bormans)
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I accept this answer. You can close it out.