Proper way to return JSON list in Web API with MVC and partial model - json

I am trying to use Web API to grab certain fields from my MVC controller. I can't seem to match the right type with the right list. I am fine with converting everything to string.
I either get an error in code (can not convert types), or if I get it to compile, I get this error:
"The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'."
From other similar posts, people responded with how to create a list, but not with the declaration of the return value of the Get. Please include both.
Also I would prefer not to add additional controllers as I need to do this on a number of my models.
Here is my code--note you can see I tried a number of different ways:
public class APICLIENTsController : ApiController
{
private ApplicationDbContext db = new ApplicationDbContext();
// GET api/<controller>
public IEnumerable<string> Get()
//public IEnumerable<CLIENT> Get()
{
//return db.CLIENTs.OrderBy(x => x.CLIENTNAME).ToList();
string[] listOfUsers = db.CLIENTs.OrderBy(x => x.CLIENTNAME).Select(r => new
{
ID = r.CLIENTID.ToString(),
NAME = r.CLIENTNAME
});
return listOfUsers.ToList();
//return db.CLIENTs.Select(x => new { x.CLIENTNAME }).ToArray();
}

If you want to return JSON use the
JsonResult
type.
public JsonResult Get()
{
//return db.CLIENTs.OrderBy(x => x.CLIENTNAME).ToList();
string[] listOfUsers = db.CLIENTs.OrderBy(x => x.CLIENTNAME).Select(r => new
{
ID = r.CLIENTID.ToString(),
NAME = r.CLIENTNAME
});
return Json(listOfUsers.ToList(), JsonRequestBehavior.AllowGet);
}

Your query is returning a collection of anonymous objects, not string[] so it will throw an exception. Even if you were to generate string[] by concatenating the CLIENTID and CLIENTNAME properties, it would be a little use to the client.
Create a model to represent what you need to return to the view
public class ClientVM
{
public int ID { get; set; }
public string Name { get; set; }
}
and modify your method to
public IEnumerable<ClientVM> Get()
{
IEnumerable<ClientVM> model = db.CLIENTs.OrderBy(x => x.CLIENTNAME).Select(r => new ClientVM
{
ID = r.CLIENTID,
Name = r.CLIENTNAME
});
return model;
}
Side note: depending on how your calling and consuming this in the client, you may need to change the Content-Type to specifically return json (refer these answers for more detail)

Related

ASP .NET MVC Drop Down List throwing multiple errors when empty [duplicate]

I have the following view model
public class ProjectVM
{
....
[Display(Name = "Category")]
[Required(ErrorMessage = "Please select a category")]
public int CategoryID { get; set; }
public IEnumerable<SelectListItem> CategoryList { get; set; }
....
}
and the following controller method to create a new Project and assign a Category
public ActionResult Create()
{
ProjectVM model = new ProjectVM
{
CategoryList = new SelectList(db.Categories, "ID", "Name")
}
return View(model);
}
public ActionResult Create(ProjectVM model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Save and redirect
}
and in the view
#model ProjectVM
....
#using (Html.BeginForm())
{
....
#Html.LabelFor(m => m.CategoryID)
#Html.DropDownListFor(m => m.CategoryID, Model.CategoryList, "-Please select-")
#Html.ValidationMessageFor(m => m.CategoryID)
....
<input type="submit" value="Create" />
}
The view displays correctly but when submitting the form, I get the following error message
InvalidOperationException: The ViewData item that has the key 'CategoryID' is of type 'System.Int32' but must be of type 'IEnumerable<SelectListItem>'.
The same error occurs using the #Html.DropDownList() method, and if I pass the SelectList using a ViewBag or ViewData.
The error means that the value of CategoryList is null (and as a result the DropDownListFor() method expects that the first parameter is of type IEnumerable<SelectListItem>).
You are not generating an input for each property of each SelectListItem in CategoryList (and nor should you) so no values for the SelectList are posted to the controller method, and therefore the value of model.CategoryList in the POST method is null. If you return the view, you must first reassign the value of CategoryList, just as you did in the GET method.
public ActionResult Create(ProjectVM model)
{
if (!ModelState.IsValid)
{
model.CategoryList = new SelectList(db.Categories, "ID", "Name"); // add this
return View(model);
}
// Save and redirect
}
To explain the inner workings (the source code can be seen here)
Each overload of DropDownList() and DropDownListFor() eventually calls the following method
private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata,
string optionLabel, string name, IEnumerable<SelectListItem> selectList, bool allowMultiple,
IDictionary<string, object> htmlAttributes)
which checks if the selectList (the second parameter of #Html.DropDownListFor()) is null
// If we got a null selectList, try to use ViewData to get the list of items.
if (selectList == null)
{
selectList = htmlHelper.GetSelectData(name);
usedViewData = true;
}
which in turn calls
private static IEnumerable<SelectListItem> GetSelectData(this HtmlHelper htmlHelper, string name)
which evaluates the the first parameter of #Html.DropDownListFor() (in this case CategoryID)
....
o = htmlHelper.ViewData.Eval(name);
....
IEnumerable<SelectListItem> selectList = o as IEnumerable<SelectListItem>;
if (selectList == null)
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
MvcResources.HtmlHelper_WrongSelectDataType,
name, o.GetType().FullName, "IEnumerable<SelectListItem>"));
}
Because property CategoryID is typeof int, it cannot be cast to IEnumerable<SelectListItem> and the exception is thrown (which is defined in the MvcResources.resx file as)
<data name="HtmlHelper_WrongSelectDataType" xml:space="preserve">
<value>The ViewData item that has the key '{0}' is of type '{1}' but must be of type '{2}'.</value>
</data>
according to stephens (user3559349) answer, this can be useful:
#Html.DropDownListFor(m => m.CategoryID, Model.CategoryList ?? new List<SelectListItem>(), "-Please select-")
or in ProjectVM:
public class ProjectVM
{
public ProjectVM()
{
CategoryList = new List<SelectListItem>();
}
...
}
Most Likely Caused some sort of error redirecting to your page and you not initializing your model's drop down lists again.
Make sure that you initialize your drop downs in either the model's constructor or every time before you send said model to the page.
Otherwise you will need to maintain the state of the drop down lists either through the view bag or through the hidden value helpers.
OK, the poster's canned answer neatly explained why the error occurred, but not how to get it to work. I'm not sure that's really an answer, but it did point me in the right direction.
I ran into the same issue and found a slick way to resolve it. I'll try to capture that here. Disclaimer - I work on web pages once a year or so and really don't know what I'm doing most of the time. This answer should in no way be considered an "expert" answer, but it does the job with little work...
Given that I have some data object (most likely a Data Transfer Object) that I want to use a drop-down list to supply valid values for a field, like so:
public class MyDataObject
{
public int id;
public string StrValue;
}
Then the ViewModel looks like this:
public class MyDataObjectVM
{
public int id;
public string StrValue;
public List<SectListItem> strValues;
}
The real problem here, as #Stephen so eloquently described above, is the select list isn't populated on the POST method in the controller. So your controller methods would look like this:
// GET
public ActionResult Create()
{
var dataObjectVM = GetNewMyDataObjectVM();
return View(dataObjectVM); // I use T4MVC, don't you?
}
private MyDataObjectVM GetNewMyDataObjectVM(MyDataObjectVM model = null)
{
return new MyDataObjectVM
{
int id = model?.Id ?? 0,
string StrValue = model?.StrValue ?? "",
var strValues = new List<SelectListItem>
{
new SelectListItem {Text = "Select", Value = ""},
new SelectListITem {Text = "Item1", Value = "Item1"},
new SelectListItem {Text = "Item2", Value = "Item2"}
};
};
}
// POST
public ActionResult Create(FormCollection formValues)
{
var dataObject = new MyDataObject();
try
{
UpdateModel(dataObject, formValues);
AddObjectToObjectStore(dataObject);
return RedirectToAction(Actions.Index);
}
catch (Exception ex)
{
// fill in the drop-down list for the view model
var dataObjectVM = GetNewMyDataObjectVM();
ModelState.AddModelError("", ex.Message);
return View(dataObjectVM);
)
}
There you have it. This is NOT working code, I copy/pasted and edited to make it simple, but you get the idea. If the data members in both the original data model and the derived view model have the same name, UpdateModel() does an awesome job of filling in just the right data for you from the FormCollection values.
I'm posting this here so I can find the answer when I inevitably run into this issue again -- hopefully it will help someone else out as well.
I had the same problem, I was getting an invalid ModelState when I tried to post the form. For me, this was caused by setting CategoryId to int, when I changed it to string the ModelState was valid and the Create method worked as expected.
In my case the first ID in my list was zero, once I changed the ID to start from 1, it worked.

How to change cas resulting JSON objects back to PascalCase?

I am writing a WebAPICore to return the JSON objects from the database. For unknown reason, the properties are returned as camelCase by default.
I have checked the SQL Script and it does return the correct case for the DataFields. But when I consume the service, the properties of the objects are changed to camelCase automatically.
For example, OfferingID is returned as offeringID
The existing Return JSON object
{
"offeringID": 120842,
"courseCode": "FLTE2A1F/1",
"courseName": "FLT - E2 Certificate in Skills for Working Life (Animals) (QCF)"
}
The format which I want to return
{
"OfferingID": 120842,
"CourseCode": "FLTE2A1F/1",
"CourseName": "FLT - E2 Certificate in Skills for Working Life (Animals) (QCF)"
}
The Model - Offering:
public class Offering
{
[Key]
public int OfferingID { get; set; }
public string CourseCode { get; set; }
public string CourseName { get; set; }
}
My WebAPI Controller Get Method
[HttpGet("{id}")]
public async Task<IActionResult> GetOfferingDetail(int id)
{
var obj = await _context.Offerings.FromSql("dbo.GetOfferingDetail #p0", id).SingleOrDefaultAsync();
if (obj == null)
return NotFound("ID not found");
return new ObjectResult(obj);
}
Configure Services Method in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DbContexts.OakCommonsDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("MyConnection")));
services.AddCors(options => options.AddPolicy("AllowAll", p => p.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()));
var mvccore = services.AddMvc();
mvccore.AddJsonOptions(o => o.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore);
}
Could you please advise me how I could return JSON Objects in the Exact Case as I defined in the Model?
Here is the working code. By default, WebAPI Core is going to use CamelCasePropertyNamesContractResolver(). You need to change it to DefaultContractResolver to render as you defined in the Model.
And DefaultContractResolver is under Newtonsoft.Json.Serialization namespace.
services.AddMvc()
.AddJsonOptions(o => o.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)
.AddJsonOptions(o => o.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver());

Struts2 Convert json array to java object array - not LinkedHashmap

First off my question is very similar to below however I'm not sure if the answers are applicable to my specific problem or whether I just need clarification about how to approach it:
Convert LinkedHashMap<String,String> to an object in Java
I am using struts2 json rest plugin to convert a json array into a java array. The array is sent through an ajax post request and the java receives this data. However instead of being the object type I expect it is received as a LinkedHashmap. Which is identical to the json request in structure.
[
{advance_Or_Premium=10000, available=true},
{advance_Or_Premium=10000, available=true},
{advance_Or_Premium=10000, available=true}
]
The data is all present and correct but just in the wrong type. Ideally I want to send the data in my object type or if this is not possible convert the LinkedHashMap from a list of keys and values into the object array. Here is the class I am using, incoming data is received in the create() method:
#Namespace(value = "/rest")
public class OptionRequestAction extends MadeAbstractAction implements ModelDriven<ArrayList<OptionRequestRest>>
{
private String id;
ArrayList<OptionRequestRest> model = new ArrayList<OptionRequestRest>();
public HttpHeaders create()
{
// TODO - need to use model here but it's a LinkedHashmap
return new DefaultHttpHeaders("create");
}
public String getId()
{
return this.id;
}
public ArrayList<OptionRequestRest> getModel()
{
return this.model;
}
public ArrayList<OptionRequestRest> getOptionRequests()
{
#SuppressWarnings("unchecked")
ArrayList<OptionRequestRest> lReturn = (ArrayList<OptionRequestRest>) this.getSession().get("optionRequest");
return lReturn;
}
// Handles /option-request GET requests
public HttpHeaders index()
{
this.model = this.getOptionRequests();
return new DefaultHttpHeaders("index").lastModified(new Date());
}
public void setId(String pId)
{
this.id = pId;
}
public void setModel(ArrayList<OptionRequestRest> pModel)
{
this.model = pModel;
}
// Handles /option-request/{id} GET requests
public HttpHeaders show()
{
this.model = this.getOptionRequests();
return new DefaultHttpHeaders("show").lastModified(new Date());
}
}
One of the things which is confusing me is that this code works fine and returns the correct object type if the model is not an array. Please let me know if my question is not clear enough and needs additional information. Thanks.

Newtonsoft.Json.JsonConvert not Deserializing EntityCollection Properly

I have an MVC Web App and a Web API Service that I am trying to retrieve information from. The problem is in the CapitalMailOrders entity collection is missing items when it is deserialized on the web side.
The Service uses the below to retrieve the information
var result = db.Contacts
.Include(a => a.IDXPageLinks)
.Include(b => b.ReboGatewayLoginInfoes)
.Include(c => c.SocialMedias)
.Include(d => d.WebSiteInfoes)
.Include(e => e.ContactImages)
.Include(f => f.RealtorSetUpProcesses.Select(f1 => f1.CapitalMailOrders)
.Include(g => g.Contact_CarrierCode_Assignments)
.FirstOrDefault(c => c.ContactID == id);
This code is good and returns the below on the service side. The below image shows 3 CapitalMailOrders which is what there should be.
But when it's deserialized on the Web side I only get 2 the 3rd is null
here is the Web Side Repository Code
public Contact Get(int id)
{
var responseStream =
requestMethod.GetResponseStream(
requestMethod.getRequest("GET", "application/json",
string.Format("{0}/api/contact/{1}", restService, id)).GetResponse());
var contacts = deSerialize<Contact>(responseStream) as Contact;
return contacts;
}
deSerialize is in the base repository class
public class BaseRepository
{
protected readonly string restService = ConfigurationManager.AppSettings["restService"];
protected readonly RequestMethod requestMethod = new RequestMethod();
protected ISerialization _serializer;
protected BaseRepository()
{ }
protected object deSerialize<T>(Stream stream)
{
var retval = _serializer.DeSerialize<T>(stream);
return retval;
}
protected string serialize<T>(T value)
{
var retval = _serializer.Serialize<T>(value);
return retval;
}
}
public class JsonNetSerialization : ISerialization
{
public string Serialize<T>(object o)
{
return JsonConvert.SerializeObject((T)o);
}
public object DeSerialize<T>(Stream stream)
{
return JsonConvert.DeserializeObject<T>(new StreamReader(stream).ReadToEnd());
}
}
Any ideas? Thanks
This post pointed me to the problem and suggested solution. In a nutshell the poster #Darin advised to return the data collection from the web api using models rather than the entity collection. Since there is no databinding to the db context when serializing the data back to the web app theres really no reason to carry the overhead and problems of trying to serialize the entity collection. This blog post goes into more detail.

MVC repository and interface returning Json

I'm new at MVC and can't get this to work. I basically have a Users class, a UserRepository, and a IUser interface.
This is my code:
public class Users
{
public string UserName { get; set; }
public string Department { get; set; }
public string UserType { get; set; }
}
public class UsersRepository : TimeAttendanceMVC.Models.IUsers
{
public Users Return_UserName_Dept()
{
Users U = new Users();
List<Users> LoggedInUser = new List<Users>();
U.UserName = "TestUser";
U.Department = "Finance";
U.UserType = "Administrator";
LoggedInUser.Add(U);
//string json = JsonConvert.SerializeObject(LoggedInUser, Formatting.Indented);
//return json;
return Json(LoggedInUser.ToArray(), JsonRequestBehavior.AllowGet);
}
}
namespace TimeAttendanceMVC.Models
{
public class IUsers
{
List<Users> Return_UserName_Dept();
}
}
There are a few errors that I get. In UsersRepository.cs where i'm returning Json, the error says that "The name Json does not exist in the current context". The error from IUsers.cs is that "Return_UserName_Dept() must declare a body because it is not marked abstract...".
Can anybody please help me with this. I just don't know how this is supposed to work and i'm trying to learn MVC by working on this application. It's actually the FullCalendar application found here - link to FullCalendar. I'm trying to turn it into an MVC application.
EDIT:
Maybe I need to do this:
public JsonResult Return_UserName_Dept()
instead of public Users Return_UserName_Dept()
You should be doing this on your controller in some method which returns a json action (jsonresult). The repository should be returning your data only and all the operations you need to do, whether you're converting data to json or any other logic should happen at the controller or at some helper class which would be called by the controller..
Edit:
In order to have a method which returns a JsonResult, you need to have a reference to System.Web.Mvc.ActionResult and since the repository is usually at the model, you won't have this reference.. another thing is that you might be breaking your design the logic should be available at the controller for what you want
Edit 2:
The code below is from an old post you can see here. Note how the action PopulateDetails gets the user object from the repository and that's all the repository does.. the actual logic is happening inside this method, such as populate the rest of the UserModel class, and then it returns the JsonResult:
public JsonResult PopulateDetails(UserModel model)
{
UserResultModel userResultModel = new UserResultModel();
if (String.IsNullOrEmpty(model.UserId))
{
userResultModel.Message = "UserId can not be blank";
return Json(userResultModel);
}
User user = _userRepository.GetUser(model.UserId);
if (user == null)
{
userResultModel.Message = String.Format("No UserId found for {0}", model.UserId);
return Json(userResultModel);
}
userResultModel.LastName = user.LastName;
userResultModel.FirstName = user.FirstName;
userResultModel.Message = String.Empty; //success message is empty in this case
return Json(userResultModel);
}