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

Kjartan
19.2k16 gold badges75 silver badges100 bronze badges
asked Apr 18, 2013 at 9:22
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() );
answered Apr 18, 2013 at 9:46
3
  • 1
    Hi Phil, sorry that throws this error. Accessed JObject values with invalid key value: 0. Object property name expected. Commented Apr 18, 2013 at 9:50
  • 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 Commented Apr 18, 2013 at 10:36
  • 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. Commented Apr 18, 2013 at 10:59
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; 
answered Sep 29, 2017 at 16:07
0
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"
answered Jul 17, 2022 at 7:39

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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.