Custom JsonSerialization - asp.net

I have a simple class in asp.net mvc that looks like this:
public class JsonResponseItem
{
public string Key { get; set; }
public string Value { get; set; }
public JsonResponseItem(string key, string value)
{
Key = key;
Value = value;
}
}
In my controllers I create a list of that type
List<JsonResponseItem> response = new List<JsonResponseItem>();
so I can easily manage and add to the Json response. A dictionary object is kind of hard to do that with.
When I return the json object
return Json(response);
It deserializes it so I have to reference everything by index first, because of the list. So if I had a property called "IsValid" I would have to reference it like this "IsValid[0]". I have way too much javascript code to make these changes.
How could I deserialize the JsonResponseItem class so I don't need the index reference in there?
Thanks!

A Dictionary<string, string> would serialize into exactly the Json you're asking for. If you don't want to expose directly a dictionary, wrap it around in another class or use Json(response.ToDictionary(item => item.Key).

Related

How to return serialized JSON string, without further serialization from ASP.Net Core API

This question is similar to Return "raw" json in ASP.NET Core 2.0 Web Api but slightly more complicated.
I have a mixed content, some class like:
public class ResponseModel
{
public Guid Id { get; set; }
public DateTime TimesStamp { get; set; }
// this is actually JSON serialized data,
// which the function just passes through and doesn't need to understand
public string Data { get; set; }
}
Currently, a response would contain the Id and TimeStamp serialized correctly and Data would just be a string which would need to be deserialized one more time.
I'd instead want Data to be just pointing to the "Raw" json string, which I set it to, without further escaping it.
We don't make use of content negotiation, we only support JSON request and response, so this would be fine.
I know that I could deserialize the json string into a dynamic object and that would work, but why should the string be deserialized just to be serialized again?
So what I would want is something like
public class ResponseModel
{
public Guid Id { get; set; }
public DateTime TimesStamp { get; set; }
public object Data { get; set; }
}
but without the need to spend unnecessary time to deserialize and again serialize the content of the json string.
Not possible. If it's a string, you know it's JSON, but the serializer has no way of knowing that. However, even if it could somehow determine that it's a JSON string, it would still need to internally deserialize it so it could work it into the rest of the object, before serializing the whole thing - effectively no different than doing it yourself.

OData paging with expand issue

I'm using OData v5/Web API 2.2 to create an endpoint that will return a list of employees from each company.
My problem occurs when I try to implement server-side paging while also using the OData $expand property. When I try to make a call to
http://localhost:60067/Companies?$expand=Employees
I get an error that says "Could not find a property named 'Employees' on type 'System.Web.OData.Query.Expressions.SelectAllAndExpand_1OfCompanyApiModel'"
However, when I removed the EnableQuery attribute the call to the endpoint or when I didn't expand it works as expected. Does anyone have an idea of what I am doing wrong? I've been googling this for a while but haven't found anything.
Here are some code snippets -
Data Models:
public class CompanyApiModel
{
[Key]
public Guid CompanyGuid { get; set; }
[Required]
public string Name { get; set; }
// other properties
public List<EmployeeApiModel> Employees { get; set; }
}
public class EmployeeApiModel
{
[Key]
public Guid EmployeeGuid { get; set; }
[Required]
public string Name { get; set; }
// other properties
}
CompaniesController.cs:
[EnableQuery(PageSize = 10)] // If I comment this out everything works
//[EnableQuery] // This fails as well
public IHttpActionResult Get(ODataQueryOptions<CompanyApiModel> queryOptions)
{
var companies = GetCompanies(queryOptions);
return Ok(companies);
// return Ok(companies.AsQueryable()); // This doesn't work either
}
WebApiConfig.cs:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
var routingConventions = ODataRoutingConventions.CreateDefault();
routingConventions.Insert(0, new OptionsRoutingConvention());
config.MapODataServiceRoute("odata", null, GetEdmModel(), new DefaultODataPathHandler(), routingConventions);
// below code allows endpoints to respond with either XML or JSON, depending on accept header preferences sent from client
// (default in absence of accept header is JSON)
var odataFormatters = ODataMediaTypeFormatters.Create();
config.Formatters.InsertRange(0, odataFormatters);
config.EnsureInitialized();
}
public static IEdmModel GetEdmModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.Namespace = "Demos";
builder.ContainerName = "DefaultContainer";
builder.EntitySet<CompanyApiModel>("Companies");
builder.EntitySet<EmployeeApiModel>("Employees");
var edmModel = builder.GetEdmModel();
return edmModel;
}
}
Figured out the problem. We were overriding the EnableQuery attribute somewhere in our code and calling it EnableMappedQuery and applying it to the controller. Thus instead of having [EnableQuery(PageSize = 10)] I should have had [EnableMappedQuery(PageSize = 10)].
EnableQuery Attribute do many works,
1. it will validate the queryoption for you.
2. it will apply the queryoption for you.
3. it can add some querysettings like PageSize.
Your scenario not working is because your GetCompanies is already applied the queryoption, so when EnableQuery get the result and apply the queryoption again, it fails, it can't find the expand property, my suggestion is just return original Company and let EnableQuery do the reset of work for you, ODataQueryOption in parameter is also not needed.
If you realy do some custom work in GetCompanies and don't need EnableQuery to apply for you, you can add PageSize in ODataQuerySettings when you call method ODataQueryOptions.ApplyTo(IQueryable, ODataQuerySettings).

Map all properties of a class using reflection

I have two domain classes
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string HouseName { get; set; }
public string StreetName { get; set; }
public string PinCode { get; set; }
}
I want to map object of Employee class to another class.
I am using reflection to map empData object to another object. The code i used is
private void GetValues(object empData)
{
System.Type type = empData.GetType();
foreach (PropertyInfo pInfo in type.GetProperties())
{
//do some stuff using this pInfo.
}
}
I could easily map all the properties except the Address property in the emp object which is an object of another class.
So how can i map all the properties irrespective of its type ? i.e, if address contains object of another class it should also get mapped.
Can't you use AutoMapper for mapping classes?
You can know the type of property you are mapping by
if (propertyInfo.PropertyType == typeof(Address))
{ // do now get all properties of this object and map them}
Assuming that you want to be able to do this on any type of object and not just this specific one, you should use some sort of recursive solution. However if it's just for this object - why are you even using reflection? To me it just adds unnecessary complexity to something as simple as mapping six properties to another set of objects.
If you want to get more concrete help with code examples, you'll have to give us some more context. Why does a method named "GetValues" has a return type of void? I have a hard time coding up an example with that in mind. :)

how to edit json data runtime?

i want to add the data to existing generated json data in my asp.net mvc application , what i have to do ?
means , suppose i have class Company that having list of Departments as property. but json serialization is not able to support such kind of circular reference. so i thought how if, i get serialize the Company object first without list of Departments, then get departments for each company and then serialize this list and append data to company serialized data. i know this may be wrong way . but i have to do because of time ultimatum. please guide.
You could define a view model where you won't have any circular references:
public class DepartmentViewModel
{
public string DepartmentName { get; set; }
}
public class CompanyViewModel
{
public IEnumerable<DepartmentViewModel> Departments { get; set; }
}
and then you would map between your model and view model (you could use AutoMapper for this) and finally return the view model to the view.
For example:
public ActionResult Index()
{
var companies = _repository.GetCompanies();
var companiesVM = Mapper.Map<IEnumerable<Company>, IEnumerable<CompanyViewModel>>(companies);
return Json(companiesVM, JsonRequestBehavior.AllowGet);
}
Now you no longer would have circular references and you will be able to successfully serialize the view model to JSON and you would only pass the information that is required to the view.

Deserializing JSON objects as List<type> not working with asmx service

I am having trouble deserializing my JSON string. I have a class of type person with public properties for sequence number of type int, first name, and last name. I want to pass an array of these objects in JSON format and have them deserialized as a list so I can loop through them on the server, but ASP.NET says something about not being supported to be deserialized as an array. I have validated the JSON I am producing, and it is valid. Is there something special about the JSON that ASP.NET needs to have before it can deserialize? The funny thing is if I serialize a list<person> object to JSON it looks exactly like the JSON I am producing. I must be missing something... To clarify, I'm using the ASP.NET Ajax library to deserialize. This is what I get back from the web service:
{"Message":"Type \u0027System.Collections.Generic.IDictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Object, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]\u0027 is not supported for deserialization of an array."
Actually unfortunately this doesn't seem to have anything to do with deserializing, it appears that you can't pass an array of JSON objects to an asmx web service. Am I correct? If you can't do that, is it possible to pass a collection of JSON objects to a web service and have them processed on the server with ASP.NET and C#?
Update:
OK, here is my code. Here is the person class:
public class person
{
public person()
{
//
// TODO: Add constructor logic here
//
}
public int seq
{
get;
set;
}
public string firstName
{
get;
set;
}
public string lastName
{
get;
set;
}
}
And here is my JSON string:
[{"seq":1,"firstName":"Chris","lastName":"Westbrook"},
{"seq":2,"firstName":"sayyl","lastName":"westbrook"}]
And here is the code I'm using
[WebMethod]
public void updatePeople(string json)
{
IList<person> people =
new JavaScriptSerializer().Deserialize<IList<person>>(json);
//do stuff...
}
I figured it out. I wasn't wrapping my JSON in an object like ASP.NET Ajax requires. For future viewers of this question, all JSON objects must be wrapped with a main object before being sent to the web service. The easiest way to do this is to create the object in JavaScript and use something like json2.js to stringify it. Also, if using an asmx web service, the objects must have a __type attribute to be serialized properly. An example of this might be:
var person=new object;
person.firstName="chris";
person.lastName="Westbrook";
person.seq=-1;
var data=new object;
data.p=person;
JSON.stringify(data);
This will create an object called p that will wrap a person object. This can then be linked to a parameter p in the web service. Lists of type person are made similarly, accept using an array of persons instead of just a single object. I hope this helps someone.
Could you show the JSON string you are trying to deserialize and the way you are using the Deserialize method? The following works fine:
using System;
using System.Collections.Generic;
using System.Web.Script.Serialization;
namespace Test
{
class Program
{
class Person
{
public int SequenceNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public static void Main()
{
string json = "[{\"SequenceNumber\":1,\"FirstName\":\"FN1\",\"LastName\":\"LN1\"},{\"SequenceNumber\":2,\"FirstName\":\"FN2\",\"LastName\":\"LN2\"}]";
IList<Person> persons = new JavaScriptSerializer()
.Deserialize<IList<Person>>(json);
Console.WriteLine(persons.Count);
}
}
}
Or even simpler, when you are doing the $.ajax(...) use
data:"{\"key\":"+JSON.stringify(json_array)+"}",
and then on the other side of the code, make your function use the parameter "object key"
[WebMethod]
public static return_type myfunction(object key){...}
SERVER SIDE
[WebMethod]
public void updatePeople(object json)
CLIENT SIDE
var person = "[{"seq":1,"firstName":"Chris","lastName":"Westbrook"}
,{"seq":2,"firstName":"sayyl","lastName":"westbrook"}]";
var str = "{'json':'" + JSON.stringify(person) + "'}";
I think the problem is what type you have to deserialize. You are trying to deserialize type
IList
but you should try to deserialize just
List
Since interface can not be instantiated this might is the root problem.

Resources