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>
}
Related
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.
I've got a list of objects in JSON that isn't recognized by a WebApi2 controller
The JSON list is the following:
{
"FirstObjectType": [{"Name": "the_name"}],
"SecondObjectType": [{"Label": "01_obj"}, {"Label": "02_obj"}]
}
The Model class is:
public class CompositeObject
{
[JsonProperty("FirstObjectType")]
public List<FirstObject> fo { get; set; }
[JsonProperty("SecondObjectType")]
public List<SecondObject> so { get; set; }
}
The controller is:
public IHttpActionResult PostList([FromBody] CompositeObject jsonList)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
List<FirstObject> fo_list = jsonList.fo;
foreach (var item in fo_list)
{
db.FirstObject.Add(item);
db.SaveChanges();
}
return StatusCode(HttpStatusCode.OK);
}
When I submit the Post action, the controller recognize both lists in CompositeObject jsonList as Null
There is a problem in your model, where names are not being matched. You have to update model as:
public class FirstObjectType
{
public string Name { get; set; }
}
public class SecondObjectType
{
public string Label { get; set; }
}
public class RootObject
{
public List<FirstObjectType> FirstObjectType { get; set; }
public List<SecondObjectType> SecondObjectType { get; set; }
}
I have assumed that FirstObjectType contains string with name Name and SecondObjectType contains string with name Label. Make sure to use same names for properties of FirstObjectType and SecondObjectType class as in JSON string.
The issue was in the client code because I missed to set the Content-type as application/json in the header section.
In this way the WebApi server doesn't recognize in the right way the JSON object (I think that the server look for a x-www-form-urlencoded type)
So, the code above is right, but I have found another solution
In the WebApi controller:
using Newtonsoft.Json.Linq;
public IHttpActionResult PostList([FromBody] JObject ReceivedObjectsList)
{
var receivedLists = ReceivedObjectsList.Properties();
List<FirstObject> fo = ReceivedObjectsList["FirstObjectType"].ToObject<List<FirstObject>>();
List<SecondObject> so = ReceivedObjectsList["SecondObjectType"].ToObject<List<SecondObject>>();
...
}
Problem is: I want to run 3 different actions but instead of that i want to fed all data from single action in a bigger model.
I am using:
public class SearchScrapClass
{
public WClass WClass { get; set; }
public SClass SClass { get; set; }
public YClass YClass { get; set; }
}
public class WClass
{
public string title { get; set; }
public string link { get; set; }
}
public class SClass
{
public string title { get; set; }
public string link { get; set; }
}
public class YClass
{
public string title { get; set; }
public string link { get; set; }
}
I am using LINQ to add data in these models.
I am using :
var wikians = from info in document.DocumentNode.SelectNodes("//div[#id='span']")
from link in info.SelectNodes("div//a").Where(x => x.Attributes.Contains("href"))
select new SearchScrapClass //Main Bigger Class
{
WClass.link= link.Attributes["href"].Value, //ERROR: How to add to WClass's url ?
WClass.title= link.InnerText //ERROR: How to add to WClass's url ?
}
var wikians = from info in document.DocumentNode.SelectNodes("//div[#id='results']")
from link in info.SelectNodes("p//a").Where(x => x.Attributes.Contains("href"))
select new SearchScrapClass //Main Bigger Class
{
YClass.link= link.Attributes["href"].Value, //ERROR: How to add to YClass's url ?
YClass.title= link.InnerText //ERROR: How to add to YClass's url ?
}
//Also for the 3rd class (model)
return View(wikians); //and then return bigger class model so that i can access them in view
This is one way i want to add data to link and title of all the classes.
My try is to add data to all 3 classes from different sources and pass the bigger model to view so that i can access all the classes as:
#model SearchScrapClass
#using(Html.BeginForm()) {
#Html.EditorFor(o => o.WClass.link)
...
}
Please suggest a way
Thanks
To Expand on my comment, I would suggest creating a ViewModel folder for organization sake. in this add the view model
public class SearchScrapClassViewModel
{
SearchScrapClass searchScrap;
WClass wClass;
SClass sClass;
YClass yClass;
}
In your controller then you instantiate the new viewmodel
SearchScrapClassViewModel model = new SearchScrapClassViewModel
{
....add in your logic to fill your class objects here
}
return view(model);
then in your view add the using for the viewmodel.
#using SearchScrapClassViewModel
You can pass multiple model by creating a new model class which will contain multiple objects.
public class MultiModel
{
SearchScrapClass searchScrap;
WClass wClass;
SClass sClass;
YClass yClass;
}
See the tuple tutorial http://www.dotnetperls.com/tuple or this one http://msdn.microsoft.com/en-us/library/system.tuple(v=vs.110).aspx
Like controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var first = new FirstModel();
var second = new SecondModel();
return View(Tuple.Create(first,second));
}
}
And the view:
#model Tuple
<div>
#Model.Item1.FirstModelProp
#Model.Item2.SecondModelProp
</div>
SearchScrapClassViewModel model = new SearchScrapClassViewModel
{
....add in your logic to fill your class objects here
}
what logic we apply here " ....add in your logic to fill your class objects here"
I have a class that requires another class to be specified, but I don't want the MVC ModelState validator to check whether the secondary model is valid. Is this possible?
Here's a brief overview:
My entities look something like this:
public class WidgetType
{
public long Id { get; private set; }
[Required]
public string Name { get; set; }
...
}
public class Widget
{
public long Id { get; private set; }
[Required]
public string Name { get; set; }
[Required]
public WidgetType WidgetType { get; set; }
...
}
I have them encapsulated in a WidgetViewModel class that I'm passing to/from the View like this:
public class WidgetViewModel
{
public Widget Widget { get; set; }
public ICollection<WidgetType> WidgetTypes
{
get
{
return _repository.GetWidgets();
}
}
...
}
My view looks something like this:
...
#Html.DropDownListFor( m => m.Widget.WidgetType.Id, new SelectList( new EquipmentViewModel().EquipmentTypes, "Id", "Name" ) )
...
All of this works except for validation. ModelState.IsValid is always false because "Widget.WidgetType.Name" is required. I need the user to select a WidgetType, but I don't want ModelState to be validated deeper than "Widget.WidgetType.Id" (which should be all that Widget needs for its foreign key?).
Is there a better way to do this? I feel like there should be some way to validate without recursively inspecting deeper into the properties, but I can't find it. What am I missing...?
public class WidgetViewModel
{
[Required]
public string Name { get; set; }
[Required]
public WidgetType WidgetTypeId { get; set; }
public SelectList WidgetTypes
{
get
{
//This should be popuplated in your controller or factory not in the view model
retun new SelectList{ _repository.GetWidgets(),"Id","Name");
}
}
}
In your view
#Html.DropDownListFor( m => m.WidgetTypeId, Model.WidgetTypes)
And in your controller
public ActionResult Create(WidgetViewModel model)
{
Widget widget = new Widget{
Name = model.Name,
WidgetType = yourManager.GetWidgetTypeByID(model.WigetTypeId);
};
yourManager.Create(widget);
//...
}
If all you need in your view is the WidgetID then you don't need to include the entire Widget in the WidgetViewModel. Just have property called WidgetID. View model classes should have only the data the is necessary for the view.
In the controller action method that is called when you submit the form, you can use the WidgetID to fetch the Widget object from the database if it is needed.
http://blog.stevensanderson.com/2010/02/19/partial-validation-in-aspnet-mvc-2/ gives an example of partial validation
I am coverting my app from webforms to mvc, at the moment i am at a design issue (well i just dont know how to do it in mvc).
Basically my model would be something like this:
public class DamagedItem
{
public Int32 LoanId {get;set;}
public String IdentityCode {get;set;}
public virtual ICollection<DamagedItems> DamagedItems {get;set;}
}
In my controller i would like to do:
public ActionResult Add(DamagedItem damagedItem)
{
//Do update logic here
}
Then in my view i can add to the ICollection as needed.
But, i can't do this because if i try and access the ICollection from my controller it is null.
Here is an image of when i want to do:
I just dont know how to lay it out in my view, how to i add such items to my ICollection, update the view then when i need to save i have access to what i have added from my controller?
Thanks,
Nick
Edit:
I was thinking of using a partial in the view and doing all the logic for the bottom half using ajax and storing it in a session variable, but i would prefer NOT to make it reliant on ajax.
It is better to separate: you shoud have 2 actions, which produce 2 view.
You should have LoadInformationModel classe:
public class LoadInformationModel
{
public string StudentCode { get; set; }
public string FirstName { get; set; }
// etc..
public ICollection<Damage> Type { get; set; }
}
corresponding action
[HttpGet]
public ActionResult LoanInformation(int id)
{
var loanInfo = // get data by given id..
var model = new LoadInformationModel {
StudentCode = loanInfo.StudentCode,
// etc
Type = new List<Damage> { new Damage { Value = "Damaged"}, new Damage { Value = "Damaged Again" }
}
return View(model);
}
As well as RepairDataModel class
public class RepairDataModel
{
public bool CoveredByWarranty { get; set; }
public ICollection Status { get; set; }
}
And corresponding action
[HttpGet]
public ActionResult Repair(int id)
{
// logic
return View(model);
}
Your task is to create Post handler, that would save data to DB then form submitted
[HttpPost]
public ActionResult(RepairDataModel model)
{
// save to db
return View();
}
The view returned by Index() method, could be created like
#Html.RenderAction("LoanInformation")
#Html.RenderAction("Repair")
The rest depends on your desing and imagination. I hope that would give you direction.
What I can see is only the DamagedItem lacks a contructor with values for Collection;
public class DamagedItem
{
public DamagedItem()
{
DamagedItems = new List<DamagedItems>();
DamagedItems.Add(new DamagedItem { Description = "Damaged" } );
}
public Int32 LoanId {get;set;}
public String IdentityCode {get;set;}
public virtual ICollection<DamagedItems> DamagedItems {get;set;}
}