PsychoCoder
PsychoCoder

Reputation: 10765

Can this be shortened (LINQ to XML with VB.NET)?

I have the following code, which works by the way, but am working on making it shorter and using XML shorthand. All is fine thus far but is there a way to shorten the last few items, seems like an awful lot of Element calls:

Public Function ParseRates() As IEnumerable(Of RoomRate)
    Try
        For Each n As XElement In _xDoc...<_ns:RoomRate>
            _rates.Add(New RoomRate With { _
                        .GuaranteeSurchargeRequired = n.@GuaranteeSurchargeRequired, _
                        .IATACharacteristicIdentification = n.@IATACharacteristicIdentification, _
                        .IATAProductIdentification = n.@IATAProductIdentification, _
                        .RPH = n.@RPH, _
                        .CancellationPolicy = n...<_ns:AdditionalInfo>.<_ns:CancelPolicy>.@Numeric, _
                        .Commission = n...<_ns:AdditionalInfo>.<_ns:Commission>.@NonCommission, _
                        .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

Here's a snippet of the XML that's being parsed with said code

<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>
</RoomRates>
</HotelPropertyDescriptionRS>

Upvotes: 0

Views: 86

Answers (2)

MarcinJuraszek
MarcinJuraszek

Reputation: 125650

How about using AddRange and LINQ query inside it?

You can use Let to simplify your query a little:

Dim query = From n In _xDoc...<_ns:RoomRate>
            Let additionalInfo = n...<_ns:AdditionalInfo>
            Let rate = n.<_ns:Rates>.<_ns:Rate>
            Let hotelPricing = rate.<_ns:HotelTotalPricing>
            Select New RoomRate With {
                .GuaranteeSurchargeRequired = n.@GuaranteeSurchargeRequired, _
                .IATACharacteristicIdentification = n.@IATACharacteristicIdentification, _
                .IATAProductIdentification = n.@IATAProductIdentification, _
                .RPH = n.@RPH, _
                .CancellationPolicy = additionalInfo.<_ns:CancelPolicy>.@Numeric, _
                .Commission = additionalInfo.<_ns:Commission>.@NonCommission, _
                .Rate = rate.@Amount, _
                .CurrencyCode = rate.@CurrencyCode,
                .TotalPrice = hotelPricing.@Amount, _
                .Disclaimer = hotelPricing.<_ns:Disclaimer>.Value, _
                .Surcharge = hotelPricing.<_ns:TotalSurcharges>.@Amount
            }

It requires namespace import declaration:

Imports <xmlns:_ns="test">

and you should be able to call AddRange on _rates:

_rates.AddRange(query)

Upvotes: 1

har07
har07

Reputation: 89315

Speaking about this part :

.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})

there are some other ways to get those 5 values. First, using .Descendants() and get the first element returned :

.Rate = n.Descendants(_ns + "Rate").First().Attribute("Amount").Value, _
.CurrencyCode = n.Descendants(_ns + "Rate").First().Attribute("CurrencyCode").Value,
.TotalPrice = n.Descendants(_ns + "HotelTotalPricing").First().Attribute("Amount").Value, _
.Disclaimer = n.Descendants(_ns + "Disclaimer").First().Value, _
.Surcharge = n.Descendants(_ns + "TotalSurcharges").First().Attribute("Amount").Value})

.Descendants() can bring you any child of current element when Element() only search direct child of current element. Other option is by using XPath :

.Rate = n.XPathSelectElement(".//n:Rate", nsm).Attribute("Amount").Value, _
.CurrencyCode = n.XPathSelectElement(".//n:Rate", nsm).Attribute("CurrencyCode").Value,
.TotalPrice = n.XPathSelectElement(".//n:HotelTotalPricing", nsm).Attribute("Amount").Value, _
.Disclaimer = n.XPathSelectElement(".//n:Disclaimer", nsm).Value, _
.Surcharge = n.XPathSelectElement(".//n:TotalSurcharges", nsm).Attribute("Amount").Value

in this case, you need to define prefix to be used in XPath expression first :

Dim nsm As XmlNamespaceManager = New XmlNamespaceManager(New NameTable)
nsm.AddNamespace("n", "http://webservices.sabre.com/sabreXML/2003/07")

And add Imports :

Imports System.Xml.XPath

Switching from method style to XPath is simple. This is basics about XPath syntax :

  1. dot (.) in fornt of expression tell to search from current node. Without it expression will be evaluated from root node
  2. double-slash (//) is analog to Descendants()
  3. single-slash (/) is analog to Element() or Elements()

Upvotes: 1

Related Questions