We help IT Professionals succeed at work.

We've partnered with Certified Experts, Carl Webster and Richard Faulkner, to bring you a podcast all about Citrix Workspace, moving to the cloud, and analytics & intelligence. Episode 2 coming soon!Listen Now

x

XML XSLT return values for duplicate/repeating elements

Medium Priority
801 Views
Last Modified: 2013-11-19
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?








Comment
Watch Question

Gertone (Geert Bormans)Information Architect
CERTIFIED EXPERT
Top Expert 2006

Commented:
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 Architect
CERTIFIED EXPERT
Top Expert 2006

Commented:
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 Architect
CERTIFIED EXPERT
Top Expert 2006

Commented:
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

Author

Commented:
Gertone, thanks for all that. Let me try this out and I'll get back to you.

Author

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.
Information Architect
CERTIFIED EXPERT
Top Expert 2006
Commented:
welcome

Not the solution you were looking for? Getting a personalized solution is easy.

Ask the Experts

Author

Commented:
I accept this answer. You can close it out.
Access more of Experts Exchange with a free account
Thanks for using Experts Exchange.

Create a free account to continue.

Limited access with a free account allows you to:

  • View three pieces of content (articles, solutions, posts, and videos)
  • Ask the experts questions (counted toward content limit)
  • Customize your dashboard and profile

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.