Pregunta

I am working with a marketing platform to display the Bags that a person has ordered for a flight. One Passenger can have multiple Segments as part of an overall Journey. So if we have records like below (each record has a unique value assigned as well since this is all inserted into a database before being pulled out, the xml here is a mockup of what im working with).

Edit 8/9 - Actual XML and revised XSLT below. Also if you look at this link (http://f.spiritairlines.com/ats/msg.aspx?sg1=2e4a4d00cb00be84c598b618d6556e3e) in the Bag section. I want it to look like the second line (Pedro Smith) and not the first line (Sandra Smith). By the way this is all test data so there is no privacy concerns with showing you nor is there any private info on their.

<bookingcontact_to_booking>
    <passenger_to_bookings> 
        <passenger_to_booking>
            <Prop pk_id="4147" entity_id="126" val="SMITH" type_id="20" prop_name="lastname" prop_id="10208"/>
            <Prop pk_id="4147" entity_id="126" val="PEDRO" type_id="20" prop_name="firstname" prop_id="10206"/>
            <passseg_to_passengers>
                <passseg_to_passenger>
                    <Prop pk_id="9432" entity_id="122" val="2013-08-27 17:45:00.000" type_id="30" prop_name="arrivaltime" prop_id="10216"/>
                    <Prop pk_id="9432" entity_id="122" val="188" type_id="20" prop_name="flightnumber" prop_id="10217"/>
                    <Prop pk_id="9432" entity_id="122" val="5" type_id="10" prop_name="checkedbagcount" prop_id="10220"/>
                    <Prop pk_id="9432" entity_id="122" val="1" type_id="10" prop_name="carryonbagcount" prop_id="10221"/>
                    <Prop pk_id="9432" entity_id="122" val="1" type_id="10" prop_name="journeynumber" prop_id="10222"/>
                    <Prop pk_id="9432" entity_id="122" val="0" type_id="10" prop_name="segmentnumber" prop_id="10223"/>
                    <Prop pk_id="9432" entity_id="122" val="2013-08-27 08:12:00.000" type_id="30" prop_name="departuretime" prop_id="10296"/>
                    <Prop pk_id="9432" entity_id="122" val="Las Vegas, NV" type_id="20" prop_name="departureairport" prop_id="10297"/>
                    <Prop pk_id="9432" entity_id="122" val="New York, NY - LaGuardia " type_id="20" prop_name="arrivalairport" prop_id="10298"/>
                    <Prop pk_id="9432" entity_id="122" val="10B,20B" type_id="20" prop_name="seatassignment" prop_id="10299"/>
                </passseg_to_passenger>
                <passseg_to_passenger>
                    <Prop pk_id="9433" entity_id="122" val="10B,20B" type_id="20" prop_name="seatassignment" prop_id="10299"/>
                    <Prop pk_id="9433" entity_id="122" val="Fort Lauderdale, FL" type_id="20" prop_name="arrivalairport" prop_id="10298"/>
                    <Prop pk_id="9433" entity_id="122" val="New York, NY - LaGuardia " type_id="20" prop_name="departureairport" prop_id="10297"/>
                    <Prop pk_id="9433" entity_id="122" val="2013-08-27 19:45:00.000" type_id="30" prop_name="departuretime" prop_id="10296"/>
                    <Prop pk_id="9433" entity_id="122" val="1" type_id="10" prop_name="segmentnumber" prop_id="10223"/>
                    <Prop pk_id="9433" entity_id="122" val="1" type_id="10" prop_name="journeynumber" prop_id="10222"/>
                    <Prop pk_id="9433" entity_id="122" val="1" type_id="10" prop_name="carryonbagcount" prop_id="10221"/>
                    <Prop pk_id="9433" entity_id="122" val="5" type_id="10" prop_name="checkedbagcount" prop_id="10220"/>
                    <Prop pk_id="9433" entity_id="122" val="779" type_id="20" prop_name="flightnumber" prop_id="10217"/>
                    <Prop pk_id="9433" entity_id="122" val="2013-08-27 22:45:00.000" type_id="30" prop_name="arrivaltime" prop_id="10216"/>
                </passseg_to_passenger>
            </passseg_to_passengers>
        </passenger_to_booking>
    </passenger_to_bookings>
</bookingcontact_to_booking>

I want to only display the CarryOnBagCount and CheckedBagCount once per journey. I have tried a few different methods, but i think i am getting further from the solution. At the moment the output is all bagcounts regardless of journey and segment. XSLT is below.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="UTF-8" indent="yes" omit-xml-declaration="yes" />

    <xsl:key name="journey" match="passengersegment" use="Prop[@prop_name = 'journeynumber']/@val"/>

    <xsl:template match="/">
        <xsl:for-each select="/Msg/Props/bookingcontact_to_booking/passenger_to_bookings/passenger_to_booking">
            <tr>
                <td colspan="3">
                    <table style="text-align: left; line-height: 18px; font-family: Arial, Helvetica, sans-serif; font-size: 12px; color:#333333;"
                    border="0" cellspacing="0" cellpadding="0" width="542" align="left">
                        <tbody>
                            <tr>
                                <td height="22" width="200">
                                    <xsl:value-of select="Prop[@prop_name = 'firstname']/@val" /><xsl:text> </xsl:text><xsl:value-of select="Prop[@prop_name = 'lastname']/@val" />
                                </td>
                                <xsl:apply-templates select="passengersegment[generate-id() = generate-id(key('journey', journeynumber)[1])]"/>
                            </tr>
                        </tbody>
                    </table>
                </td>
            </tr>
        </xsl:for-each>
    </xsl:template>

    <xsl:template match="passengersegment">
        <td style="text-align: left;" width="100">
            <table style="text-align: left; line-height: 18px; font-family: Arial, Helvetica, sans-serif; font-size: 12px; color:#333333;" width="100">
                <tr>
                    <xsl:for-each select="passseg_to_passengers/passseg_to_passenger">
                        <td width="50">
                            <xsl:value-of select="Prop[@prop_name = 'carryonbagcount']/@val" />
                        </td>
                    </xsl:for-each>
                </tr>
            </table>                            
        </td>
        <td style="text-align: left;" width="100">
            <table style="text-align: left; line-height: 18px; font-family: Arial, Helvetica, sans-serif; font-size: 12px; color:#333333;" width="100">
                <tr>
                    <xsl:for-each select="passseg_to_passengers/passseg_to_passenger">
                        <td width="50">
                            <xsl:value-of select="Prop[@prop_name = 'checkedbagcount']/@val" />
                        </td>
                    </xsl:for-each>
                </tr>
            </table>                            
        </td>
    </xsl:template>

</xsl:stylesheet>
¿Fue útil?

Solución

It's a bit tricky to give a precise answer without seeing a sample of your expected HTML, and also because the element names in your XSLT don't seem to match up with your input XML sample. However, the technique you need to be reading up about and using here is Muenchian Grouping. This is the most efficient way of grouping elements in XSLT 1.0.

In your case, you want to get the distinct journeys for the passenger, and then get the first segment for each one. You do this by defining a key to lookup the passengersegment elements by their journeynumber

<xsl:key name="journey" match="passengersegment" use="journeynumber"/>

Then, to get the distinct journey numbers, you look at all passengersegment but only select the ones that occur first in the key for their given journeynumber value.

<xsl:apply-templates 
     select="passengersegment
             [generate-id() = generate-id(key('journey', journeynumber)[1])]"/>

Within the template that matches passengersegment you can then output the first carryonbagcount and checkednbagcount for the segment (the first in the journey).

Try this XSLT as a start, and build on this:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output omit-xml-declaration="yes" indent="yes"/>
  <xsl:key name="journey" match="passengersegment" use="journeynumber"/>

  <xsl:template match="/passenger">
    <table border="1">
      <tr>
        <th>Journey</th>
        <th>Carry on</th>
        <th>Checked</th>
      </tr>
      <xsl:apply-templates select="passengersegment[generate-id() = generate-id(key('journey', journeynumber)[1])]"/>
    </table>
  </xsl:template>

  <xsl:template match="passengersegment">
    <tr>
      <td>
        <xsl:value-of select="journeynumber"/>
      </td>
      <td>
        <xsl:value-of select="carryonbagcount"/>
      </td>
      <td>
        <xsl:value-of select="checkedbagcount"/>
      </td>
    </tr>
  </xsl:template>
</xsl:stylesheet>

Note that if wanted to get the total carryonbagcount for all segments in the journey, then within the passengersegment you could do something like this:

<xsl:value-of select="sum(key('journey', journeynumber)/carryonbagcount)" />

Otros consejos

Assuming that the output carryonbagcount should be the maximum of each of the legs, here is a small tweak to Tim's answer.

This input document...

<passenger>
  <passengersegment>
      <journeynumber>0</journeynumber>
      <segmentnumber>0</segmentnumber>
      <carryonbagcount>1</carryonbagcount>
      <checkedbagcount>1</checkedbagcount>
  </passengersegment>
  <passengersegment>
       <journeynumber>0</journeynumber>
       <segmentnumber>1</segmentnumber>
       <carryonbagcount>1</carryonbagcount>
       <checkedbagcount>1</checkedbagcount>
   </passengersegment>
   <passengersegment>
       <journeynumber>1</journeynumber>
       <segmentnumber>0</segmentnumber>
       <carryonbagcount>1</carryonbagcount>
       <checkedbagcount>1</checkedbagcount>
   </passengersegment>
   <passengersegment>
       <journeynumber>1</journeynumber>
       <segmentnumber>1</segmentnumber>
       <carryonbagcount>1</carryonbagcount>
       <checkedbagcount>1</checkedbagcount>
   </passengersegment>

... when this XSLT 1.0 stylesheet is applied to it ...

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html" doctype-system="about:legacy-compat" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />

<xsl:key name="kJourney" match="passengersegment" use="journeynumber" />  

<xsl:template match="/*">
<table>
 <thead>
  <tr>
   <th><strong>Journey</strong></th> <th><strong>CARRY-ON</strong></th> <th><strong>CHECKED</strong></th>
  </tr>
 </thead>
 <tbody>
    <xsl:apply-templates select="passengersegment[generate-id() =
                                                  generate-id(key('kJourney',journeynumber)[1])]"
                         mode="group" /> 
 </tbody>
</table>
</xsl:template>

<xsl:template match="passengersegment" mode="group">
  <tr>
    <td><xsl:value-of select="journeynumber" /></td>

    <xsl:apply-templates select="." mode="max"> 
      <xsl:with-param name="col-name" select="'carryonbagcount'" />
    </xsl:apply-templates>

    <xsl:apply-templates select="." mode="max"> 
      <xsl:with-param name="col-name" select="'checkedbagcount'" />
    </xsl:apply-templates>
  </tr>  
</xsl:template>

<xsl:template match="passengersegment" mode="max"> 
  <xsl:param name="col-name" /> 
  <xsl:for-each select="key('kJourney',journeynumber)/*[name()=$col-name]">
    <xsl:sort select="." data-type="number" order="descending" />
    <xsl:if test="position()=1">
      <td><xsl:value-of select="." /></td>
    </xsl:if>
  </xsl:for-each>  
</xsl:template>  

</xsl:stylesheet>

...yields this HTML output...

<!DOCTYPE html SYSTEM "about:legacy-compat">
<table>
  <thead>
    <tr>
      <th><strong>Journey</strong></th>
      <th><strong>CARRY-ON</strong></th>
      <th><strong>CHECKED</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>1</td>
      <td>1</td>
    </tr>
    <tr>
      <td>1</td>
      <td>1</td>
      <td>1</td>
    </tr>
  </tbody>
</table>

Update

The OP has altered the question and sample data so far, it is really a new question. Assuming that the @entity_id uniquely identifies a passenger. If not, the OP should properly explain the input data structure and rules of transformation. So here is a solution to the new question..

This XSLT 1.0 stylesheet...

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html" doctype-system="about:legacy-compat" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />

<xsl:key name="kJourney" match="passseg_to_passenger"
  use="concat( ../../Prop/@entity_id, '|', Prop[@prop_name='journeynumber']/@val)" /> 
  <!-- Assume that @entity_id is an identifier for the passenger. -->

<xsl:template match="bookingcontact_to_booking">
<table>
 <thead>
  <tr>
   <th><strong>PASSENGER</strong></th> <th><strong>CARRY-ON</strong></th> <th><strong>CHECKED</strong></th>
  </tr>
 </thead>
 <tbody>
   <xsl:apply-templates select="passenger_to_bookings/passenger_to_booking" /> 
 </tbody>
</table>
</xsl:template>

<xsl:template match="passenger_to_booking">
  <xsl:variable name="passenger-id" select="(Prop/@entity_id)[1]" />
  <xsl:variable name="passenger-name" select="
   concat( Prop[@prop_name='firstname']/@val, ' ', Prop[@prop_name='lastname']/@val)" />
  <tr>
   <xsl:apply-templates select="passseg_to_passengers/passseg_to_passenger
       [generate-id() = generate-id(key('kJourney',
            concat( $passenger-id, '|', Prop[@prop_name='journeynumber']/@val))[1])]"
     mode="group"> 
    <xsl:with-param name="passenger-id"   select="$passenger-id" />
    <xsl:with-param name="passenger-name" select="$passenger-name" />
  </xsl:apply-templates>   
  </tr>
</xsl:template>

<xsl:template match="passseg_to_passenger" mode="group">
  <xsl:param name="passenger-id" />
  <xsl:param name="passenger-name" />
  <td><xsl:value-of select="$passenger-name" /></td>

  <xsl:apply-templates select="." mode="max"> 
    <xsl:with-param name="passenger-id" select="$passenger-id" />
    <xsl:with-param name="col-name" select="'carryonbagcount'" />
  </xsl:apply-templates>

  <xsl:apply-templates select="." mode="max"> 
    <xsl:with-param name="passenger-id" select="$passenger-id" />
    <xsl:with-param name="col-name" select="'checkedbagcount'" />
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="passseg_to_passenger" mode="max"> 
  <xsl:param name="passenger-id" />
  <xsl:param name="col-name" /> 
  <xsl:for-each select="key('kJourney',
      concat( $passenger-id, '|', Prop[@prop_name='journeynumber']/@val)
  )/Prop[@prop_name=$col-name]/@val">
    <xsl:sort select="." data-type="number" order="descending" />
    <xsl:if test="position()=1">
      <td><xsl:value-of select="." /></td>
    </xsl:if>
  </xsl:for-each>  
</xsl:template>  

</xsl:stylesheet>

...when applied to this input document...

<bookingcontact_to_booking>
    <passenger_to_bookings> 
        <passenger_to_booking>
            <Prop pk_id="4147" entity_id="126" val="SMITH" type_id="20" prop_name="lastname" prop_id="10208"/>
            <Prop pk_id="4147" entity_id="126" val="PEDRO" type_id="20" prop_name="firstname" prop_id="10206"/>
            <passseg_to_passengers>
                <passseg_to_passenger>
                    <Prop pk_id="9432" entity_id="122" val="2013-08-27 17:45:00.000" type_id="30" prop_name="arrivaltime" prop_id="10216"/>
                    <Prop pk_id="9432" entity_id="122" val="188" type_id="20" prop_name="flightnumber" prop_id="10217"/>
                    <Prop pk_id="9432" entity_id="122" val="5" type_id="10" prop_name="checkedbagcount" prop_id="10220"/>
                    <Prop pk_id="9432" entity_id="122" val="1" type_id="10" prop_name="carryonbagcount" prop_id="10221"/>
                    <Prop pk_id="9432" entity_id="122" val="1" type_id="10" prop_name="journeynumber" prop_id="10222"/>
                    <Prop pk_id="9432" entity_id="122" val="0" type_id="10" prop_name="segmentnumber" prop_id="10223"/>
                    <Prop pk_id="9432" entity_id="122" val="2013-08-27 08:12:00.000" type_id="30" prop_name="departuretime" prop_id="10296"/>
                    <Prop pk_id="9432" entity_id="122" val="Las Vegas, NV" type_id="20" prop_name="departureairport" prop_id="10297"/>
                    <Prop pk_id="9432" entity_id="122" val="New York, NY - LaGuardia " type_id="20" prop_name="arrivalairport" prop_id="10298"/>
                    <Prop pk_id="9432" entity_id="122" val="10B,20B" type_id="20" prop_name="seatassignment" prop_id="10299"/>
                </passseg_to_passenger>
                <passseg_to_passenger>
                    <Prop pk_id="9433" entity_id="122" val="10B,20B" type_id="20" prop_name="seatassignment" prop_id="10299"/>
                    <Prop pk_id="9433" entity_id="122" val="Fort Lauderdale, FL" type_id="20" prop_name="arrivalairport" prop_id="10298"/>
                    <Prop pk_id="9433" entity_id="122" val="New York, NY - LaGuardia " type_id="20" prop_name="departureairport" prop_id="10297"/>
                    <Prop pk_id="9433" entity_id="122" val="2013-08-27 19:45:00.000" type_id="30" prop_name="departuretime" prop_id="10296"/>
                    <Prop pk_id="9433" entity_id="122" val="1" type_id="10" prop_name="segmentnumber" prop_id="10223"/>
                    <Prop pk_id="9433" entity_id="122" val="1" type_id="10" prop_name="journeynumber" prop_id="10222"/>
                    <Prop pk_id="9433" entity_id="122" val="1" type_id="10" prop_name="carryonbagcount" prop_id="10221"/>
                    <Prop pk_id="9433" entity_id="122" val="5" type_id="10" prop_name="checkedbagcount" prop_id="10220"/>
                    <Prop pk_id="9433" entity_id="122" val="779" type_id="20" prop_name="flightnumber" prop_id="10217"/>
                    <Prop pk_id="9433" entity_id="122" val="2013-08-27 22:45:00.000" type_id="30" prop_name="arrivaltime" prop_id="10216"/>
                </passseg_to_passenger>
            </passseg_to_passengers>
        </passenger_to_booking>
    </passenger_to_bookings>
</bookingcontact_to_booking>

...yields this html output document...

<!DOCTYPE html SYSTEM "about:legacy-compat">
<table>
  <thead>
    <tr>
      <th><strong>PASSENGER</strong></th>
      <th><strong>CARRY-ON</strong></th>
      <th><strong>CHECKED</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>PEDRO SMITH</td>
      <td>1</td>
      <td>5</td>
    </tr>
  </tbody>
</table>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top