0

I have an incoming XML stream from a web service response that looks like so

<?xml version="1.0" encoding="utf-16"?>
<HotelPropertyDescriptionRS xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" TimeStamp="2013-12-30T18:49:36" Version="1.14.1">
  <Success xmlns="http://webservices.sabre.com/sabreXML/2003/07" />
  <RoomStay xmlns="http://webservices.sabre.com/sabreXML/2003/07">
    <RoomRates>
      <RoomRate GuaranteeSurchargeRequired="G" IATACharacteristicIdentification="BGGO00" IATAProductIdentification="BLOOMBERG" RPH="001">
        <AdditionalInfo>
          <CancelPolicy Numeric="06" />
          <Commission NonCommission="true">NON COMMISSIONABLE</Commission>
          <Text>BLOOMBERG LP, 0.0 KM, INCLUDES BREAKFAST, INTERNET, WIFI, SEE</Text>
          <Text>RATE RULES DELUXE ROOM, GUEST ROOM, 1 KING OR 2 TWIN/SINGLE BE</Text>
        </AdditionalInfo>
        <Rates>
          <Rate Amount="66.600" CurrencyCode="KWD">
            <AdditionalGuestAmounts>
              <AdditionalGuestAmount MaxExtraPersonsAllowed="0">
                <Charges ExtraPerson="0" />
              </AdditionalGuestAmount>
            </AdditionalGuestAmounts>
            <HotelTotalPricing Amount="76.590">
              <Disclaimer>INCLUDES TAXES AND SURCHARGES</Disclaimer>
              <TotalSurcharges Amount="9.990" />
            </HotelTotalPricing>
          </Rate>
        </Rates>
      </RoomRate>
....

(there will be many RoomRate values returned. I have created a class to hold the values I need to parse from said XML which looks like so

Namespace Classes.Models
    Public Class RoomRate
        Public Property Rate() As String
        Public Property Surcharge() As String
        Public Property TotalPrice() As String
        Public Property CurrencyCode() As String
        Public Property CancellationPolicy() As String
        Public Property Disclaimer() As String
        Public Property Text() As List(Of String)
        Public Property Commission() As String
        Public Property GuaranteeSurchargeRequired() As String
        Public Property IATACharacteristicIdentification() As String
        Public Property IATAProductIdentification() As String
        Public Property RPH() As String
    End Class
End Namespace

Now I'm not looking for someone to write all the code for me, I'm more looking for ideas on which would be the most efficient way to parse this into my class. I can use the XPathDocument and XPathNavigation & XPathNodeIterator classes to do this (not 100% sure how they work yet) but is there a better way to accomplish this task?

EDIT

I have come up with this so far, which should get me the first attributes in the RoomRate element

Return From el As XElement In _xDoc...<RoomRate>
               Select New RoomRate With { _
                   .GuaranteeSurchargeRequired = el...<RoomRate>.@GuaranteeSurchargeRequired, _
                   .IATACharacteristicIdentification = el...<RoomRate>.@IATACharacteristicIdentification, _
                   .IATAProductIdentification = el...<RoomRate>.@IATAProductIdentification, _
                   .RPH = el...<RoomRate>.@RPH}

Now how would I traverse to get say the cancellation policy, room rate and other attributes/elements within this code I provided? Sorry for so many questions, I really want to learn this the right way and be able to do it correctly.

EDIT #2

I think I'm on the right track here:

Return From el As XElement In _xDoc...<RoomRate>
           Select New RoomRate With { _
               .GuaranteeSurchargeRequired = el.@GuaranteeSurchargeRequired, _
               .IATACharacteristicIdentification = el.@IATACharacteristicIdentification, _
               .IATAProductIdentification = el.@IATAProductIdentification, _
               .RPH = el.@RPH, _
               .CancellationPolicy = el...<AdditionalInfo>...<CancellationPolicy>.@Numeric, _
               .Commission = el...<AdditionalInfo>...<Commission>.@NonCommission}

Can someone let me know if I'm on the right track for accomplishing this?

EDIT 3

I have changed my code to this

For Each n As XElement In _xDoc.Elements("RoomRate")
                    _rates.Add(New RoomRate With { _
                               .GuaranteeSurchargeRequired = n.Attribute("GuarateeSurchargeRequired").Value, _
                               .IATACharacteristicIdentification = n.Attribute("IATACharacteristicIdentification").Value, _
                               .IATAProductIdentification = n.Attribute("IATAProductIdentification").Value, _
                               .RPH = n.Attribute("RPH")})
                Next

And while I'm no longer getting any errors, but am not getting any results returned as well.

EDIT 4 I have made the following changes t my code, which should return an IEnumerable(Of RoomRate) but it's returning nothing (this is based on the XML snippet i posted above)

Dim rates = From rate In _xDoc.Descendants("RoomRate")
                Select New RoomRate With { _
                    .GuaranteeSurchargeRequired = rate.Attribute("GuaranteeSurchargeRequired").Value, _
                    .IATACharacteristicIdentification = rate.Attribute("IATACharacteristicIdentification").Value, _
                    .IATAProductIdentification = rate.Attribute("IATAProductIdentification").Value, _
                    .RPH = rate.Attribute("RPH").Value}

That looks like it should work, what am I doing wrong here?

EDIT #5 @Neolisk I made my changes the way you suggested, it now looks like so

For Each n As XElement In _xDoc.Descendants("RoomRate")
    rate = New RoomRate()
    rate.GuaranteeSurchargeRequired = n.Attribute("GuaranteeSurchargeRequired").Value
    rate.IATACharacteristicIdentification = n.Attribute("IATACharacteristicIdentification").Value
    rate.IATAProductIdentification = n.Attribute("IATAProductIdentification")
    _rates.Add(rate)
Next
Return _rates

But it never goes into the loop, it goes straight to Return _rates

EDIT #6

Ok I got it populating the first 3 attributes of the RoomRate element like this

Dim rate As RoomRate
Dim ns As XNamespace = "http://webservices.sabre.com/sabreXML/2003/07"
Dim rate As RoomRate
For Each n As XElement In _xDoc.Descendants(ns + "RoomRate")
    rate = New RoomRate()
    rate.GuaranteeSurchargeRequired = n.Attribute("GuaranteeSurchargeRequired").Value
    rate.IATACharacteristicIdentification = n.Attribute("IATACharacteristicIdentification").Value
    rate.IATAProductIdentification = n.Attribute("IATAProductIdentification").Value
    rate.RPH = n.Attribute("RPH").Value

    _rates.Add(rate)
Next

Now when I try to traverse through the rest of the document I try and get the CancelPolicy value like so

Dim rate As RoomRate
Dim ns As XNamespace = "http://webservices.sabre.com/sabreXML/2003/07"
Dim rate As RoomRate
For Each n As XElement In _xDoc.Descendants(ns + "RoomRate")
    rate = New RoomRate()
    rate.GuaranteeSurchargeRequired = n.Attribute("GuaranteeSurchargeRequired").Value
    rate.IATACharacteristicIdentification = n.Attribute("IATACharacteristicIdentification").Value
    rate.IATAProductIdentification = n.Attribute("IATAProductIdentification").Value
    rate.RPH = n.Attribute("RPH").Value
    rate.CancellationPolicy = n.Element("AdditionalInfo").Element("CancelPolicy").Attribute("Numeric").Value

    _rates.Add(rate)
Next

But adding that last line causes a NullReferenceException. How would I go about going through the XML now?

8
  • What version of .NET are you using? LINQ to XML is much easier than XPathDocument, especially in VB.NET, where you can use XML literals inline. Commented Feb 6, 2014 at 18:34
  • 1
    Then use LINQ to XML and ignore XPathDocument and XmlDocument. Commented Feb 6, 2014 at 18:36
  • I've not used LINQ to XML before, got an example (given the above XML and class) how I would accomplish this with LINQ to XML? Commented Feb 6, 2014 at 18:40
  • Regarding your edit: I would advise against using LINQ in this case, you will have a hard time debugging this if your XML ever changes, or you revise how you retrieve elements. Also don't use VB's LINQ-to-XML shorthand syntax (..., <>, @), better use Descendant(s), Element(s) and Attribute(s) functions - easier to understand this way. Without picking on the syntax, I think you need to add .Value at the end. Always check the retrieved object in debugger. Make sure the types are what you expect to have. I recommend having most simple types possible, i.e. integer, string etc. Commented Feb 8, 2014 at 20:10
  • 1
    @Neolisk My edit #3 doesn't generate any error any longer, but it's not returning any values. Also, I wanted to take the time to thank you for taking your time to walk me through this. Commented Feb 9, 2014 at 17:03

2 Answers 2

1

Got my issue resolved with this code

Public Function ParseRates() As IEnumerable(Of RoomRate)
    Try
        Dim ns As XNamespace = "http://webservices.sabre.com/sabreXML/2003/07"
        Dim rate As RoomRate
        For Each n As XElement In _xDoc.Descendants(ns + "RoomRate")
            _rates.Add(New RoomRate With { _
                        .GuaranteeSurchargeRequired = n.Attribute("GuaranteeSurchargeRequired").Value, _
                        .IATACharacteristicIdentification = n.Attribute("IATACharacteristicIdentification").Value, _
                        .IATAProductIdentification = n.Attribute("IATAProductIdentification").Value, _
                        .RPH = n.Attribute("RPH").Value, _
                        .CancellationPolicy = n.Element(ns + "AdditionalInfo").Element(ns + "CancelPolicy").Attribute("Numeric").Value, _
                        .Commission = n.Element(ns + "AdditionalInfo").Element(ns + "Commission").Attribute("NonCommission").Value, _
                        .Rate = n.Element(ns + "Rates").Element(ns + "Rate").Attribute("Amount").Value, _
                        .CurrencyCode = n.Element(ns + "Rates").Element(ns + "Rate").Attribute("CurrencyCode").Value,
                        .TotalPrice = n.Element(ns + "Rates").Element(ns + "Rate").Element(ns + "HotelTotalPricing").Attribute("Amount").Value, _
                        .Disclaimer = n.Element(ns + "Rates").Element(ns + "Rate").Element(ns + "HotelTotalPricing").Element(ns + "Disclaimer").Value, _
                        .Surcharge = n.Element(ns + "Rates").Element(ns + "Rate").Element(ns + "HotelTotalPricing").Element(ns + "TotalSurcharges").Attribute("Amount").Value})
        Next
        Return _rates
    Catch ex As Exception
        ErrorMessage = ex.Message
        Return Nothing
    End Try
End Function

Maybe someday I'll find a more efficient way to accomplish this

Sign up to request clarification or add additional context in comments.

Comments

0

If you've never dealt with LINQ to XML, you may find yourself struggling a bit at the beginning, but this should help you get there faster:

Dim xml As XDocument = XDocument.Parse("...") 'or XDocument.Load("...")

You can also assign a whole XML (or part thereof) into a variable AS IS, to test your concept, i.e. this is 100% valid syntax in VB10+ (~.NET 4.0+):

Dim xml As XDocument = <?xml version="1.0" encoding="utf-16"?>
                       <RoomRates>
                         <RoomRate>
                           <AdditionalInfo>
                             <Text>BLOOMBERG LP, 0.0 KM</Text>
                           </AdditionalInfo>
                           <Rates>
                             <Rate Amount="66.600" CurrencyCode="KWD"></Rate>
                           </Rates>
                         </RoomRate>
                       </RoomRates>

After that, here is what you can do:

Dim ns As XNamespace = "http://webservices.sabre.com/sabreXML/2003/07"
Dim roomRate As XElement = xml.Descendants(ns + "RoomRate").First

Dim additionalInfoText As String = roomRate.Descendants(ns + "Text").First

You can now inspect both roomRate and additionalInfoText in debugger, to see how it worked.

When in doubt about functionality of XDocument/XElement, please see relevant MSDN articles.

3 Comments

You're right I am struggling with this. It's a complex XML file and I'm struggling getting the attributes and elements into my class. Once I can get 1 in I can figure out how to loop through and get them all. Any help from anyone?
@PsychoCoder: The above did not help at all? If you have a question, feel free to ask. Without a question I cannot help you. To make the above code work, I pasted your XML as text into the code file, and added missing ending tags (to make it valid). I mean it's not just some code, it's the actual code for YOUR data, to showcase data retrieval mechanism using LINQ-to-XML. As you can imagine, I tested it to work in Visual Studio before posting here.
check my edit above, maybe that will clarify things more what I'm having issues with :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.