Error serializing 'nested' JSON Object in C# - c#

I'm deserializing the following JSON and having trouble with the categories/WebFilters section. I want to be able to loop through it but get the
Cannot deserialize current JSON object into type - c#´
CS1579 foreach statement cannot operate on variables of type 'WebFilters' because 'WebFilters' does not contain a public definition for 'GetEnumerator'
error when I run my code. Eventually I need to make a list of the keys and values in each of the categories as I'm using it to create a dynamic results filter - I don't know at this stage what or how many filters they'll be.
I tried changing my json, but that doesn't achieve what I want it to. I think I need to give WebFilters the ability to be iterated over...
Thanks.
{
"Webinars": [
{
"date":"2017-11-08T19:21:46Z",
"title":"Americano",
"desc":"Nondisp fx of prox phalanx of l mid fngr, 7thG",
"startTime":"5:39 PM",
"endTime":"5:19 PM",
"categories":{
"category":"introduction",
"timeZone":"Europe/Stockholm",
"language":"English"
}
},...
Model
public class Rootobject
{
public Webinar[] Webinars { get; set; }
public ELearning[] ELearning { get; set; }
}
public class Webinar
{
public DateTime Date { get; set; }
public string Title { get; set; }
public string Desc { get; set; }
public string StartTime { get; set; }
public string EndTime { get; set; }
public WebFilters Categories { get; set; }
}
public class WebFilters
{
public string Category { get; set; }
public string TimeZone { get; set; }
public string Language { get; set; }
}
View
#foreach (var webinar in Model.Webinars)
{
<li>#webinar.Title</li>
<ul>
#{
var categories = webinar.Categories;
}
#foreach (var cat in categories)
{
<li>#cat</li>
}
</ul>
}
Controller
public ActionResult Dashboard_Portal()
{
// Example JSON
var webClient = new WebClient();
var json = webClient.DownloadString(#"http://elliottjbrown.co.uk/portal/js/data.json");
var webinars = JsonConvert.DeserializeObject<Rootobject>(json);
return View(webinars);
}

Your Categories Property in your Webinar class is not a collection of any kind, so you can't iterate over it.
You may be looking to either make it a collection:
public WebFilters Categories[] { get; set; } or
public WebFilters List<Categories> { get; set; }
If you actually want a collection of categories, you'll either need to have a partial view configured to render a given Category object, or you'll need to provide specific rendering inside your for loop. Something like this (note the below code will only show the one property):
#foreach (var cat in categories)
{
<li>#cat.Category</li>
}
If you only expect one category for each webinar, then you'll need to refer to each property individually in the View. Here's a .NET Fiddle showing a simple render with Categories as a single object, rather than a collection Simple Display Render With Non-Collection Categories Property
<ul>
<li>#webinar.Categories.Category</li>
<li>#webinar.Categories.TimeZone</li>
<li>#webinar.Categories.Language</li>
</ul>
UPDATE
Here's a link to a .NET fiddle that shows what this might look like if you're just trying to iterate through the Categories and display them: Simple Display Render With Category Collection. You'll notice that the Webinars and Elearning properties of the Rootobject are still arrays (not List<T>), as originally defined, and the the Rootobject does not implement IEnumerable as was suggested in other answers. There's no need for those changes.
Now, if you're looking for an edit page of some kind where the user can actually select a single category from a list of options, that's a different issue and wasn't clear from your OP.

Convert the Rootobject class properties into List<> as follows:
public class Rootobject
{
public List<Webinar> Webinars { get; set; }
}
Update 1:
Note: Make sure Categories in the Webinar class implements the IEnumerable interface or list collection as the Json data returns an array.
Update 2: I've updated your code and made it work using the following:
The Webinar Class:
public class Webinar
{
public DateTime Date { get; set; }
public string Title { get; set; }
public string Desc { get; set; }
public string StartTime { get; set; }
public string EndTime { get; set; }
public WebFilters[] Categories { get; set; } //Converted it into a collection
}
The Controller:
public ActionResult Dashboard_Portal()
{
var webClient = new WebClient();
var json = webClient.DownloadString(#"http://elliottjbrown.co.uk/portal/js/data.json");
var webinars = JsonConvert.DeserializeObject<Rootobject>(json);
return View(webinars);
}
Finally, The View:
#model DemoApp.Models.Rootobject
#{
Layout = null;
}
<div>
#foreach (var webinar in Model.Webinars)
{
<h3>#webinar.Title</h3>
<ul>
<li>#webinar.Categories[0].TimeZone</li>
</ul>
}
</div>
See the output with a screenshot:

according to JSON-RPC spec , json is case senitive.
all of your c# class properties start with uppercases, but your json propertise with lowercases.

Related

how to define model of JSON.DeserializeObject

I'm a little bit of confused about when I see the JSON visualizer how exactly I have to define the model that JSON deserializes correctly;
for example:
Example Screenshot
Text:
{"questions":[{"QID":"NEW0","Context":"از چه سنی مبتلا به دیابت
شدید؟"},{"QID":"8","Context":"قند خون سه ساعت بعد از وعده غذایی
(میانگین قند خونی که در دو روز اخیر اندازه گرفتید)"}]}
I had an array of Javascript Classe (Questions which is made of Question) now I sent it to the controller side and do not have any idea how I have to deserialize it. I've tested these things and none of them worked.
Wrong ones: (Jsonstring is the JSON object in action's arguments)
var QustionList = JsonConvert.DeserializeObject<Dictionary<int,Ques>>(jsonstring);
var QustionList = JsonConvert.DeserializeObject<Dictionary<string,Ques>>(jsonstring);
var QustionList = JsonConvert.DeserializeObject<Dictionary<List<Ques>>(jsonstring);
var QustionList = JsonConvert.DeserializeObject<Dictionary<Ques>(jsonstring);
Here is Ques:
public class Ques
{
public string QID { get; set; }
public string Context{ get; set; }
}
The top level property in your JSON string is "questions", which is an array of objects, therefore you need a top level class to hold that property. eg
public class Root
{
public Question[] Questions { get; set; }
}
public class Question
{
public string QID { get; set; }
public string Context { get; set; }
}
var root = JsonConvert.DeserializeObject<Root>(jsonString);

How to use json data in MVC view .cshtml

I hope someone can help me:
My challenge is, that I have a web service returning a json.
The format is
{"stations":[{"name":"aname","free":false},{"name":"anothername","free":true}]}
so I have one object which is an array that hold n objects with n attributes....
Now for each object in that array of the stations object I would like to render the attributes, like
<p>stations[0].name</p>
I need to use this in mvc.
So I created a model
public station(){}
public string name {get; set;}
public boolean free {get; set;}
in my contorller I use a WebClient and now I need to handle the response.
I was thinking of IEnumerable but I don't know how to put this in the view?!
my goal is to understand how i can do something like
public Actionresult Stations(){
var stations = JObject.Load(reponse);
return View(Stations);
}
but I have no idea how to the handle each object of the array and get their values in the Stations.cshtml view using for each or similar....
Any idea?
There are many ways to do that, this is my way.
Model
Create a class in which your JSON will be deserialized to:
public class RootJson
{
public IEnumerable<station> Stations { get; set; }
}
The RootJson class has a property which will contain a list of station's instances (your class):
public class station
{
public string name { get; set; }
public bool free { get; set; }
}
Controller
Then, deserialize your JSON using:
var deserialized = JsonConvert.DeserializeObject<RootJson>(json);
And pass the stations to the view:
return View(deserialized.Stations);
View
In your view you have to specify the type of the data passed, in this case IEnumerable<station>. So, at the top of your Stations.cshtml add:
#model IEnumerable<station>
You can use a foreach to iterate over the model:
#foreach(var station in Model)
{
<p>#station.name</p>
}
Edit: Full code for clarification
Model
RootJson.cs
public class RootJson
{
public IEnumerable<station> Stations { get; set; }
}
station.cs
public class station
{
public string name { get; set; }
public bool free { get; set; }
}
Controller
Inside YourController.cs
public ActionResult Stations() {
string json = YourMethodToGetJsonAsString();
var deserialized = JsonConvert.DeserializeObject<RootJson>(json);
return View(deserialized.Stations);
}
View
Stations.cshtml
#model IEnumerable<station>
#foreach(var station in Model)
{
<p>#station.name</p>
}
One of simplest way is to use ViewBag like this:
public ActionResult Stations()
{
string jsonString = "{ \"stations\":[{\"name\":\"aname\",\"free\":false},{\"name\":\"anothername\",\"free\":true}]}";
ViewBag.stations = ((dynamic)Newtonsoft.Json.JsonConvert.DeserializeObject(jsonString)).stations;
return View();
}
and inside cshtml for ex.
<p>#ViewBag.stations[0].name</p>
Model
public class Stations
{
public List<Station> Station { get; set; }
}
public class Station
{
public string Name { get; set; }
public bool Free { get; set; }
}
Controller
// Deserialising JSON
var station = JsonConvert.DeserializeObject<Stations>(response);
// Pass result to view
return View(station.Station);
View
At the top of your Stations.cshtml add:
#model IEnumerable<Station>
You can use a foreach to iterate over the model:
#foreach(var station in Model)
{
<p>#station.Name</p>
}

Search method by string value MVC 5

A a part of my project i need to find a way to search my object by a string and show a result in view. Your help is appreciated.
in my MainMedia view i have a sidesection were i manually pass a string value to a SearchMedia method:
#section SideBar{
<ul>
<li> #Html.ActionLink("Astronomy", "SearchMedia", new {searchString = "Astronomy" })</li>
<li> #Html.ActionLink("World", "SearchMedia", new { searchString = "World" })</li>
<li> #Html.ActionLink("Movies", "SearchMedia", new { searchString = "Movies" })</li>
</ul>
}
This method should check every object if TagsEnum string and then display an object in SearchMedia view.
Here is my Media class
public class Media
{
public int Id { get; set; }
public string title { get; set; }
public string description { get; set; }
public string body { get; set; }
public string ImagePath { get; set; }
public string VideoLink { get; set; }
public string Source { get; set; }
public string tags { get; set; }
public TagsEnum TagsEnum { get; set; }
}
TagsEnum Class
public enum TagsEnum
{
[Display(Name = "Astronomy and space")]
Astronomy,
[Display(Name = "World around us")]
World,
[Display(Name = "Movies, video")]
Movies
}
and finaly MediaMainController SearchMedia method
public ActionResult SearchMedia(string searchString)
{
db.Medias.Where(i => i.TagsEnum.ToString() == searchString);
return View(db.Medias.OrderBy(it => it.Title));
}
As i understand .Where() should find a match and return an object, however it is not working. How i can sort it out? Perhaps there are other ways to do it? Thank you
Update
I have changed it like this:
var result = db.Medias.Where(TagsEnum => TagsEnum.ToString() == searchString);
return View(result.OrderBy(it => it.title));
but i still dont see the results to be sorted by search
Update 2
I have a class MediaViewModel which i use to create a list of objects, it looks like this:
public class MediaViewModel
{
public List<Media> media { get; set; }
public List<Video> video { get; set; }
}
If i set up SearchMedia View like this
#model PhClub.Models.MediaViewModel
#foreach (var b in Model.media)
{}
i'm getting an error:
The model item passed into the dictionary is of type System.Linq.Enumerable+WhereListIterator 1[PhClub.Models.Media], but this dictionary requires a model item of type PhClub.Models.MediaViewModel.
If i set it up as
`#model IEnumerable<PhClub.Models.Media>
#foreach (var b in Model)
{}`
it is saying Values of type 'Media' can not be converted to string.
I think i need to change SearchMedia method to support MediaView class, but i didnt figure it out yet. Help is appreciated
You should assign it to a variable and use it,
var result = db.Medias.Where(i => i.TagsEnum.ToString() == searchString);
return View(result.OrderBy(it => it.Title));

Access many to one Entity Framework model properties in RenderAction view using lambda expressions

I tried to access the property of model (FilePath) in my Render action, but without success.
I have these models:
public class Offer
{
public int OfferID { get; set; }
public string Reference { get; set; }
public string Title { get; set; }
public virtual ICollection<FilePath> FilePaths { get; set; }
}
public class FilePath
{
public int FilePathId { get; set; }
public string FileName { get; set; }
public virtual Offer Offer { get; set; }
}
In controller offer:
public PartialViewResult Category()
{
var offers = db.Offers
.Include(i => i.FilePaths)
// i tried with .Include(“FilePaths”) with no sucess
.ToList();
return PartialView("_OfferBoxList", offers);
}
Showing with:
#{Html.RenderAction("Category", "Offer");}
The problem is in partial action view:
#foreach (var item in Model)
{
#item.Title // This work
#item.FilePath.FileName // This NOT work
}
Output error:
'System.Data.Entity.DynamicProxies.Offer_278BF6A1F89FB9514329AC922E992AEBD19368D66A4460B6BEBA1BB2256CAFC3' does not contain a definition for 'FilePath'
Thanks for help.
Each Offer has a list of FilePath instances (ICollection<FilePath> FilePaths), so you can't just access the Offer.FilePath.FileName property, you have to get for instance the first one (depending on what you need), using something like:
#foreach (var item in Model)
{
#item.Title // This work
#item.FilePaths.First().FileName // Take the first FilePath object from the collection
}
You should really not be loading your entities into views..
I generally create Data Transfer Objects (DTOs) for a multitude of purposes. Not only does it help me shape the query expression (IQueryable<T>), but I also use so that I have concert types (instead of generated dynamic proxy class types - like the type you're seeing in your runtime exception) in control properties, like dataItem and dataSource, which are often used in runtime bindable dynamic/reflective controls (grids, listviews)..
Note
It is possible to disable the generated dynamic proxy types by declaring and passing your own DbContextConfiguration instance to EntityFramework; However, doing so will affect EntityFramework supporting features.
public class OfferDTO
{
public int OfferID { get; set; }
public string Reference { get; set; }
public string Title { get; set; }
public IEnumerable<string> FileNames { get; set; }
}
you controller function would then look like:
public PartialViewResult Category()
{
var offers = db.Offers.Select<Offer, OfferDTO>( (entity) => new OfferDTO() {
OfferID = entity.OfferID,
Reference = entity.Reference,
Title = entity.Title,
FileNames = entity.FilePaths.Select<FilePath, string>( filePath => filePath.FileName).AsEnumerable()
});
return PartialView("_OfferBoxList", offers.ToList());
}
then,
#{Html.RenderAction("Category", "OfferDTO");}
#foreach (var item in Model) // Model is IEnumerable<OfferDTO>
{
#item.Title
#item.FileNames.First()
}
Additionally,
you can create an IQueryable<IQueryable<TEntityDTO>> to suit your purposes and perform a SelectMany on it to flatten the results.
public class OfferTitleFilenameDTO
{
public string Title {get;set;}
public string Filename {get;set;}
}
public PartialViewResult Category()
{
var offers = db.Offers.Select<Offer, IQueryable<OfferTitleFilenameDTO>>( (entity) => entity.FilePaths.Select<FilePath, OfferTitleFilenameDTO>(filePath => new OfferTitleFilenameDTO() {
Filename = filePath.FileName,
Title = entity.Title
})
});
return PartialView("_OfferBoxList", offers.SelectMany(dtos => dtos));
}
then,
#{Html.RenderAction("Category", "OfferTitleFilenameDTO");}
#foreach (var item in Model) // Model is IEnumerable<OfferTitleFilenameDTO>
{
#item.Title
#item.Filename
}

Cloning A Class

I have two classes which contain the same fields, however one inherits some properties from somewhere else and the other does not.
I have created a generic list using the class "ZEUS_ResearchStocksHistory" , but then I need to clone all of the fields over to the other list "ZEUS_ResearchStocksHistoryWithExcel". I don't want to have to loop through each field in one list and populate the other, or write some sort of linq join, there must be a faster way?
The reason I can't use the same class in both instances is that when inheriting the ExcelReport function it adds additional fields which I do not want when I display this list in a data grid.
internal class ZEUS_ResearchStocksHistory
{
public String Amendment { get; set; }
public String AmendedBy { get; set; }
public String Sedol { get; set; }
public String Date { get; set; }
}
internal class ZEUS_ResearchStocksHistoryWithExcel : ExcelReport
{
public String Amendment { get; set; }
public String AmendedBy { get; set; }
public String Sedol { get; set; }
public String Date { get; set; }
}
Is this possible?
Thanks
Did you have a look at automapper?
example from codeproject:
CustomerViewItem customerViewItem =
Mapper.Map<Customer, CustomerViewItem>(customer);
Check out Automapper, which is designed to do exactly this. Automapper is up on NuGet.
http://lostechies.com/jimmybogard/2009/01/23/automapper-the-object-object-mapper/
You could do something as simple as:
Mapper.CreateMap<ZEUS_ResearchStocksHistory, ZEUS_ResearchStocksHistoryWithExcel>();
var newObject = Mapper.Map<ZEUS_ResearchStocksHistory, ZEUS_ResearchStocksHistoryWithExcel>(oldObject);
Or, since you said you have a list, you could do:
var newList = oldList.Select(x => Mapper.Map<ZEUS_ResearchStocksHistory, ZEUS_ResearchStocksHistoryWithExcel>(x));

Categories

Resources