2

I'm limited to working with XML I can't customize. It gets automatically generated by an upstream process, and it creates a confusing nest of nodes that I'm having trouble sorting though with XSLT. I'm trying to iterate through them to return an element called FilterSize.

The XML is a list of objects called Campaigns (two of them) and a FilterSize value for each of them. The goal is to create a simple, two column table showing a name I choose next to it's filtersize.

The problem is the Campaign titles are filled in under the attribute "name=" and on top of that, its a non-human readable one, like 40F0C2B3-0CA2-4E10-A3A5-8F7CF4BB9916

So basically I want to substitute those names for my own: Campaign1 Campaign2

and then use a for-each loop to grab the values for FilterSize, which are currently housed in the ININ.Dialer.Campaign_FilterSize node. I've been able to do the latter, but I can't figure out how to line them up up with my custom campaign names.

I've been playing around with table designs using the TryIt editor on W3 schools so I can visualize the output, and that's helped. I've tried using the XPATH selectors of 1 and [0] but it just returns blank with any value besides 1. One thing that seems to work is , so that's what I've used.

Here's the XML:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Statistics PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<Statistics>
   <IcServer name="localserv">
      <ININ.Dialer.Campaign>
         <ININ.Dialer_Campaign name="{40F0C2B3-0CA2-4E10-A3A5-8F7CF4BB9916}">
            <ININ.Dialer_Site name="XLSite">
               <ININ.Dialer.Campaign_FilterSize>116101</ININ.Dialer.Campaign_FilterSize>
            </ININ.Dialer_Site>
         </ININ.Dialer_Campaign>
         <ININ.Dialer_Campaign name="{657778E0-9079-4114-B639-BFD1AC3613F6}">
            <ININ.Dialer_Site name="XLSite">
               <ININ.Dialer.Campaign_FilterSize>21665</ININ.Dialer.Campaign_FilterSize>
            </ININ.Dialer_Site>
         </ININ.Dialer_Campaign>
      </ININ.Dialer.Campaign>
   </IcServer>
   <PackageName>ICBL_Admin_Clin</PackageName>
   <Description />
   <Created>20190724T184946169Z</Created>
   <Message />
   <MessageExpires>20200626T201516Z</MessageExpires>
</Statistics>

And here's what I've been playing around with in XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
  <html>
  <body>
    <h2>Campaigns</h2>
    <table border="1">
      <tr bgcolor="#9acd32">
        <th>Campaign</th>
        <th>Filter Size</th>
      </tr>
      <tr>
        <td>Campaign 1</td>
      </tr>
      <tr>
        <td>Campaign 2</td>
      </tr>
      <xsl:for-each select="//ININ.Dialer.Campaign_FilterSize">
        <td><xsl:copy-of select="node()"></td>
      </xsl:for-each>
    </table>

  </body>
  </html>
</xsl:template>
</xsl:stylesheet>

In place of the loops, I've also tried selecting the values and dropping them in the rows one at a time using

<xsl:value-of select="Statistics/IcServer/ININ.Dialer.Campaign"/>

But that selects both FilterSize values, regardless of using a selector.

I'm not sure where to put the loop when setting up the table, since it will need two rows worth of space, and need to be adjacent to the appropriate Campaign name.

So, Ideally, something like this: good

Instead, I can't get past this: not so good

I was looking at this post on creating tables and this one about doing it with loops but I can't seem to synthesize the loop with static content.

I also considered looping though the "bad" names in the ININ.Dialer_Campaign node, doing something like "if name equals 40F0C2B3-0CA2-4E10-A3A5-8F7CF4BB9916 then print Campaign 1" and kind of post-process it, but I wasn't sure if that was a capability.

The issue I'm having with researching this is that every example I see has extremely simple tag structures, while these ones seem messy and confusing.

formLoad
  • 25
  • 5

1 Answers1

0

Here's a dead simple way to look at it:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/Statistics">
    <html>
        <body>
            <h2>Campaigns</h2>
            <table border="1">
                <tr bgcolor="#9acd32">
                    <th>Campaign</th>
                    <th>Filter Size</th>
                </tr>
                <xsl:for-each select="IcServer/ININ.Dialer.Campaign/ININ.Dialer_Campaign">
                    <tr>
                        <td>
                            <xsl:choose>
                                <xsl:when test="position() = 1">Your String 1</xsl:when>
                                <xsl:when test="position() = 2">Your String 2</xsl:when>
                                <!-- add more as necessary -->
                            </xsl:choose>
                        </td>
                        <td>
                            <xsl:value-of select="ININ.Dialer_Site/ININ.Dialer.Campaign_FilterSize"/>
                        </td>
                    </tr>
                </xsl:for-each>
            </table>
        </body>
  </html>
</xsl:template>

</xsl:stylesheet>

Alternatively, you could set up a kind of a lookup table, so you don't depend on the order of the nodes in the input:

<xsl:for-each select="IcServer/ININ.Dialer.Campaign/ININ.Dialer_Campaign">
    <tr>
        <td>
            <xsl:choose>
                <xsl:when test="@name ='{40F0C2B3-0CA2-4E10-A3A5-8F7CF4BB9916}'">String 1</xsl:when>
                <xsl:when test="@name ='{657778E0-9079-4114-B639-BFD1AC3613F6}'">String 2</xsl:when>
                <!-- add more as necessary -->
            </xsl:choose>
        </td>
        <td>
            <xsl:value-of select="ININ.Dialer_Site/ININ.Dialer.Campaign_FilterSize"/>
        </td>
    </tr>
</xsl:for-each>
michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
  • Ah! Okay so a bit of clarification, Campaign 1 and Campaign 2 were just sort of stand-ins, the real values would be discrete strings that I pick, so instead of Campaign 1 and 2, they'd be like Taco: Filter_Size_Val and Whale: Filter_Size_Val. I can't figure out how to set those individually for each loop, if that makes sense. – formLoad Jul 24 '19 at 23:29
  • Where would these strings be kept? Do you want to hard-code them into your stylesheet? And what if your XML has more campaigns than you have strings? – michael.hor257k Jul 24 '19 at 23:32
  • Ideally I would pull it from the XML, but for some reason, the names that you see in "ININ.Dialer_Campaign name=" are like weird IDs instead of their proper names, and I don't think there's any way to derive them. I think hardcoding is my best bet here, as the number of campaign variables will always be the same. I tried declaring something like Whale, but would I have to make it part of a template to use it in the tag? – formLoad Jul 24 '19 at 23:47
  • *"the number of campaign variables will always be the same.*" How many? – michael.hor257k Jul 24 '19 at 23:49
  • Sorry, it's as many as I choose, so like in this upstream process I can choose to show the statistics of X number of campaigns, and for this text I picked two, and it generated that XML. This "view" will be a static page, so in this case it will permanently be just the two campaigns forever. I just can't work out how to apply my chosen names in the loop. Or outside of it, for that matter. – formLoad Jul 24 '19 at 23:59
  • I have to leave now. See if my edited answer solves your problem. – michael.hor257k Jul 25 '19 at 00:01