15

I'm really stuck on this problem now for 2 days, how can I get the data out of a deeply nested json object.

I have found an online json tools http://www.jsoneditoronline.org/ http://jsonformat.com/ which when you paste your json into it, shows all the objects arrays etc, so I can dig down into the data and get the information I want.

When I debug the code and put a break point on: foreach (JToken data in rates.ToArray()) I can see the data I'm after, I just cannot get the data out, depends on what I try depends on the error I get, last error was.

Error converting value "@rateChange" to type 'Web.UI.Controllers.HomeController+RateInfo'.

Could not cast or convert from System.String to Web.UI.Controllers.HomeController+RateInfo.

Any help with this is much appreciated.

My class

public class RateInfo
        {
            public string RateChange { get; set; }
            public string Promo { get; set; }
            public string PriceBreakdown { get; set; }
            public bool NonRefundable { get; set; }
            public string RateType { get; set; }
            public int CurrentAllotment { get; set; }
            public int? PromoId { get; set; }
            public string PromoDescription { get; set; }
            public string PromoType { get; set; }
        }

code

 IList<JToken> rates = root["HotelListResponse"]["HotelList"]["HotelSummary"][0]["RoomRateDetailsList"]["RoomRateDetails"]["RateInfos"]["RateInfo"].Children().ToList();


                        IList<RateInfo> info = new List<RateInfo>();
                        foreach (JToken data in rates.ToArray())
                        {
                            RateInfo rateInfo = JsonConvert.DeserializeObject<RateInfo>(data.ToString());
                            info.Add(rateInfo);
                        }

Json

{ "HotelListResponse" : { "HotelList" : { "@activePropertyCount" : "168",
          "@size" : "2",
          "HotelSummary" : [ { "@order" : "0",
                "@ubsScore" : "360017",
                "RoomRateDetailsList" : { "RoomRateDetails" : { "RateInfos" : { "@size" : "1",
                            "RateInfo" : { "@priceBreakdown" : "true",
                                "@promo" : "true",
                                "@rateChange" : "true",
                                "ChargeableRateInfo" : { "@averageBaseRate" : "68.62333",
                                    "@averageRate" : "68.62333",
                                    "@commissionableUsdTotal" : "205.87",
                                    "@currencyCode" : "USD",
                                    "@grossProfitOffline" : "14.06",
                                    "@grossProfitOnline" : "27.44",
                                    "@maxNightlyRate" : "77.87",
                                    "@nightlyRateTotal" : "205.87",
                                    "@total" : "205.87",
                                    "NightlyRatesPerRoom" : { "@size" : "3",
                                        "NightlyRate" : [ { "@baseRate" : "77.87",
                                              "@promo" : "false",
                                              "@rate" : "77.87"
                                            },
                                            { "@baseRate" : "64.0",
                                              "@promo" : "false",
                                              "@rate" : "64.0"
                                            },
                                            { "@baseRate" : "64.0",
                                              "@promo" : "false",
                                              "@rate" : "64.0"
                                            }
                                          ]
                                      }
                                  },
                                "RoomGroup" : { "Room" : { "numberOfAdults" : 2,
                                        "numberOfChildren" : 0,
                                        "rateKey" : "f82ab843-49ee-481a-b53a-71647592b183"
                                      } },
                                "currentAllotment" : 0,
                                "nonRefundable" : true,
                                "promoDescription" : "Advance Purchase Special - non-refundable",
                                "promoId" : 200827770,
                                "promoType" : "Standard",
                                "rateType" : "MerchantStandard"
                              }
                          },
                        "ValueAdds" : { "@size" : "1",
                            "ValueAdd" : { "@id" : "2048",
                                "description" : "Free Wireless Internet"
                              }
                          },
                        "expediaPropertyId" : 3084588,
                        "maxRoomOccupancy" : 3,
                        "minGuestAge" : 0,
                        "propertyAvailable" : true,
                        "propertyRestricted" : false,
                        "quotedRoomOccupancy" : 2,
                        "rateCode" : 200371945,
                        "roomDescription" : "Standard Room with King size bed",
                        "roomTypeCode" : 477014
                      } },
                "address1" : "Stone Cellar Road",
                "address2" : "High Usworth Newcastle",
                "airportCode" : " ",
                "amenityMask" : 18063491,
                "city" : "Washington",
                "confidenceRating" : 90,
                "countryCode" : "GB",
                "deepLink" : "http://travel.ian.com/index.jsp?pageName=hotAvail&cid=55505&hotelID=340461&mode=2&numberOfRooms=1&room-0-adult-total=2&room-0-child-total=0&arrivalMonth=11&arrivalDay=12&departureMonth=11&departureDay=15&showInfo=true&locale=en_US&currencyCode=USD",
                "highRate" : 77.870000000000005,
                "hotelId" : 340461,
                "hotelInDestination" : true,
                "hotelRating" : 3,
                "latitude" : 54.922739999999997,
                "locationDescription" : "Near Washington Old Hall",
                "longitude" : -1.5342899999999999,
                "lowRate" : 64,
                "name" : "Mercure Newcastle George Washington Hotel Golf and Spa",
                "postalCode" : "NE37 1PH",
                "propertyCategory" : 1,
                "proximityDistance" : 1.4710813,
                "proximityUnit" : "MI",
                "rateCurrencyCode" : "USD",
                "shortDescription" : "<p><b>Location. </b> <br />Mercure Newcastle George Washington Hotel Golf and Spa is a business-friendly hotel located in Washington, close to Washington Old Hall, Angel of the North, and WWT",
                "supplierType" : "E",
                "thumbNailUrl" : "/hotels/4000000/3090000/3084600/3084588/3084588_84_t.jpg",
                "tripAdvisorRating" : 3.5,
                "tripAdvisorRatingUrl" : "http://www.tripadvisor.com/img/cdsi/img2/ratings/traveler/3.5-12345-4.gif",
                "tripAdvisorReviewCount" : 215
              },
              { "@order" : "1",
                "@ubsScore" : "258461",
                "RoomRateDetailsList" : { "RoomRateDetails" : { "RateInfos" : { "@size" : "1",
                            "RateInfo" : { "@priceBreakdown" : "true",
                                "@promo" : "false",
                                "@rateChange" : "true",
                                "ChargeableRateInfo" : { "@averageBaseRate" : "54.83667",
                                    "@averageRate" : "54.83667",
                                    "@commissionableUsdTotal" : "164.51001",
                                    "@currencyCode" : "USD",
                                    "@grossProfitOffline" : "11.69",
                                    "@grossProfitOnline" : "22.38",
                                    "@maxNightlyRate" : "63.47",
                                    "@nightlyRateTotal" : "164.51001",
                                    "@total" : "164.51",
                                    "NightlyRatesPerRoom" : { "@size" : "3",
                                        "NightlyRate" : [ { "@baseRate" : "50.52",
                                              "@promo" : "false",
                                              "@rate" : "50.52"
                                            },
                                            { "@baseRate" : "50.52",
                                              "@promo" : "false",
                                              "@rate" : "50.52"
                                            },
                                            { "@baseRate" : "63.47",
                                              "@promo" : "false",
                                              "@rate" : "63.47"
                                            }
                                          ]
                                      }
                                  },
                                "RoomGroup" : { "Room" : { "numberOfAdults" : 2,
                                        "numberOfChildren" : 0,
                                        "rateKey" : "f82ab843-49ee-481a-b53a-71647592b183"
                                      } },
                                "currentAllotment" : 0,
                                "nonRefundable" : true,
                                "rateType" : "MerchantStandard"
                              }
                          },
                        "expediaPropertyId" : 901118,
                        "maxRoomOccupancy" : 2,
                        "minGuestAge" : 0,
                        "propertyAvailable" : true,
                        "propertyRestricted" : false,
                        "quotedRoomOccupancy" : 2,
                        "rateCode" : 200369466,
                        "roomDescription" : "Standard room with double bed - Book early & Save",
                        "roomTypeCode" : 162976
                      } },
                "address1" : "Emerson Road",
                "address2" : "District 5",
                "airportCode" : "NCL",
                "amenityMask" : 1507328,
                "city" : "Washington",
                "confidenceRating" : 85,
                "countryCode" : "GB",
                "deepLink" : "http://travel.ian.com/index.jsp?pageName=hotAvail&cid=55505&hotelID=207631&mode=2&numberOfRooms=1&room-0-adult-total=2&room-0-child-total=0&arrivalMonth=11&arrivalDay=12&departureMonth=11&departureDay=15&showInfo=true&locale=en_US&currencyCode=USD",
                "highRate" : 63.469999999999999,
                "hotelId" : 207631,
                "hotelInDestination" : true,
                "hotelRating" : 2,
                "latitude" : 54.895090000000003,
                "locationDescription" : "Near Washington Old Hall",
                "longitude" : -1.55661,
                "lowRate" : 50.520000000000003,
                "name" : "Campanile Washington Newcastle Upon Tyne",
                "postalCode" : "NE37 1LB",
                "propertyCategory" : 1,
                "proximityDistance" : 1.2526573000000001,
                "proximityUnit" : "MI",
                "rateCurrencyCode" : "USD",
                "shortDescription" : "<p><b>Location. </b> <br />Campanile Washington Newcastle Upon Tyne is located in Washington, close to Washington Old Hall, Angel of the North, and WWT Washington Wetland Centre. Additional area",
                "supplierType" : "E",
                "thumbNailUrl" : "/hotels/1000000/910000/901200/901118/901118_20_t.jpg",
                "tripAdvisorRating" : 3.5,
                "tripAdvisorRatingUrl" : "http://www.tripadvisor.com/img/cdsi/img2/ratings/traveler/3.5-12345-4.gif",
                "tripAdvisorReviewCount" : 55
              }
            ]
        },
      "cacheKey" : "4ef59f3e:13e1c495694:-6e28",
      "cacheLocation" : "10.186.168.74:7301",
      "cachedSupplierResponse" : { "@cachedTime" : "0",
          "@candidatePreptime" : "100",
          "@matchedCurrency" : "true",
          "@matchedLocale" : "true",
          "@otherOverheadTime" : "3",
          "@supplierCacheTolerance" : "MED",
          "@supplierRequestNum" : "118",
          "@supplierResponseNum" : "2",
          "@supplierResponseTime" : "468",
          "@tpidUsed" : "5200"
        },
      "customerSessionId" : "0ABAA84A-59F3-E913-E1C2-495694906E33",
      "moreResultsAvailable" : true,
      "numberOfRoomsRequested" : 1
    } } 

=============================This code works getting data out of summary array============

public class Hotelsummary
    {
        public string Name                  { get; set; }
        public string Address1              { get; set; }
        public string Address2              { get; set; }
        public string City                  { get; set; }
        public string PostalCode            { get; set; }
        public string CountryCode           { get; set; }
        public string ThumbNailUrl          { get; set; }
        public string ShortDescription      { get; set; }
        public double HotelRating           { get; set; }
        public double TripAdvisorRating     { get; set; }
        public string RateCurrencyCode      { get; set; }
    }


public IEnumerable<Hotelsummary> GetHotelsForLocationSearch()
        {
            var hotelDetails = GetHotelsFromEan.GetListOfHotels();
            var root = JObject.Parse(hotelDetails.ToString());
            IList<JToken> hotels = root["HotelListResponse"]["HotelList"]["HotelSummary"].Children().ToList();
            IList<Hotelsummary> hotelsummaries = hotels.Select(result => JsonConvert
                                                                             .DeserializeObject<Hotelsummary>(
                                                                                 result.ToString())).ToList();
            return hotelsummaries;
        }

But as soon as I try digging deeper into data, I cannot get the data I needs

7
  • If you're looking at this in the debugger, what are actually the elements of rates. That is, is the huge expression at the start correct? Commented Apr 18, 2013 at 9:26
  • Hi @millimoose yes the expression is correct, the json is what gets returned from remote service Commented Apr 18, 2013 at 9:33
  • This line of code is what throws the error. RateInfo rateInfo = JsonConvert.DeserializeObject<RateInfo>(data.ToString()); Commented Apr 18, 2013 at 9:34
  • By "is it correct?", I didn't mean "does it throw an error?" I meant actually look at the unserialised data in the debugger and see if you're getting the objects you expect to be getting. Commented Apr 18, 2013 at 9:54
  • Hi @millimoose i can see the data in the debugger and it is the objects i expect, once I get to RateInfo rateInfo = JsonConvert.DeserializeObject<RateInfo>(data.ToString()); it throws the error Commented Apr 18, 2013 at 9:57

3 Answers 3

27

Try this instead:

IList<JToken> rates = root["HotelListResponse"]["HotelList"]["HotelSummary"][0]["RoomRateDetailsList"]["RoomRateDetails"]["RateInfos"].Children().ToList();

EDIT:

var rateInfo = json["HotelListResponse"]["HotelList"]["HotelSummary"][0]["RoomRateDetailsList"]["RoomRateDetails"]["RateInfos"]["RateInfo"];

var result =JsonConvert.DeserializeObject<RateInfo>( rateInfo .ToString() );
Sign up to request clarification or add additional context in comments.

3 Comments

Hi Phil, sorry that throws this error. Accessed JObject values with invalid key value: 0. Object property name expected.
Hi @Phil yes, it is now returning the results, thank you very very much. If you have the time, could you please tell me how i would loop through the HotelSummary arrays and get all the data, as the data returned has muliple HotelSummary arrays. No problem if you cannot, just thank you again for fixing this problem
I would create a nested class structure that duplicates the json. Then convert the root element and access it as you would a normal object graph. e.g. The HotelSummary object would have a RoomRateDetail object, etc.
10

This extension method uses recursion to loop through a deep nested json and find the value for a jProperty.

      public static TType JsonValue<TType>(this JObject obj, string key)
    {
        object result = null; //default to null if nothing is found

        foreach (var item in obj)
        {
            var token = item;

            if (token.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase))
            {
                result = token.Value.ToObject<TType>(); //return the value found
                break;
            }

            if (!obj[token.Key].Children().Any())
                continue;

                var jt = obj[token.Key].ToString();

                if (!jt.StartsWith("["))
                {
                  result = JsonValue<TType>(JObject.Parse(jt), key);
                }
                else
                {
                    obj[token.Key].Children().ToList().ForEach(x =>
                    {
                        //only the first match will be returned
                        result = JsonValue<TType>(JObject.Parse(x.ToString()), key);
                    });
                }

            if (result != null)
                break;

        }

       return (TType)result;
    }

How to use:

    var myValue = jsonObject.JsonValue<string>("propName");
    var numbValue = jsonObject.JsonValue<long?>("propName2") ?? 0; 

Comments

0
public JToken GetNestedJsonData(JToken json, string path, string defaultValue)
    {   
        //Using `:` as a key separator to access nested keys
        var parts = path.Split(':');

        if(parts.Length == 1)
        {
            return json[parts[0]] ?? defaultValue;
        }

        if (parts.Length > 1 && json[parts[0]]?.Type == JTokenType.Object)
        {
            return GetNestedJsonData(json[parts[0]], string.Join(":", parts[1..^0]), defaultValue);
        }
        return json[parts[0]] ?? defaultValue;
    }

Example

string json = "{\"PZLAD_LocationDetails\":{\"ShortDesc\":\"TEST\",\"AddLine1\":\"LOCATION LINE 1\",\"AddLine2\":\"LOCATION LINE 2\",\"AddLine3\":\"LOCATION LINE 3\",\"AddLine4\":\"LOCATION LINE 4\",\"AddLine5\":\"LOCATION LINE 5\",\"State\":\"UDL\",\"Cresta\":\"10001\",\"AccReg\":\"U33000001\",\"Postcode\":\"1\",\"EQ\":\"01\",\"Test\":{\"Name\":\"Pravin\"}}}";

JObject d = JsonConvert.DeserializeObject<JObject>(json);

//Using `:` as a key separator to access nested keys
string key = "PZLAD_LocationDetails:Test:Name";

var value = GetNestedJsonData(d, key, "");
//Expected value is in string. You can try with any JToken
Console.WriteLine(value.toString());
//Output "Pravin"

Comments

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.