Where’s my seat?

Summary: Sabre TravelItineraryReadService TravelItineraryReadRQ version 2.2.0 returns incomplete Seat data. TravelItineraryReadRQ version 3.4.0 returns all of the Seat data.

I had bug in my PNR capture code – it was throwing an exception on my persist flight details stored procedure call because I wasn’t giving it a required seat number. It was a trivial code fix, I needed to pass an empty string to the stored procedure (not a null value).

But did I have a bug further upstream in my TravelItineraryReadRS parsing and mapping code? Or is the data really missing?

My current version of the TravelItineraryReadService is using TravelItineraryReadRQ version 2.2.0. Below is the snippet from the TravelItineraryReadRS. You can see that it’s only returning a seat number for segment 2, all of the other segments have no value for the seat number – the attribute is missing entirely. So I don’t have any parsing/mapping issues, sabre isn’t giving me the data.

<TravelItineraryReadRS Version="2.2.0">
...
  <Seats>
	<Seat NameNumber="01.01" SegmentNumber="0001" Status="HRQ">
	  <FlightSegment>
		<DestinationLocation LocationCode="PHX" />
		<OriginLocation LocationCode="SBA" />
	  </FlightSegment>
	</Seat>
	<Seat Changed="N" NameNumber="01.01" Number="04A" SegmentNumber="0002" SegmentStatus="HK" SmokingPreference="N" Status="HRS" TypeTwo="WLMI">
	  <FlightSegment>
		<DestinationLocation LocationCode="ORD" />
		<OriginLocation LocationCode="PHX" />
	  </FlightSegment>
	</Seat>
	<Seat NameNumber="01.01" SegmentNumber="0003" Status="HRQ">
	  <FlightSegment>
		<DestinationLocation LocationCode="PHX" />
		<OriginLocation LocationCode="ORD" />
	  </FlightSegment>
	</Seat>
	<Seat NameNumber="01.01" SegmentNumber="0004" Status="HRQ">
	  <FlightSegment>
		<DestinationLocation LocationCode="SBA" />
		<OriginLocation LocationCode="PHX" />
	  </FlightSegment>
	</Seat>
  </Seats>
...
</TravelItineraryReadRS>

I’m in the middle of evaluating (and implementing) TravelItineraryReadRQ version 3.4.0. Cool, it has my seat data! Maybe I need to roll this into production sooner than planned…

<TravelItineraryReadRS Version="3.4.0">
...
  <Seats>
	<Seat Changed="N" NameNumber="01.01" Number="02A" SegmentNumber="0001" SegmentStatus="PN" SmokingPreference="N" Status="HRS" TypeTwo="">
	  <FlightSegment>
		<DestinationLocation LocationCode="PHX" />
		<OriginLocation LocationCode="SBA" />
	  </FlightSegment>
	</Seat>
	<Seat Changed="N" NameNumber="01.01" Number="04A" SegmentNumber="0002" SegmentStatus="HK" SmokingPreference="N" Status="HRS" TypeTwo="WLMI">
	  <FlightSegment>
		<DestinationLocation LocationCode="ORD" />
		<OriginLocation LocationCode="PHX" />
	  </FlightSegment>
	</Seat>
	<Seat Changed="N" NameNumber="01.01" Number="03A" SegmentNumber="0003" SegmentStatus="PN" SmokingPreference="N" Status="HRS" TypeTwo="">
	  <FlightSegment>
		<DestinationLocation LocationCode="PHX" />
		<OriginLocation LocationCode="ORD" />
	  </FlightSegment>
	</Seat>
	<Seat Changed="N" NameNumber="01.01" Number="03F" SegmentNumber="0004" SegmentStatus="PN" SmokingPreference="N" Status="HRS" TypeTwo="">
	  <FlightSegment>
		<DestinationLocation LocationCode="SBA" />
		<OriginLocation LocationCode="PHX" />
	  </FlightSegment>
	</Seat>
  </Seats>
...
</TravelItineraryReadRS>

Custom List In app.config File

I recently had the need to define a custom list in an application. There are many ways to make this happen but I wanted it in the app config file.

After a few searches, I pieced together the following example.

The app.config below has a custom section, AppConfigCollection.CustomDictionary. This section is declared in the configSections and the type attribute has my handler class and assembly name.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 
  <configSections>
    <section name="AppConfigCollection.CustomDictionary" type="AppConfigCollection.CustomDictionary, AppConfigCollection"/>
  </configSections>
 
  <AppConfigCollection.CustomDictionary>
 
    <NFL>
      <AFC>
        <AFC_WEST>
          <Item code="DEN" team="Denver Broncos" />
          <Item code="MCI" team="Kansas City Chiefs" />
          <Item code="SAN" team="San Diego Chargers" />
          <Item code="OAK" team="Oakland Raiders" />
        </AFC_WEST>
      </AFC>
 
      <NFC>
        <NFC_EAST>
          <Item code="DFW" team="Dallas Cowboys" />
          <Item code="PHL" team="Philadelphia Eagles" />
          <Item code="EWR" team="New York Giants" />
          <Item code="IAD" team="Washington Redskins" />
        </NFC_EAST>
      </NFC>
    </NFL>
 
  </AppConfigCollection.CustomDictionary>
 
</configuration>

The handler class CustomDictionary inherits from IConfigurationSectionHandler. This interface has been deprecated but it’s still available and easy to implement and make it work.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Xml.Linq;
 
namespace AppConfigCollection
{
 
    class Program
    {
        static void Main(string[] args)
        {
            var teamDictionary = (Dictionary<string, string>)ConfigurationManager.GetSection("AppConfigCollection.CustomDictionary");
 
            Console.WriteLine();
            Console.WriteLine("NFL TEAMS");
            foreach (var key in teamDictionary.Keys)
            {
                Console.WriteLine("{0} : {1}", key, teamDictionary[key]);
            }
 
            Console.WriteLine();
            Console.WriteLine("press any key to continue ...");
            Console.ReadLine();
        }
    }
 
    public class CustomDictionary : IConfigurationSectionHandler
    {
        public object Create(object parent, object configContext, System.Xml.XmlNode section)
        {
            var rList = new Dictionary<string, string>();
 
            var doc = XDocument.Parse(section.OuterXml);
            var root = (XElement)doc.FirstNode;
 
            var rootElement = (XElement)root.FirstNode;
            if (rootElement.Name != "NFL")
                throw new ConfigurationErrorsException(
                    "AppConfigCollection.CustomDictionary section only accepts 'NFL' elements.");
 
            try
            {
                foreach (XElement conference in rootElement.Nodes())
                {
                    Console.WriteLine();
                    Console.WriteLine("Conference : {0}", conference.Name);
 
                    foreach (XElement division in conference.Nodes())
                    {
                        Console.WriteLine("  Division : {0}", division.Name);
 
                        var teamList = from div in division.Elements("Item") select div;
                        foreach (var item in teamList)
                        {
                            var code = item.Attribute("code").Value;
                            var team = item.Attribute("team").Value;
 
                            Console.WriteLine("    Team ({0}) : {1}", code, team);
                            rList.Add(code, team);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw new ConfigurationErrorsException(
                    "Error reading element.", ex);
            }
 
            return rList;
        }
    }
 
}

This is the output.
AppConfigCollection

Feel free to download the application solution – experiment and adapt it for your needs.

HTTPS WCF web service web config

Here’s the web config for a WCF web service. I struggled getting it to work under HTTPS. But when all done and working it’s not terrible – I’ll try to highlight the relevant names because that was my primary struggle point. I won’t go into explaining everything, there are tons of posts out there can (and do) explain much better than I can or want to here.

WebConfig

I pieced together several different examples in an attempt to get this working and at first my highlighted names didn’t match – and no luck working. So here are the highlighted sections of the serviceModel element with the names matching up.

See ServiceBehaviorA, wsHttpBinding, and TransportSecurity. These are referenced in the endpoint and must also be referenced in the behaviors and bindings elements.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
  <system.serviceModel>
 
    <services>
      <service name="S2C.API.Service" 
 
               <!-- SEE BELOW in serviceBehaviors -->
               behaviorConfiguration="ServiceBehaviorA">
 
        <endpoint address="" 
 
                  <!-- SEE BELOW in bindings -->
                  binding="wsHttpBinding"
 
                  <!-- SEE BELOW the binding name -->
                  bindingConfiguration="TransportSecurity"
 
                  contract="S2C.API.IService" />
 
        <endpoint address="mex" 
                  binding="mexHttpBinding" 
                  contract="IMetadataExchange" />
      </service>
    </services>
 
    <behaviors>
      <serviceBehaviors>
        <behavior name="ServiceBehaviorA">
 
          <!-- To avoid disclosing metadata information, 
               set the value below to false and remove 
               the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
 
          <!-- To receive exception details in faults for debugging purposes, 
               set the value below to true. Set to false before deployment 
               to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="true"/>
 
        </behavior>
      </serviceBehaviors>
    </behaviors>
 
    <bindings>
      <wsHttpBinding>
        <binding name="TransportSecurity">
 
          <security mode="Transport">
            <transport clientCredentialType="None"/>
          </security>
 
        </binding>
      </wsHttpBinding>
    </bindings>
 
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
 
  </system.serviceModel>

Logging Serializable Objects

Here’s a handy method to log your object serialized as XML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//
public void LogObject(object obj)
{
	var msg = "";
	try
	{
		using (var w = new StringWriter())
		{
			var s = new XmlSerializer(obj.GetType());
			s.Serialize(w, obj);
			w.Flush();
			msg = w.ToString();
		}
	}
	catch (Exception e)
	{
		msg = e.Message.ToString();
	}
 
	msg = string.Format("{0}\n{1}", obj.GetType(), msg);
	Log.Debug(msg);
}
 
LogObject(msgHeader);
//

This is what my logger spit out

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
OTA_TravelItineraryReadNS2_2_0.MessageHeader
<?xml version="1.0" encoding="utf-16"?>
<MessageHeader xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.ebxml.org/namespaces/messageHeader">
  <From>
    <PartyId>55555</PartyId>
  </From>
  <To>
    <PartyId>789789789</PartyId>
  </To>
  <CPAId>2WE2</CPAId>
  <ConversationId>130615-120130.2844</ConversationId>
  <Action>TravelItineraryReadLLSRQ</Action>
  <MessageData>
    <MessageId>mid:200051209-18808-23933@clientofsabre.com</MessageId>
  </MessageData>
</MessageHeader>