I m using the Google Place Api Json:
{
"html_attributions": ["Listings by \u003ca href=\"http://www.openrice.com/\"\u003eOpenRice\u003c/a\u003e"],
"result": {
"address_components": [{
"long_name": "Bayfield Building",
"short_name": "Bayfield Building",
"types": ["premise"]
}, {
"long_name": "99",
"short_name": "99",
"types": ["street_number"]
}, {
"long_name": "Hennessy Road",
"short_name": "Hennessy Rd",
"types": ["route"]
}, {
"long_name": "Wan Chai",
"short_name": "Wan Chai",
"types": ["neighborhood", "political"]
}, {
"long_name": "Hong Kong Island",
"short_name": "Hong Kong Island",
"types": ["administrative_area_level_1", "political"]
}, {
"long_name": "Hong Kong",
"short_name": "HK",
"types": ["country", "political"]
}],
"adr_address": "Bayfield Building, \u003cspan class=\"street-address\"\u003e99 Hennessy Rd\u003c/span\u003e, \u003cspan class=\"locality\"\u003eWan Chai\u003c/span\u003e, \u003cspan class=\"country-name\"\u003eHong Kong\u003c/span\u003e",
"formatted_address": "Bayfield Building, 99 Hennessy Rd, Wan Chai, Hong Kong",
"formatted_phone_number": "2529 3338",
"geometry": {
"location": {
"lat": 22.2775876,
"lng": 114.1719815
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id": "b787af8ba2c2037cb18f4c9ccfa6686c58637981",
"international_phone_number": "+852 2529 3338",
"name": "Happy Veggies",
"opening_hours": {
"open_now": false,
"periods": [{
"close": {
"day": 0,
"time": "2300"
},
"open": {
"day": 0,
"time": "1030"
}
}, {
"close": {
"day": 1,
"time": "1430"
},
"open": {
"day": 1,
"time": "1130"
}
}, {
"close": {
"day": 1,
"time": "2200"
},
"open": {
"day": 1,
"time": "1800"
}
}, {
"close": {
"day": 2,
"time": "1430"
},
"open": {
"day": 2,
"time": "1130"
}
}, {
"close": {
"day": 2,
"time": "2200"
},
"open": {
"day": 2,
"time": "1800"
}
}, {
"close": {
"day": 3,
"time": "1430"
},
"open": {
"day": 3,
"time": "1130"
}
}, {
"close": {
"day": 3,
"time": "2200"
},
"open": {
"day": 3,
"time": "1800"
}
}, {
"close": {
"day": 5,
"time": "1430"
},
"open": {
"day": 5,
"time": "1130"
}
}, {
"close": {
"day": 5,
"time": "2200"
},
"open": {
"day": 5,
"time": "1800"
}
}, {
"close": {
"day": 6,
"time": "1430"
},
"open": {
"day": 6,
"time": "1130"
}
}, {
"close": {
"day": 6,
"time": "2200"
},
"open": {
"day": 6,
"time": "1800"
}
}],
"weekday_text": ["Monday: 11:30 AM – 2:30 PM, 6:00 – 10:00 PM", "Tuesday: 11:30 AM – 2:30 PM, 6:00 – 10:00 PM", "Wednesday: 11:30 AM – 2:30 PM, 6:00 – 10:00 PM", "Thursday: Closed", "Friday: 11:30 AM – 2:30 PM, 6:00 – 10:00 PM", "Saturday: 11:30 AM – 2:30 PM, 6:00 – 10:00 PM", "Sunday: 11:30 AM – 2:30 PM"]
},
"photos": [{
"height": 1373,
"html_attributions": ["\u003ca href=\"https://maps.google.com/maps/contrib/109496796118538295490/photos\"\u003eHappy Veggies\u003c/a\u003e"],
"photo_reference": "CoQBcwAAAEQZPA8WYu4ICwr--n4gCE7uyinkzAdRguvnTLHJD2xiqgUbmzkiMjP_MyzKt4Vdtb46wrLIC33LmvZqKB9avWMEjeSioVgAFPapKOlzpZKfEKEeapW5dQw7_Tx3f5QHrmN2VGQx3DCVS7BKi-wxfJNzbHwpHHIlHHUUlR_XOqDwEhDjmrQwOmJG1fndPu9BKsJ4GhT2PlTXf6cbqF8gUPDm8SMAnXw3WQ",
"width": 1373
}, {
"height": 1536,
"html_attributions": ["\u003ca href=\"https://maps.google.com/maps/contrib/109496796118538295490/photos\"\u003eHappy Veggies\u003c/a\u003e"],
"photo_reference": "CoQBcwAAAMJJ7kMtGwTU9s0ovCjH6jT3BbPTrL88hXGWSeKSKH4VdBcWV3NBqw3OekIy1UGyeGzYxbxbgTVyr8ICxyT2uK0R8fTyFqd_EyJPGzlLa8pU3l3uX3eJRD24nUscVGukojyo02u_R0r56bHBTxbcm5pjtHQPdY0fhlZ1-e8v9MoYEhCOHRNLFUGv5WYYfJnn-_d1GhR7OhmJsrYolDf4VBl-EuktkM-5Dw",
"width": 2048
}, {
"height": 497,
"html_attributions": ["\u003ca href=\"https://maps.google.com/maps/contrib/109496796118538295490/photos\"\u003eHappy Veggies\u003c/a\u003e"],
"photo_reference": "CoQBcwAAAA0pkWFzMfsRY-MJEzoXkdj4IN5fPEg1eeQz6kjH2rVHN2yJc7VeO7_sG718W-WMAwTHZYhs_fNA8yB5NxemJxsgDsgcOEYhZ-wUxFhgI6Hoo0fisOIhF5q84YhV5SukNbX8mHJvYSLzphEt_gT41HZlnAMOsLe1eWEdSL2S8n1REhChG5m3Q8EBDNwrSWYPmZe4GhTV0wssb2CdwQPiaWVNdE9XJMEonQ",
"width": 471
}, {
"height": 426,
"html_attributions": ["\u003ca href=\"https://maps.google.com/maps/contrib/109496796118538295490/photos\"\u003eHappy Veggies\u003c/a\u003e"],
"photo_reference": "CoQBcwAAAALlkDVQcNsLN7im-tGd0U7TDchQkcsfn_0M1bHNs2yYozzsnhlzdUPiKRjZFgP_KnYG81w4suNMmee1GFNXtnXNmv1wybFuQ8-xOHd_sGpDDl5zNlKXOH9AcnXzQdsNmMFycxHYdK6rYhO4kOo7YLtt6lF7WgtDgmlBYqUE-yoXEhD-W8FUa2t3jwCeXpPRv_dEGhQ0gVhjmGyq87QIGk_Bowrgmv7nEA",
"width": 640
}, {
"height": 1536,
"html_attributions": ["\u003ca href=\"https://maps.google.com/maps/contrib/109496796118538295490/photos\"\u003eHappy Veggies\u003c/a\u003e"],
"photo_reference": "CoQBcwAAACEn-ildhEyOgoPbwQGMFPkbFebvqhlahs0BOtZJ9tCw0tIJJ4adZZqyjupnXzNdPr19pLS56KroZdZ1ZNCMaJTypmI5lBH5Z2lfXrHjbVHsFe-s_gPar4A39O-9YPaQZv8CqYFx-i1x4YDz8UP-CQP1ftFmGtxk7751yeK6vUcgEhDt1WS1dybROHbC-Gs2PmLBGhQ-0lnbTr_PAMviMNyPbCxrIZmL2A",
"width": 2048
}, {
"height": 2048,
"html_attributions": ["\u003ca href=\"https://maps.google.com/maps/contrib/109496796118538295490/photos\"\u003eHappy Veggies\u003c/a\u003e"],
"photo_reference": "CoQBcwAAAEzl1eyD61BiO4HsQx7A5jpO9i67UrIfXzfoGEueaDrJj0FgOZVgXNpYsN9nNVVF6zasWLe8aSbxgeuGlGTdsk6U6pS1zcNkxU1iqWCCQcxcCGLvYO_C2GR3qoDn-UODSam0r86U6aSdueqzy8kGahLCN2zcK8ctwpl0w1b7EZg3EhC28QM7UPm1VTsWOyYFpngbGhREwyF8qyITAlgwN-SG83V-lK2Tcg",
"width": 1536
}, {
"height": 617,
"html_attributions": ["\u003ca href=\"https://maps.google.com/maps/contrib/109496796118538295490/photos\"\u003eHappy Veggies\u003c/a\u003e"],
"photo_reference": "CoQBcwAAADWrOhfim4ZmsbNrq2Hh816DEuVuO6IVNizs5tGL9B9F9S2_nK73Wf06MAlLP1TJoAmXtxhgtSKOz9cb4BBmt8pvn7panyVON4WbdI3WPwkMrIBwAs7EH-YT4UR3iAXZAHKi8i6EOlsAqYE9vaZRBDuN-z1gdr2N1DF8docak42YEhAzby18TkQgTCcGFNxsh53bGhThKRyxqQsOCEE6dRvmcLPxaARfug",
"width": 823
}, {
"height": 656,
"html_attributions": ["\u003ca href=\"https://maps.google.com/maps/contrib/109496796118538295490/photos\"\u003eHappy Veggies\u003c/a\u003e"],
"photo_reference": "CoQBcwAAAPigk5WL5k_ygcIcbOmLj2RdvhoUnNVvtxw4IibLoOcm4iPUNVvWHs6Tisk9DCat2isgu4tb9WClk2y9N7-TLhlYPZc3hNtQode7bLMVA38_xJjDP4N9lOF5SJOmdqV2GQJubAydGt2ilHttRfRF3CJC7mg78flSSBbz2qAX6mBgEhCxUYB4wcO7cpUTUFdqFEVmGhRQHHccNROeTdlqlfRZ7c0_KJjduQ",
"width": 873
}, {
"height": 612,
"html_attributions": ["\u003ca href=\"https://maps.google.com/maps/contrib/109496796118538295490/photos\"\u003eHappy Veggies\u003c/a\u003e"],
"photo_reference": "CoQBcwAAAIysZFfMl2raeOg7mfjM6JUYSolIXV_eHz3_bgF7TEjowLiewiX5wOuQ1sEjpQ0awnie7j3dLuD83xfeWkWjbWkH2S8n7FAIp50kTX8qHt7CPNalngIIC7L897TG0-R-r038G19lon3WTpax3Jnbnl5KZR0DwO-kwW91KDiZ4evWEhDH8uY-V5s7GQ7eC6ItEX84GhQsPl_-wgk4gH4-oXCY_HXS__2iTA",
"width": 816
}, {
"height": 578,
"html_attributions": ["\u003ca href=\"https://maps.google.com/maps/contrib/109496796118538295490/photos\"\u003eHappy Veggies\u003c/a\u003e"],
"photo_reference": "CoQBcwAAAB956t7YS0IV9YDIdetEEoyqXwMjTLJyx1nRZnC-VMrBrkwh_K-LopmYCcL3QsOFEPHUm6VM4IxduGDNGxglkwJN_ITfI8RbZkTWzRJOTtKM6IMfa1BZYTBDu4GKOcYEYsWMapIhyRow8VzqBXnBy7KoFCoH9et-QS0cglZbsK2sEhAtZKNN1uMe2m2IqrV8fDKnGhQ1xxTzthWr219DDWNjzMdVPUMPiw",
"width": 582
}],
"place_id": "ChIJn3Z-9FwABDQR8viBEIexpBk",
"rating": 4.1,
"reference": "CmRgAAAA0ByNliXqngNLcbGvQbQavXzFsoC4NJC_XTX1Zw4qJEifEKKA-ENokJXEsDurXhV_ab_oVWh6HKXYlE4bxdY70rDtyKRLQiE7cCNrAAYGzfMOgiJhafpgHKbdMJsBfEOuEhCu_B6ns2zKF11vkCs2qjo0GhRVNylMETQsQ8RRQoWwjRhW_R23Sg",
"reviews": [{
"aspects": [{
"rating": 3,
"type": "overall"
}],
"author_name": "Boris Zlatopolsky",
"author_url": "https://plus.google.com/102033276533166804255",
"language": "en",
"profile_photo_url": "//lh6.googleusercontent.com/-cwVCIDebmuU/AAAAAAAAAAI/AAAAAAAA9Us/ZgWUAaRInrk/photo.jpg",
"rating": 5,
"text": "Thank you for putting up with another menu pointing foreigner. Well cooked vegetarian food. Was filled with locals which is always a good sign. Free WiFi ",
"time": 1463145260
}, {
"aspects": [{
"rating": 3,
"type": "overall"
}],
"author_name": "Mark Chase",
"author_url": "https://plus.google.com/107281325021421310105",
"language": "en",
"profile_photo_url": "//lh4.googleusercontent.com/--ECOxP2sh8U/AAAAAAAAAAI/AAAAAAAAAyI/WehjRN4DcgQ/photo.jpg",
"rating": 5,
"text": "Delicious healthy vegan food. While the menu is in Chinese there are pictures of six items. Pick any three and mark your items on an order slip. The items come with a bowl of rice and tea and costs about $45 Hong Kong Dollars. Desert and soy drinks cost extra. It's on the 2nd floor (1st in local tradition), so use the elevator to get there. \n",
"time": 1447665684
}, {
"aspects": [{
"rating": 2,
"type": "overall"
}],
"author_name": "Shih Rachel",
"author_url": "https://plus.google.com/114669614384635257051",
"language": "en",
"rating": 4,
"text": "Veggie can be nice too! ",
"time": 1462295974
}, {
"aspects": [{
"rating": 0,
"type": "overall"
}],
"author_name": "Lawrenzo Lee",
"author_url": "https://plus.google.com/113370010886257427075",
"language": "en",
"profile_photo_url": "//lh3.googleusercontent.com/-Vf5cBKiyFS8/AAAAAAAAAAI/AAAAAAAAui0/bKuRPNk-KIs/photo.jpg",
"rating": 2,
"text": "Saw this place on tv introduced as a place to give people who are deaf or hard of hearing a place to work. Thought it was well meaning and interesting so decided to have a visit. \n\nArrived I think around 11:30am. Walked in and was greeted by no one. The place was pretty much empty except for one table (more people came in later closer to lunch). Didn't quite know how this place operates and no one there to show us, so we picked a table ourself and sat down, waited, but no one came over to give us a menu or anything. \n\nWent over to the cashier and asked. Gave us a sheet where we can tick what we would like to order. The lunch menu you get to pick 3 dishes out of 6 and you get with it a bowl of soup and unlimited refill of rice (the rice is not the plain white ones but with added healthy red grains). This costs $42 per person, and each person MUST order one! I think it must be because they have free rice refills. At the time I wasn't feeling that well so I didn't wan't to eat but was forced (well could have walked out but we came all the from NT to try this place) to buy a lunch set each. I couldn't eat mine and my friend wasn't able to eat 2 sets so it was all wasted. Why do they have to have this rule? \n\nI wasn't able to eat much so the food review is from the friend I went with. The food was bland, a bit like hospital food, so guess that makes it healthy. The ingredients were not good quality, One of the dish was green cabbage but there was yellowing and \"stringy\" in the mouth. \n\nFor a place that promotes healthy eating, it does not show in the quality of their ingredients. And for a place that tries to help the handicapped in society, it's good someone is doing that but they also need to provide the training for the staff to work in this place. Even the manager there, who was not deaf, will do good with a bit of training as we had to walk up to him to ask for everything. \n\nGood intentions, but very disappointed by everything else!",
"time": 1348137307
}, {
"aspects": [{
"rating": 2,
"type": "overall"
}],
"author_name": "Boris Thoenissen",
"author_url": "https://plus.google.com/108970509270187901466",
"language": "en",
"profile_photo_url": "//lh3.googleusercontent.com/-7DrNKzLlJmI/AAAAAAAAAAI/AAAAAAAAG-A/5PZAQ_R48IE/photo.jpg",
"rating": 4,
"text": "Nice place to escape crowed HK",
"time": 1408779193
}],
"scope": "GOOGLE",
"types": ["restaurant", "food", "point_of_interest", "establishment"],
"url": "https://maps.google.com/?cid=1847796940784400626",
"utc_offset": 480,
"vicinity": "Bayfield Building, 99 Hennessy Road",
"website": "http://happyveggies99.blogspot.hk/"
},
"status": "OK"
}
I m using the following Model to deserialize the Json file :
public class GooglePlaceApiLocation
{
public double lat { get; set; }
public double lng { get; set; }
}
public class GooglePlaceApiNortheast
{
public double lat { get; set; }
public double lng { get; set; }
}
public class GooglePlaceApiSouthwest
{
public double lat { get; set; }
public double lng { get; set; }
}
public class GooglePlaceApiViewport
{
public GooglePlaceApiNortheast northeast { get; set; }
public GooglePlaceApiSouthwest southwest { get; set; }
}
public class GooglePlaceApiGeometry
{
public GooglePlaceApiLocation location { get; set; }
public GooglePlaceApiViewport viewport { get; set; }
}
public class GooglePlaceApiOpeningHours
{
public bool open_now { get; set; }
public List<GooglePlaceApiPeriod> periods { get; set; }
public List<string> weekday_text { get; set; }
}
public class GooglePlaceApiPeriod
{
public GooglePlaceApiOpenClose close { get; set; }
public GooglePlaceApiOpenClose open { get; set; }
}
public class GooglePlaceApiOpenClose
{
public int day { get; set; }
public string time { get; set; }
}
public class GooglePlaceApiPhoto
{
public int height { get; set; }
public List<string> html_attributions { get; set; }
public string photo_reference { get; set; }
public int width { get; set; }
}
public class GooglePlaceApiResult
{
public GooglePlaceApiGeometry geometry { get; set; }
public string icon { get; set; }
public string id { get; set; }
public string name { get; set; }
public string place_id { get; set; }
public string reference { get; set; }
public string scope { get; set; }
public List<string> types { get; set; }
public string vicinity { get; set; }
public GooglePlaceApiOpeningHours opening_hours { get; set; }
//public List<Photo> photos { get; set; }
public double? rating { get; set; }
}
public class GooglePlaceApi
{
public List<object> html_attributions { get; set; }
public string next_page_token { get; set; }
public GooglePlaceApiResult result { get; set; }
public string status { get; set; }
}
I wish to extract the opening_hours to build a new List<OpenCloseDaysModel> with the following model:
public class OpenCloseDaysModel
{
public int day { get; set; }
public List<OpenCloseResultModel> range { get; set; }
public bool is_open { get; set; }
}
public class OpenCloseResultModel
{
public string start { get; set; }
public string end { get; set; }
}
I try to do it with the following function:
public async static Task<string> FormatOpenCloseFromGoogle(GooglePlaceApiOpeningHours GooglePlaceApiOpeningHours)
{
string Output = null;
int CurrentDay = 0;
string start = null;
string end = null;
OpenCloseDaysModel OpenCloseDaysModel = new OpenCloseDaysModel();
List<OpenCloseDaysModel> OpenCloseDaysList = new List<OpenCloseDaysModel>();
List<OpenCloseResultModel> OpenCloseResultModel = new List<OpenCloseResultModel>();
//HttpContext.Current.Response.Write("<hr />" + JsonConvert.SerializeObject(GooglePlaceApiOpeningHours, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }) + "</ hr>");
if (GooglePlaceApiOpeningHours != null)
{
foreach (var item in GooglePlaceApiOpeningHours?.periods)
{
TimeSpan tsstart;
TimeSpan tsend;
if (!TimeSpan.TryParse(item.open?.time?.Substring(0, 2) + ":" + item.open?.time?.Substring(2, 2), CultureInfo.InvariantCulture, out tsstart))
{
return null;
}
else
{
start = tsstart.ToString(#"hh\:mm");
}
if (!TimeSpan.TryParse(item.close?.time?.Substring(0, 2) + ":" + item.close?.time?.Substring(2, 2), CultureInfo.InvariantCulture, out tsend))
{
return null;
}
else
{
end = tsend.ToString(#"hh\:mm");
}
OpenCloseDaysModel.day = CurrentDay;
if (item.open.day != CurrentDay)
{
OpenCloseDaysModel.range = OpenCloseResultModel;
OpenCloseDaysList.Add(OpenCloseDaysModel);
OpenCloseDaysModel = new OpenCloseDaysModel();
OpenCloseResultModel = new List<OpenCloseResultModel>();
OpenCloseResultModel.Add(new OpenCloseResultModel { start = start, end = end });
}
else
{
OpenCloseResultModel.Add(new OpenCloseResultModel { start = start, end = end });
}
CurrentDay = item.open.day;
}
OpenCloseDaysModel.day = CurrentDay;
OpenCloseResultModel.Add(new OpenCloseResultModel { start = start, end = end });
OpenCloseDaysModel.range = OpenCloseResultModel;
OpenCloseDaysList.Add(OpenCloseDaysModel);
List<OpenCloseDaysModel> OrderdOpenCloseDaysList = new List<OpenCloseDaysModel>();
for (int i = 1; i < 7; i++)
{
var DayQuery = OpenCloseDaysList.OrderByDescending(o => o.day).Where(o => o.day == i).FirstOrDefault();
if (DayQuery != null)
{
OrderdOpenCloseDaysList.Add(new OpenCloseDaysModel { day = i, range = DayQuery.range, is_open = true });
}
else
{
OrderdOpenCloseDaysList.Add(new OpenCloseDaysModel { day = i, range = null, is_open = false });
}
}
//for sunday
var DayQuerySunday = OpenCloseDaysList.Where(o => o.day == 0).FirstOrDefault();
if (DayQuerySunday != null)
{
OrderdOpenCloseDaysList.Add(new OpenCloseDaysModel { day = 0, range = DayQuerySunday.range, is_open = true });
}
else
{
OrderdOpenCloseDaysList.Add(new OpenCloseDaysModel { day = 0, range = null, is_open = false });
}
Output = JsonConvert.SerializeObject(OrderdOpenCloseDaysList, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
}
return Output;
}
But it's returning some wrong value...
Is there an easier way to build my Model : OpenCloseDaysModel ?
I would use LINQ methods to do the transformation between the models, then fill in the missing days, sort the results by day and finally serialize. Perhaps something like this:
public static string FormatOpenCloseFromGoogle(GooglePlaceApiOpeningHours openingHoursModel)
{
if (openingHoursModel == null || openingHoursModel.periods == null) return null;
// build a list of open days from Google data
var list = openingHoursModel.periods
.GroupBy(p => p.open.day)
.Select(g => new OpenCloseDaysModel
{
day = g.Key,
is_open = true,
range = g.Select(p => new OpenCloseResultModel
{
start = p.open.time.Substring(0, 2) + ":" + p.open.time.Substring(2, 2),
end = p.close != null ? p.close.time.Substring(0, 2) + ":" + p.close.time.Substring(2, 2) : "23:59"
})
.OrderBy(r => r.start)
.ToList()
})
.ToList();
// fill in missing days
for (int i = 0; i < 7; i++)
{
if (!list.Any(m => m.day == i))
list.Add(new OpenCloseDaysModel { day = i, is_open = false, range = null });
}
// sort days with Sunday at the end and return the serialized result
var settings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };
return JsonConvert.SerializeObject(list.OrderBy(m => m.day > 0 ? m.day : 7), settings);
}
With your sample data, this gives the following result (I added formatting here for readability, but the output of the above is unformatted as per your original code):
[
{
"day": 1,
"range": [
{
"start": "11:30",
"end": "14:30"
},
{
"start": "18:00",
"end": "22:00"
}
],
"is_open": true
},
{
"day": 2,
"range": [
{
"start": "11:30",
"end": "14:30"
},
{
"start": "18:00",
"end": "22:00"
}
],
"is_open": true
},
{
"day": 3,
"range": [
{
"start": "11:30",
"end": "14:30"
},
{
"start": "18:00",
"end": "22:00"
}
],
"is_open": true
},
{
"day": 4,
"is_open": false
},
{
"day": 5,
"range": [
{
"start": "11:30",
"end": "14:30"
},
{
"start": "18:00",
"end": "22:00"
}
],
"is_open": true
},
{
"day": 6,
"range": [
{
"start": "11:30",
"end": "14:30"
},
{
"start": "18:00",
"end": "22:00"
}
],
"is_open": true
},
{
"day": 0,
"range": [
{
"start": "10:30",
"end": "23:00"
}
],
"is_open": true
}
]
Fiddle: https://dotnetfiddle.net/lojcg1
Related
I try to make an API using ASP NET CORE 7 and mongodb.
I come with a complicated problem that i simply don't understand.
I got a duplicated element name '_t' error when i get my documents after i used ReplaceOneAsync method.
I have Ship documents using polymorphism as :
using MongoDB.Bson.Serialization.Attributes;
using Shipest.Api.Models.DTO.QueriesDTO;
using Shipest.Api.Utils.ExceptionUtils;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
namespace Shipest.Api.Models.DTO;
[BsonIgnoreExtraElements(true)]
[BsonDiscriminator(Required = true, RootClass = true)]
[BsonKnownTypes(typeof(ProbatioDTO), typeof(SpeculatorDTO), typeof(ExtractorDTO), typeof(ManusDTO))]
[JsonDerivedType(typeof(ProbatioDTO))]
[JsonDerivedType(typeof(SpeculatorDTO))]
[JsonDerivedType(typeof(ExtractorDTO))]
[JsonDerivedType(typeof(ManusDTO))]
public class BaseShipDTO
{
[BsonIgnore]
private string id;
[BsonId]
[BsonRepresentation(MongoDB.Bson.BsonType.ObjectId)]
public string Id {
get {
if (new Regex("^[0-9a-fA-F]{24}$").IsMatch(id))
return id;
else
throw new ShipestException("Invalid Id : " + id, 400);
}
set => id = value;
}
public string Name { get; set; }
public virtual PlanetGetDTO? Planet { get; set; }
public DateTime Creation_Date { get; set; }
public int Health { get; set; }
public int Max_Health { get; set; }
[BsonElement("_t")]
public ShipType Type { get; set; }
public long? Owner_Id { get; set; }
public PlanetGetDTO? Planet_Arrival { get; set; }
public DateTime? Arrival_Date { get; set; }
public int Speed { get; set; }
public int Energy { get; set; }
public int Max_Energy { get; set; }
public DateTime? Next_Scan_Date { get; set; }
public int Scan_Cost { get; set; }
public DateTime? Next_Heal_Date { get; set; }
public int Creatium_Cost { get; set; }
}
with ShipType enumeration as :
public enum ShipType
{
Manus,
Probatio,
Speculator,
Extractor
}
When i simply query my document using id, all works. Here my aggregation json used :
[
%m,
{
"$lookup": {
"from": "planets",
"localField": "Coordinates",
"foreignField": "_id",
"as": "Planets"
}
},
{
"$lookup": {
"from": "planets",
"as": "Neighbours",
"let": {
"main_x": "$Coordinates.X",
"main_y": "$Coordinates.Y"
},
"pipeline": [
{
"$match": {
"$expr": {
"$or": [
{
"$eq": [
"$_id",
{
"X": {
"$add": [ "$$main_x", 1 ]
},
"Y": {
"$add": [ "$$main_y" ]
}
}
]
},
{
"$eq": [
"$_id",
{
"X": {
"$add": [ "$$main_x", -1 ]
},
"Y": {
"$add": [ "$$main_y" ]
}
}
]
},
{
"$eq": [
"$_id",
{
"X": {
"$add": [ "$$main_x" ]
},
"Y": {
"$add": [ "$$main_y", 1 ]
}
}
]
},
{
"$eq": [
"$_id",
{
"X": {
"$add": [ "$$main_x" ]
},
"Y": {
"$add": [ "$$main_y", -1 ]
}
}
]
}
]
}
}
}
]
}
},
{
"$lookup": {
"from": "planets",
"localField": "Planet_of_Arrival_Coordinate",
"foreignField": "_id",
"as": "planet_of_arrival"
}
},
{
"$lookup": {
"from": "planets",
"as": "Neighbours_Arrival",
"let": {
"main_x": "$Planet_of_Arrival_Coordinate.X",
"main_y": "$Planet_of_Arrival_Coordinate.Y"
},
"pipeline": [
{
"$match": {
"$expr": {
"$or": [
{
"$eq": [
"$_id",
{
"X": {
"$add": [ "$$main_x", 1 ]
},
"Y": {
"$add": [ "$$main_y" ]
}
}
]
},
{
"$eq": [
"$_id",
{
"X": {
"$add": [ "$$main_x", -1 ]
},
"Y": {
"$add": [ "$$main_y" ]
}
}
]
},
{
"$eq": [
"$_id",
{
"X": {
"$add": [ "$$main_x" ]
},
"Y": {
"$add": [ "$$main_y", 1 ]
}
}
]
},
{
"$eq": [
"$_id",
{
"X": {
"$add": [ "$$main_x" ]
},
"Y": {
"$add": [ "$$main_y", -1 ]
}
}
]
}
]
}
}
}
]
}
},
{
"$addFields": {
"Planet": {
"$first": "$Planets"
},
"Planet_Arrival": {
"$first": "$planet_of_arrival"
}
}
},
{
"$addFields":
{
"Planet.Neighbours": "$Neighbours",
"Planet_Arrival.Neighbours": "$Neighbours_Arrival"
}
},
{
"$unset": [
"Coordinates",
"Planet_of_Arrival_Coordinate",
"Planets",
"planet_of_arrival",
"Neighbours_Arrival",
"Neighbours"
]
},
{
"$set":
{
"Planet_Arrival": {
"$cond": {
"if": {
"$eq": [
{
"$type": "$Planet_Arrival._id"
},
"object"
]
},
"then": "$Planet_Arrival",
"else": "$$REMOVE"
}
},
"Planet": {
"$cond": {
"if": {
"$eq": [
{
"$type": "$Planet._id"
},
"object"
]
},
"then": "$Planet",
"else": "$$REMOVE"
}
}
}
}
]
i know my aggregate can surely be simplified but i'm a beginner with these noSQL syntaxe ahah.
And of course %m is replaced in code as :
private string getAggregateWithMatch(string match)
{
return File.ReadAllText(#".\Database\JsonQueries\shipAggregate.json").Replace("%m", match);
}
public async Task<BaseShipDTO?> GetAsync(string shipId)
{
// use the json
var query = getAggregateWithMatch("{'$match': { '_id': ObjectId('" + shipId + "') }}");
var listResulted = await DataBaseUtils.DoGenericAggregate<BaseShipDTO, Ship>(query, _shipsCollection);
return listResulted.FirstOrDefault();
}
and documents that i do tests on it in mongodb is as :
{
"_id": {
"$oid": "63d10256d3d1cc30603cfaf6"
},
"_t": "Speculator",
"Coordinates": {
"X": 12,
"Y": -4
},
"Health": 600,
"Max_Health": 100000,
"Name": "Roger",
"Creation_Date": {
"$date": {
"$numberLong": "1674136353103"
}
},
"Creatium_Cost": 300,
"Energy": 100,
"Max_Energy": 100,
"Next_Scan_Date": null,
"Planet_of_Arrival_Coordinate": {
"X": 13,
"Y": -4
},
"Scan_Cost": 5,
"Speed": 600,
"Next_Heal_Date": {
"$date": {
"$numberLong": "1675610925014"
}
},
"Arrival_Date": null,
"Owner_Id": {
"$numberLong": "279937954565193729"
}
}
so when i change the Health property for exemple, and after that i use this method :
public async Task UpdateAsync(Ship ship)
{
await _shipsCollection.ReplaceOneAsync(s => s._id == ship._id, ship, new ReplaceOptions { IsUpsert = false });
}
i see no change with my document in database except my Health field (and the Health Date, which is normal):
{
"_id": {
"$oid": "63d10256d3d1cc30603cfaf6"
},
"_t": "Speculator",
"Coordinates": {
"X": 12,
"Y": -4
},
"Health": 750,
"Max_Health": 100000,
"Name": "Roger",
"Creation_Date": {
"$date": {
"$numberLong": "1674136353103"
}
},
"Creatium_Cost": 300,
"Energy": 100,
"Max_Energy": 100,
"Next_Scan_Date": null,
"Planet_of_Arrival_Coordinate": {
"X": 13,
"Y": -4
},
"Scan_Cost": 5,
"Speed": 600,
"Next_Heal_Date": {
"$date": {
"$numberLong": "1675611313544"
}
},
"Arrival_Date": null,
"Owner_Id": {
"$numberLong": "279937954565193729"
}
}
But at this point, i get my firstly presented error around _t when i try to re get it using my getAsync method :/ despite the fact that the field '_t' dont change at all.
Thanks you for any help^, sorry for my weird english (i'm french huh) and sorry if i forget useful code parts !
error message after a get on a object who got a replaceOne before
I solved it by removing _t field in my bdd model :
I replace using a model with exact same fields wich my mongo db takes :
public record Ship(
ObjectId _id,
Coordinates? Coordinates,
int Health,
int Max_Health,
string Name,
DateTime Creation_Date,
int Creatium_Cost,
int Energy,
int Max_Energy,
DateTime? Next_Scan_Date,
Coordinates? Planet_of_Arrival_Coordinate,
int Scan_Cost,
string _t,
int Speed,
DateTime? Next_Heal_Date,
DateTime? Arrival_Date,
long? Owner_Id
);
i removed the _t field from it, it cwas causing a weird dual modification on it (on from mongo driver, on from my field)
So ive connected a n external API and I want to display it on the page, but im not getting any luck.
I'm using RestSharp so I cant just collect the infromation as such:
string results = response.Content.ReadAsStringAsync().Result;
Also because i'm using RestSharp, collecting the returned api information with:
string results = response.Content;
this causes the program to crash because it cannot be inserted into the data table. From what im seeing it needs to be deserialized, but im not sure how thats done in RestSharp.
EDIT 1:
This is my model class based on the API results
using Newtonsoft.Json;
namespace NBA_API_TEST.Models
{
public class NBAModel
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("league")]
public string League { get; set; }
[JsonProperty("season")]
public long Season { get; set; }
[JsonProperty("date")]
public Date Date { get; set; }
[JsonProperty("stage")]
public long Stage { get; set; }
[JsonProperty("status")]
public Status Status { get; set; }
[JsonProperty("periods")]
public Periods Periods { get; set; }
[JsonProperty("arena")]
public Arena Arena { get; set; }
[JsonProperty("teams")]
public Teams Teams { get; set; }
[JsonProperty("scores")]
public Scores Scores { get; set; }
[JsonProperty("officials")]
public object[] Officials { get; set; }
[JsonProperty("timesTied")]
public object TimesTied { get; set; }
[JsonProperty("leadChanges")]
public object LeadChanges { get; set; }
[JsonProperty("nugget")]
public object Nugget { get; set; }
}
public partial class Arena
{
[JsonProperty("name")]
public object Name { get; set; }
[JsonProperty("city")]
public object City { get; set; }
[JsonProperty("state")]
public object State { get; set; }
[JsonProperty("country")]
public object Country { get; set; }
}
public partial class Date
{
[JsonProperty("start")]
public DateTimeOffset Start { get; set; }
[JsonProperty("end")]
public object End { get; set; }
[JsonProperty("duration")]
public object Duration { get; set; }
}
public partial class Periods
{
[JsonProperty("current")]
public long Current { get; set; }
[JsonProperty("total")]
public long Total { get; set; }
[JsonProperty("endOfPeriod")]
public object EndOfPeriod { get; set; }
}
public partial class Scores
{
[JsonProperty("visitors")]
public ScoresHome Visitors { get; set; }
[JsonProperty("home")]
public ScoresHome Home { get; set; }
}
public partial class ScoresHome
{
[JsonProperty("win")]
public object Win { get; set; }
[JsonProperty("loss")]
public object Loss { get; set; }
[JsonProperty("series")]
public Series Series { get; set; }
[JsonProperty("linescore")]
public object[] Linescore { get; set; }
[JsonProperty("points")]
public long Points { get; set; }
}
public partial class Series
{
[JsonProperty("win")]
public object Win { get; set; }
[JsonProperty("loss")]
public object Loss { get; set; }
}
public partial class Status
{
[JsonProperty("clock")]
public object Clock { get; set; }
[JsonProperty("halftime")]
public object Halftime { get; set; }
[JsonProperty("short")]
public long Short { get; set; }
[JsonProperty("long")]
public string Long { get; set; }
}
public partial class Teams
{
[JsonProperty("visitors")]
public TeamsHome Visitors { get; set; }
[JsonProperty("home")]
public TeamsHome Home { get; set; }
}
public partial class TeamsHome
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("nickname")]
public string Nickname { get; set; }
[JsonProperty("code")]
public string Code { get; set; }
[JsonProperty("logo")]
public Uri Logo { get; set; }
}
}
from the API call 5 results should be returned.
So I have these set up
IList<NBAModel> GameModel = new List<NBAModel>();
GameModel = JsonConvert.DeserializeObject<List<NBAModel>>(results);
But that causes this error to be returned:
JsonSerializationException: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[NBA_API_TEST.Models.NBAModel]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly. To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
EDIT 2
Update with my JSON
{
"get": "games/",
"parameters": {
"season": "2021",
"h2h": "1-2"
},
"errors": [],
"results": 5,
"response": [
{
"id": 9484,
"league": "vegas",
"season": 2021,
"date": {
"start": "2021-08-08T20:00:00.000Z",
"end": null,
"duration": null
},
"stage": 2,
"status": {
"clock": null,
"halftime": null,
"short": 3,
"long": "Finished"
},
"periods": {
"current": 4,
"total": 4,
"endOfPeriod": null
},
"arena": {
"name": null,
"city": null,
"state": null,
"country": null
},
"teams": {
"visitors": {
"id": 2,
"name": "Boston Celtics",
"nickname": "Celtics",
"code": "BOS",
"logo": "https://upload.wikimedia.org/wikipedia/fr/thumb/6/65/Celtics_de_Boston_logo.svg/1024px-Celtics_de_Boston_logo.svg.png"
},
"home": {
"id": 1,
"name": "Atlanta Hawks",
"nickname": "Hawks",
"code": "ATL",
"logo": "https://upload.wikimedia.org/wikipedia/fr/e/ee/Hawks_2016.png"
}
},
"scores": {
"visitors": {
"win": null,
"loss": null,
"series": {
"win": null,
"loss": null
},
"linescore": [],
"points": 85
},
"home": {
"win": null,
"loss": null,
"series": {
"win": null,
"loss": null
},
"linescore": [],
"points": 83
}
},
"officials": [],
"timesTied": null,
"leadChanges": null,
"nugget": null
},
{
"id": 9780,
"league": "standard",
"season": 2021,
"date": {
"start": "2021-11-18T00:30:00.000Z",
"end": "2021-11-18T02:43:00.000Z",
"duration": "2:03"
},
"stage": 2,
"status": {
"clock": null,
"halftime": false,
"short": 3,
"long": "Finished"
},
"periods": {
"current": 4,
"total": 4,
"endOfPeriod": false
},
"arena": {
"name": "State Farm Arena",
"city": "Atlanta",
"state": "GA",
"country": "USA"
},
"teams": {
"visitors": {
"id": 2,
"name": "Boston Celtics",
"nickname": "Celtics",
"code": "BOS",
"logo": "https://upload.wikimedia.org/wikipedia/fr/thumb/6/65/Celtics_de_Boston_logo.svg/1024px-Celtics_de_Boston_logo.svg.png"
},
"home": {
"id": 1,
"name": "Atlanta Hawks",
"nickname": "Hawks",
"code": "ATL",
"logo": "https://upload.wikimedia.org/wikipedia/fr/e/ee/Hawks_2016.png"
}
},
"scores": {
"visitors": {
"win": 7,
"loss": 8,
"series": {
"win": 0,
"loss": 1
},
"linescore": [
"29",
"20",
"28",
"22"
],
"points": 99
},
"home": {
"win": 7,
"loss": 9,
"series": {
"win": 1,
"loss": 0
},
"linescore": [
"30",
"28",
"33",
"19"
],
"points": 110
}
},
"officials": [
"Leon Wood",
"Zach Zarba",
"Suyash Mehta"
],
"timesTied": null,
"leadChanges": null,
"nugget": null
},
{
"id": 10302,
"league": "standard",
"season": 2021,
"date": {
"start": "2022-01-29T00:30:00.000Z",
"end": "2022-01-29T02:54:00.000Z",
"duration": "2:13"
},
"stage": 2,
"status": {
"clock": null,
"halftime": false,
"short": 3,
"long": "Finished"
},
"periods": {
"current": 4,
"total": 4,
"endOfPeriod": false
},
"arena": {
"name": "State Farm Arena",
"city": "Atlanta",
"state": "GA",
"country": "USA"
},
"teams": {
"visitors": {
"id": 2,
"name": "Boston Celtics",
"nickname": "Celtics",
"code": "BOS",
"logo": "https://upload.wikimedia.org/wikipedia/fr/thumb/6/65/Celtics_de_Boston_logo.svg/1024px-Celtics_de_Boston_logo.svg.png"
},
"home": {
"id": 1,
"name": "Atlanta Hawks",
"nickname": "Hawks",
"code": "ATL",
"logo": "https://upload.wikimedia.org/wikipedia/fr/e/ee/Hawks_2016.png"
}
},
"scores": {
"visitors": {
"win": 25,
"loss": 25,
"series": {
"win": 0,
"loss": 2
},
"linescore": [
"20",
"27",
"30",
"15"
],
"points": 92
},
"home": {
"win": 23,
"loss": 25,
"series": {
"win": 2,
"loss": 0
},
"linescore": [
"32",
"30",
"18",
"28"
],
"points": 108
}
},
"officials": [
"Scott Wall",
"Tom Washington",
"Josh Tiven"
],
"timesTied": 5,
"leadChanges": 9,
"nugget": null
},
{
"id": 10421,
"league": "standard",
"season": 2021,
"date": {
"start": "2022-02-13T19:00:00.000Z",
"end": "2022-02-13T21:27:00.000Z",
"duration": "2:15"
},
"stage": 2,
"status": {
"clock": null,
"halftime": false,
"short": 3,
"long": "Finished"
},
"periods": {
"current": 4,
"total": 4,
"endOfPeriod": false
},
"arena": {
"name": "TD Garden",
"city": "Boston",
"state": "MA",
"country": "USA"
},
"teams": {
"visitors": {
"id": 1,
"name": "Atlanta Hawks",
"nickname": "Hawks",
"code": "ATL",
"logo": "https://upload.wikimedia.org/wikipedia/fr/e/ee/Hawks_2016.png"
},
"home": {
"id": 2,
"name": "Boston Celtics",
"nickname": "Celtics",
"code": "BOS",
"logo": "https://upload.wikimedia.org/wikipedia/fr/thumb/6/65/Celtics_de_Boston_logo.svg/1024px-Celtics_de_Boston_logo.svg.png"
}
},
"scores": {
"visitors": {
"win": 26,
"loss": 30,
"series": {
"win": 2,
"loss": 1
},
"linescore": [
"28",
"27",
"23",
"17"
],
"points": 95
},
"home": {
"win": 33,
"loss": 25,
"series": {
"win": 1,
"loss": 2
},
"linescore": [
"17",
"28",
"42",
"18"
],
"points": 105
}
},
"officials": [
"Kane Fitzgerald",
"Karl Lane",
"Brandon Adair"
],
"timesTied": 2,
"leadChanges": 1,
"nugget": null
},
{
"id": 10492,
"league": "standard",
"season": 2021,
"date": {
"start": "2022-03-02T00:30:00.000Z",
"end": "2022-03-02T03:09:00.000Z",
"duration": "2:20"
},
"stage": 2,
"status": {
"clock": null,
"halftime": false,
"short": 3,
"long": "Finished"
},
"periods": {
"current": 4,
"total": 4,
"endOfPeriod": false
},
"arena": {
"name": "TD Garden",
"city": "Boston",
"state": "MA",
"country": "USA"
},
"teams": {
"visitors": {
"id": 1,
"name": "Atlanta Hawks",
"nickname": "Hawks",
"code": "ATL",
"logo": "https://upload.wikimedia.org/wikipedia/fr/e/ee/Hawks_2016.png"
},
"home": {
"id": 2,
"name": "Boston Celtics",
"nickname": "Celtics",
"code": "BOS",
"logo": "https://upload.wikimedia.org/wikipedia/fr/thumb/6/65/Celtics_de_Boston_logo.svg/1024px-Celtics_de_Boston_logo.svg.png"
}
},
"scores": {
"visitors": {
"win": 29,
"loss": 32,
"series": {
"win": 2,
"loss": 2
},
"linescore": [
"28",
"37",
"13",
"20"
],
"points": 98
},
"home": {
"win": 37,
"loss": 27,
"series": {
"win": 2,
"loss": 2
},
"linescore": [
"19",
"32",
"31",
"25"
],
"points": 107
}
},
"officials": [
"Eric Lewis",
"Curtis Blair",
"Scott Twardoski"
],
"timesTied": 7,
"leadChanges": 6,
"nugget": null
}
]
}
for your classes you can try this, but you will not have a root level data in this cases
List<NBAModel> nbaModels=JObject.Parse(json)["response"].ToObject<List<NBAModel>>();
public partial class TeamsHome
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("name")]
public Name Name { get; set; }
[JsonProperty("nickname")]
public Nickname Nickname { get; set; }
[JsonProperty("code")]
public Code Code { get; set; }
[JsonProperty("logo")]
public Uri Logo { get; set; }
}
public enum Code { Atl, Bos };
public enum Name {
[EnumMember(Value = "Atlanta Hawks")]
AtlantaHawks,
[EnumMember(Value = "Boston Celtics")]
BostonCeltics };
public enum Nickname { Celtics, Hawks };
if you want the whole data you need to add this classes
Data data = JsonConvert.DeserializeObject<Data>(json);
public partial class Data
{
[JsonProperty("get")]
public string Get { get; set; }
[JsonProperty("parameters")]
public Parameters Parameters { get; set; }
[JsonProperty("errors")]
public List<object> Errors { get; set; }
[JsonProperty("results")]
public long Results { get; set; }
[JsonProperty("response")]
public List<NBAModel> Response { get; set; }
}
public partial class Parameters
{
[JsonProperty("season")]
public long Season { get; set; }
[JsonProperty("h2h")]
public string H2H { get; set; }
}
Having some problems deserializing JSON in JSON.net.
First of all I am pulling JSON from my source that looks like the following.
{
"guest": {
"id": 11111,
"A": "bla",
"B": "bla",
"C": false,
"credentials": [
{
"id": 222,
"Z": "bla",
"accounts": [
{
"id": 01,
"type": "bla"
}
]
},
{
"id": 222,
"Z": "bla",
"accounts": [
{
"id": 02,
"type": "bla"
}
]
},
{
"id": 333,
"Z": "bla",
"accounts": [
{
"id": 03,
"type": "bla"
},
{
"id": 04,
"type": "bla"
},
{
"id": 05,
"type": "bla"
},
]
},
]
}
}
Within my code I have the following classes
public class guest
{
public int id { get; set; }
public string A { get; set; }
public string B { get; set; }
public bool C { get; set; }
public IList<credentials> credentials { get; set; }
}
public class credentials
{
public int id { get; set; }
public string Z { get; set; }
public IList<accounts> accounts { get; set; }
}
public class accounts
{
public int id { get; set; }
public string type { get; set; }
}
From this point I get my JSON from an HttpWebResponse and deserialize.
var httpResponseGetResponse = (HttpWebResponse)httpWebRequestGetResponse.GetResponse();
using (var streamReader = new StreamReader(httpResponseGetResponse.GetResponseStream(), Encoding.UTF8))
{
var result = streamReader.ReadToEnd();
guest deseriazedJSON = JsonConvert.DeserializeObject<guest>(result);
}
This ends with everything being NULL. Am I blind and missing something in the JSON.net docs?
Appreciate any help and thank you in advance.
You have an object with a single property named guest on a root level of your JSON. So it can be deserialized in next scenario:
public class Response
{
public guest guest { get; set; }
}
...
var httpResponseGetResponse = (HttpWebResponse)httpWebRequestGetResponse.GetResponse();
using (var streamReader = new StreamReader(httpResponseGetResponse.GetResponseStream(), Encoding.UTF8))
{
var result = streamReader.ReadToEnd();
var deseriazedJSON = JsonConvert.DeserializeObject<Response>(result);
}
It's better to change your JSON input to avoid an extra wrapper:
{
"id": 11111,
"A": "bla",
"B": "bla",
"C": false,
"credentials": [
{
"id": 222,
"Z": "bla"
"accounts": [
{
"id": 01,
"type": "bla"
}
]
},
{
"id": 222,
"Z": "bla"
"accounts": [
{
"id": 02,
"type": "bla"
}
]
},
{
"id": 333,
"Z": "bla"
"accounts": [
{
"id": 03,
"type": "bla"
},
{
"id": 04,
"type": "bla"
},
{
"id": 05,
"type": "bla"
},
]
},
]
}
i have a Json file that contains some information i need elsewhere in my code but a lot of the information is irrelivant.
At the moment ive just put it into a dynamic object so i could check that it was all working:
var data = JsonConvert.DeserializeObject<dynamic>(response.Content);
How do i get the information i need out of the Json file and store them somewhere as variables.
All other tutorials where its stored in a class use the whole Json file and doesnt look like it would be useful in my case.
Here's the Json, i only really need the Stats section at the end of the file for what im doing
{
"data": {
"id": "",
"type": "player",
"children": [
{
"id": "legend_8",
"type": "legend",
"metadata": {
"legend_name": "Pathfinder",
"icon": "https://trackercdn.com/cdn/apex.tracker.gg/legends/pathfinder-tile.png",
"bgimage": "https://trackercdn.com/cdn/apex.tracker.gg/legends/pathfinder-concept-bg-small.jpg",
"is_active": true
},
"stats": [
{
"metadata": {
"key": "Kills",
"name": "Kills",
"categoryKey": "combat",
"categoryName": "Combat",
"isReversed": false
},
"value": 377.0,
"percentile": 21.0,
"displayValue": "377",
"displayRank": ""
},
{
"metadata": {
"key": "Finishers",
"name": "Finishers",
"categoryKey": "game",
"categoryName": "Game",
"isReversed": false
},
"value": 39.0,
"percentile": 0.2,
"rank": 886,
"displayValue": "39",
"displayRank": "886"
}
]
},
{
"id": "legend_5",
"type": "legend",
"metadata": {
"legend_name": "Bloodhound",
"icon": "https://trackercdn.com/cdn/apex.tracker.gg/legends/bloodhound-tile.png",
"bgimage": "https://trackercdn.com/cdn/apex.tracker.gg/legends/bloodhound-concept-bg-small.jpg",
"is_active": false
},
"stats": [
{
"metadata": {
"key": "Kills",
"name": "Kills",
"categoryKey": "combat",
"categoryName": "Combat",
"isReversed": false
},
"value": 235.0,
"percentile": 16.0,
"displayValue": "235",
"displayRank": ""
}
]
}
],
"metadata": {
"statsCategoryOrder": [
"combat",
"game",
"weapons"
],
"platformId": 2,
"platformUserHandle": "",
"accountId": "",
"cacheExpireDate": "11/10/2019 10:48:14 PM",
"level": 49,
"avatarUrl": "https://avatar-cdn.tracker.gg/api/avatar/2/",
"countryCode": null,
"collections": 36,
"activeLegend": 8
},
"stats": [
{
"metadata": {
"key": "Level",
"name": "Level",
"categoryKey": "combat",
"categoryName": "Combat",
"isReversed": false
},
"value": 49.0,
"percentile": 46.0,
"displayValue": "49",
"displayRank": ""
},
{
"metadata": {
"key": "Kills",
"name": "Kills",
"categoryKey": "combat",
"categoryName": "Combat",
"isReversed": false
},
"value": 612.0,
"percentile": 20.0,
"displayValue": "612",
"displayRank": ""
},
{
"metadata": {
"key": "Finishers",
"name": "Finishers",
"categoryKey": "game",
"categoryName": "Game",
"isReversed": false
},
"value": 39.0,
"percentile": 0.5,
"displayValue": "39",
"displayRank": ""
},
{
"metadata": {
"key": "RankScore",
"name": "Rank Score",
"categoryKey": "game",
"categoryName": "Game",
"isReversed": false
},
"value": 64.0,
"percentile": 21.0,
"displayValue": "64",
"displayRank": ""
}
]
}
}
You could create a data structure which has only the relevant properties. For example,
public class StatMetaData
{
public string key { get; set; }
public string name { get; set; }
public string categoryKey { get; set; }
public string categoryName { get; set; }
public bool isReversed { get; set; }
}
public class Stat
{
public StatMetaData metadata { get; set; }
public double value { get; set; }
public double percentile { get; set; }
public string displayValue { get; set; }
public string displayRank { get; set; }
}
public class Data
{
public List<Stat> stats { get; set; }
}
public class RootObject
{
public Data data { get; set; }
}
Now you could deserialize the json as the following to retrieve the stats sections
var result = JsonConvert.DeserializeObject<RootObject>(json).data.stats;
I need to deserialize the this json returned from grogle maps api:
{
"destination_addresses": [
"Via Medaglie D'Oro, 10, 47121 Forlì FC, Italia",
"Via Torino, 20123 Milano, Italia",
"Via Guglielmo Marconi, 71, 40121 Bologna, Italia",
"Via Irnerio, 40126 Bologna, Italia"
],
"origin_addresses": [
"Via Medaglie D'Oro, 10, 47121 Forlì FC, Italia",
"Via Torino, 20123 Milano, Italia",
"Via Guglielmo Marconi, 71, 40121 Bologna, Italia",
"Via Irnerio, 40126 Bologna, Italia"
],
"rows": [
{
"elements": [
{
"distance": {
"text": "1 m",
"value": 0
},
"duration": {
"text": "1 min",
"value": 0
},
"status": "OK"
},
{
"distance": {
"text": "286 km",
"value": 286281
},
"duration": {
"text": "2 ore 48 min",
"value": 10083
},
"status": "OK"
},
{
"distance": {
"text": "80,1 km",
"value": 80088
},
"duration": {
"text": "1 ora 3 min",
"value": 3789
},
"status": "OK"
},
{
"distance": {
"text": "77,6 km",
"value": 77594
},
"duration": {
"text": "57 min",
"value": 3422
},
"status": "OK"
}
]
},
{
"elements": [
{
"distance": {
"text": "288 km",
"value": 287811
},
"duration": {
"text": "2 ore 48 min",
"value": 10052
},
"status": "OK"
},
{
"distance": {
"text": "1 m",
"value": 0
},
"duration": {
"text": "1 min",
"value": 0
},
"status": "OK"
},
{
"distance": {
"text": "212 km",
"value": 212423
},
"duration": {
"text": "2 ore 8 min",
"value": 7664
},
"status": "OK"
},
{
"distance": {
"text": "218 km",
"value": 218219
},
"duration": {
"text": "2 ore 9 min",
"value": 7740
},
"status": "OK"
}
]
},
{
"elements": [
{
"distance": {
"text": "78,5 km",
"value": 78528
},
"duration": {
"text": "56 min",
"value": 3346
},
"status": "OK"
},
{
"distance": {
"text": "212 km",
"value": 212190
},
"duration": {
"text": "2 ore 5 min",
"value": 7519
},
"status": "OK"
},
{
"distance": {
"text": "1 m",
"value": 0
},
"duration": {
"text": "1 min",
"value": 0
},
"status": "OK"
},
{
"distance": {
"text": "2,0 km",
"value": 1979
},
"duration": {
"text": "5 min",
"value": 316
},
"status": "OK"
}
]
},
{
"elements": [
{
"distance": {
"text": "74,7 km",
"value": 74719
},
"duration": {
"text": "55 min",
"value": 3278
},
"status": "OK"
},
{
"distance": {
"text": "218 km",
"value": 217951
},
"duration": {
"text": "2 ore 9 min",
"value": 7712
},
"status": "OK"
},
{
"distance": {
"text": "3,8 km",
"value": 3782
},
"duration": {
"text": "11 min",
"value": 671
},
"status": "OK"
},
{
"distance": {
"text": "1 m",
"value": 0
},
"duration": {
"text": "1 min",
"value": 0
},
"status": "OK"
}
]
}
],
"status": "OK"
}
I need to create a Distance Matrix so I'm only interested in the "value" field inside "distance".
I've tried this approach:
DataSet data = JsonConvert.DeserializeObject<DataSet>(jsonResponse);
DataTable dataTab = data.Tables["Elements"];
foreach (DataRow elements in dataTab.Rows)
{
Console.WriteLine(elements["distance"]);
//Do something else here
}
But The JSonConvert returns "Additional text found in JSON string after finishing deserializing object."
You should deserialize to classes that match your data. You can generate these classes at http://json2csharp.com/.
// use like
var rootObj = JsonConvert.DeserializeObject<RootObject>(jsonResponse);
foreach (var row in rootObj.rows)
{
foreach (var element in row.elements)
{
Console.WriteLine(element.distance.text);
}
}
// you might want to change the property names to .Net conventions
// use [JsonProperty] to let the serializer know the JSON names where needed
public class Distance
{
public string text { get; set; }
public int value { get; set; }
}
public class Duration
{
public string text { get; set; }
public int value { get; set; }
}
public class Element
{
public Distance distance { get; set; }
public Duration duration { get; set; }
public string status { get; set; }
}
public class Row
{
public List<Element> elements { get; set; }
}
public class RootObject
{
public List<string> destination_addresses { get; set; }
public List<string> origin_addresses { get; set; }
public List<Row> rows { get; set; }
public string status { get; set; }
}
I believe the issue is with the cast to 'DataSet' based on a similar question I found searching Stack Overflow.
Try this as I believe it would work (you may need to use 'rows' instead of 'Elements', but I believe the approach of using a JObject will resolve the primary issue of the 'additional text'.
JObject json = JsonConvert.DeserializeObject<JObject>(jsonResponse);
foreach (Dictionary<string, object> item in data["Elements"])
{
foreach (string val in item.Values) {
Console.WriteLine(val);
}
}
Using dynamic :
dynamic json = JsonConvert.DeserializeObject(jsonResponse);
var rowCount = json.rows.Count;
Func<dynamic, int> getElementCount = r => r.elements.Count;
var maxElements = Enumerable.Max(json.rows, getElementCount);
var matrix = new int?[rowCount, maxElements];
for(int i = 0; i < rowCount; i++)
{
var elements = json.rows[i].elements;
for(int j = 0; j < elements.Count; j++)
{
var element = elements[j];
matrix[i, j] = element.distance.value;
}
}