0

I'm trying to work with LINQ to XML to parse the notifications I'm getting from Google Checkout.

The response is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<authorization-amount-notification xmlns="http://checkout.google.com/schema/2" serial-number="153286076708098-00005-6">
 <authorization-amount currency="USD">60.0</authorization-amount>
 <authorization-expiration-date>2011-07-03T21:27:48.000Z</authorization-expiration-date>
 <avs-response>Y</avs-response>
 <cvn-response>M</cvn-response>
 <timestamp>2011-06-26T21:28:48.741Z</timestamp>
 <google-order-number>153286076708098</google-order-number>
 <order-summary>
   <total-chargeback-amount currency="USD">0.0</total-chargeback-amount>
   <google-order-number>153286076708098</google-order-number>
   <total-charge-amount currency="USD">0.0</total-charge-amount>
   <total-refund-amount currency="USD">0.0</total-refund-amount>
   <risk-information>
     <ip-address>77.42.229.34</ip-address>
     <billing-address>
       <address1>somewhere in Beirut</address1>
       <address2></address2>
       <phone>70892555</phone>
       <email>[email protected]</email>
       <contact-name>Fisharwe User</contact-name>
       <company-name></company-name>
       <fax></fax>
       <country-code>LB</country-code>
       <city>Beirut</city>
       <region></region>
       <postal-code>1000</postal-code>
     </billing-address>
     <avs-response>Y</avs-response>
     <cvn-response>M</cvn-response>
     <eligible-for-protection>true</eligible-for-protection>
     <partial-cc-number>1111</partial-cc-number>
     <buyer-account-age>18</buyer-account-age>
   </risk-information>
   <authorization>
     <authorization-amount currency="USD">60.0</authorization-amount>
     <authorization-expiration-date>2011-07-03T21:27:48.000Z</authorization-expiration-date>
   </authorization>
   <purchase-date>2011-06-26T21:27:48.000Z</purchase-date>
   <archived>false</archived>
   <shopping-cart>
     <items>
       <item>
         <item-name>Credits</item-name>
         <item-description>Description</item-description>
         <unit-price currency="USD">60.0</unit-price>
         <quantity>1</quantity>
       </item>
     </items>
   </shopping-cart>
   <order-adjustment>
     <merchant-codes />
     <total-tax currency="USD">0.0</total-tax>
     <adjustment-total currency="USD">0.0</adjustment-total>
   </order-adjustment>
   <promotions />
   <buyer-id>975104325298289</buyer-id>
   <buyer-marketing-preferences>
     <email-allowed>false</email-allowed>
   </buyer-marketing-preferences>
   <buyer-shipping-address>
     <address1>somewhere in Beirut</address1>
     <address2></address2>
     <phone>70892555</phone>
     <email>[email protected]</email>
     <contact-name>Fisharwe User</contact-name>
     <company-name></company-name>
     <fax></fax>
     <structured-name>
       <first-name>Fisharwe</first-name>
       <last-name>User</last-name>
     </structured-name>
     <country-code>LB</country-code>
     <city>Beirut</city>
     <region></region>
     <postal-code>1000</postal-code>
   </buyer-shipping-address>
   <order-total currency="USD">60.0</order-total>
   <fulfillment-order-state>NEW</fulfillment-order-state>
   <financial-order-state>CHARGEABLE</financial-order-state>
 </order-summary>
</authorization-amount-notification>

Here's the code I'm using:

        var serverResponse = _checkoutService.Post(data, GoogleCheckoutConstants.ReportsUri);
        var xmlData = XDocument.Parse(serverResponse);
        bool charged = false;
        if(xmlData.Root.Name.Equals("authorization-amount-notification"))
        {

            var amount = (from c in xmlData.Elements()
                          where c.Name.Equals("authorization-amount")
                          select c).First().Value;
            var googleNumber = (from c in xmlData.Elements()
                                where c.Name.Equals("google-order-number")
                                select c).First().Value;
            _checkoutService.ChargeAndShip(googleNumber, amount);
            charged = true;
        }

This is the first time I use LINQ to XML, so I'm not really sure what's wrong with my code. But it's not even going inside the if statement. So when I replace the condition with:

if (serverResponse.IndexOf("authorization-amount-notification") > -1)

I end up getting errors telling me that the amount and googleNumber were not found.

Any suggestions?

Kassem
  • 8,116
  • 17
  • 75
  • 116
  • Where is the closing tag for `new-order-notification` ? – leppie Jun 27 '11 at 11:13
  • Maybe you have query with `xmlData.Elements("authorization-amount")` – V4Vendetta Jun 27 '11 at 11:13
  • 1
    Does the XML use Namespaces? You may need to add them into the query : http://stackoverflow.com/questions/2340411/use-linq-to-xml-with-xml-namespaces. Also try examining the elements using Quick Watch and see what they say. – DaveShaw Jun 27 '11 at 11:14
  • 1
    Actually, it seems the response XML has been truncated. I dont see `authorization-amount` or `authorization-amount-notification`. – leppie Jun 27 '11 at 11:15
  • You will have use `XNamespace` and append the same when you query for elements and by the way where is the root ? – V4Vendetta Jun 27 '11 at 11:20
  • Oh sorry about that, I copied the wrong XML block. Now I got it fixed. But anyway, even though I'm getting the correct XML, it doesn't seem to recognize the nodes... – Kassem Jun 27 '11 at 12:15

2 Answers2

2

You need to put the namespace in to the Xml, and you the Elements are SubElements of the Root Node.

You are only after one Element so doing Elements() then .First() is pointless. Just do Element() instead.

Also, you can match element names by passing in the Name of the Element + namespace to the Element() method.

var xmlData = XDocument.Parse(xml);

XNamespace ns = "http://checkout.google.com/schema/2";

if (xmlData.Root.Name == ns + "authorization-amount-notification")
{ 
    var amount = 
        xmlData
        .Root
        .Element(ns + "authorization-amount")
        .Value;

    var googleNumber = 
        xmlData
        .Root
        .Element(ns + "google-order-number")
        .Value;  
    _checkoutService.ChargeAndShip(googleNumber, amount);             

    charged = true;
}
DaveShaw
  • 52,123
  • 16
  • 112
  • 141
  • @DaveShaw: I'm still getting the same error `Sequence contains no elements` – Kassem Jun 27 '11 at 12:39
  • @Kassem, sorry, I didn't run it. I have fixed the code above. The Elements are under the Root node, so you needed to So Root.Element. – DaveShaw Jun 27 '11 at 12:54
  • @DaveShaw: How do you get to nodes that are not direct children of the root? I tried `username = xmlData.Root.Element(ns + "shopping-cart").Element(ns + "merchant-private-data").Value;` but it did not work for me... Any ideas? – Kassem Jun 28 '11 at 11:37
  • 1
    You are missing a step in the hierarchy, you need to go down each element from the root: xmlData.Root.Element(ns + "order-summary").Element(ns + "shopping-cart").Elements(ns + "items"); – DaveShaw Jun 28 '11 at 11:43
  • If you grab yourself a copy of LinqPad you can use the .Dump() extension method to debug what's going wrong... When I get stuck with my L2Xml I just move up a few levels and see what's there and where I am going wrong. That's the tool I used for debugging your issue. – DaveShaw Jun 28 '11 at 12:07
0

What about...

if(xmlData.Root.Name.LocalName.Equals("new-order-notification")){
 .... 
}

But the xml you posted doesn't seem to match the code your using.. The elements do not exist

TheRealTy
  • 2,409
  • 3
  • 22
  • 32
  • I fixed my post. I copied the wrong XML block, now I've got the right one in my post. Sorry about that. – Kassem Jun 27 '11 at 12:16