Web Api Parameter binding Inconsistent - c#

After several hours of googling and attempting to figure this out, I still can't figure this out. The issue is that the "[FromBody] Route input" parameter for the create method will for some reason bind Route.RouteMember[0].Lab to the provided data, but it will set Route.RouteMember[0].RouteMemberType to null. (This will happen for all items in the RouteMember array, not just the first item) Why isn't RouteMemberType bound to the input, but yet the input it will bind to lab? I must admit that I have sometimes seen it bind to a RouteMemberType but with no discernible cause.
Relevant WebApiConfig
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
json.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
RouteController
[HttpPost]
public Route Create([FromBody] Route input) {
UnitOfWork.Routes.Add(input);
UnitOfWork.SaveChanges();
return input;
}
Route
public class Route : BaseModel {
public String Name { get; set; }
public String ShortName { get; set; }
public virtual ICollection<RouteMember> RouteMembers { get; set; }
}
RouteMember
public class RouteMember : BaseModel {
[ForeignKey("Route")]
public int RouteId { get; set; }
public virtual Route Route { get; set; }
[ForeignKey("Lab")]
public int LabId { get; set; }
public virtual Lab Lab { get; set; }
[ForeignKey("RouteMemberType")]
public int RouteMemberTypeId { get; set; }
public virtual RouteMemberType RouteMemberType { get; set; }
}
Lab
public class Lab : BaseModel {
public String Name { get; set; }
public String ShortName { get; set; }
public String Description { get; set; }
[ForeignKey("Building")]
public int BuildingId { get; set; }
public virtual Building Building { get; set; }
}
RouteMemberType
public class RouteMemberType : BaseModel {
public String Name { get; set; }
}
Request Input
{
"id":0,
"name":"t1",
"shortName":"t1",
"routeMembers":[
{
"active":true,
"labId":11,
"lab":{
"$id":"1",
"building":null,
"name":"t1a",
"shortName":"t1a",
"description":"t1a",
"buildingId":2,
"id":11,
"active":true
},
"routeMemberTypeId":1,
"routeMemberType":{
"$id":"1",
"name":"Destintation",
"id":1,
"active":true
}
},
{
"active":true,
"labId":12,
"lab":{
"$id":"2",
"building":null,
"name":"t2a",
"shortName":"t2a",
"description":"t2a",
"buildingId":2,
"id":12,
"active":true
},
"routeMemberTypeId":2,
"routeMemberType":{
"$id":"2",
"name":"Fringe",
"id":2,
"active":true
}
}
],
"active":true
}

While I still have been unable to completely resolve this issue to my satisfaction, it appears in some part to be related to the
json.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
in the WebApiConfig. Turning object references off, allows the RouteMemberTypes to be bound. To work around this issue, I am using PreserveReferencesHandling.None and doing the referential integrity manually. I will update if I find a better solution

Related

How to generate a JSON class with dynamic name

I don't know if there is an existing name for that case, but I'm trying to retrieve data from NASA API (https://api.nasa.gov/) and I have a simple challenge to catch a list of objects near earth. Here is the JSON response I have from the GET request I do to "https://api.nasa.gov/neo/rest/v1/feed?...."
{
"links": {
"next": "http://www.neowsapp.com/rest/v1/feed?start_date=2021-07-04&end_date=2021-07-04&detailed=false&api_key=NjgpxgSbYHXyFSBI3HaOhRowtjMZgAKv2t4DMRym",
"prev": "http://www.neowsapp.com/rest/v1/feed?start_date=2021-07-02&end_date=2021-07-02&detailed=false&api_key=NjgpxgSbYHXyFSBI3HaOhRowtjMZgAKv2t4DMRym",
"self": "http://www.neowsapp.com/rest/v1/feed?start_date=2021-07-03&end_date=2021-07-03&detailed=false&api_key=NjgpxgSbYHXyFSBI3HaOhRowtjMZgAKv2t4DMRym"
},
"element_count": 6,
"near_earth_objects": {
"2021-07-03": [
{
"links": {
"self": "http://www.neowsapp.com/rest/v1/neo/3701710?api_key=NjgpxgSbYHXyFSBI3HaOhRowtjMZgAKv2t4DMRym"
},
"id": "3701710",
"neo_reference_id": "3701710",
"name": "(2014 WF497)",
"nasa_jpl_url": "http://ssd.jpl.nasa.gov/sbdb.cgi?sstr=3701710",
"absolute_magnitude_h": 20.23,
"estimated_diameter": {
"kilometers": {
}
And that's the way it is built in Visual Studio (using the Special Paste option for JSON)
public class NearEarthObject
{
public Links links { get; set; }
public int element_count { get; set; }
public Near_Earth_Objects near_earth_objects { get; set; }
}
public class Links
{
public string next { get; set; }
public string prev { get; set; }
public string self { get; set; }
}
public class Near_Earth_Objects
{
public _20210703[] _20210703 { get; set; }
}
public class _20210703
{
public Links1 links { get; set; }
public string id { get; set; }
public string neo_reference_id { get; set; }
public string name { get; set; }
public string nasa_jpl_url { get; set; }
public float absolute_magnitude_h { get; set; }
public Estimated_Diameter estimated_diameter { get; set; }
public bool is_potentially_hazardous_asteroid { get; set; }
public Close_Approach_Data[] close_approach_data { get; set; }
public bool is_sentry_object { get; set; }
}
The question is, inside of the element "near_earth_objects", there is an element called "2021-07-03" (the date of the data I requested), the problem is that I am trying to include it into a DataGridView made in .NET C# (Windows Forms, but that doesn't matters here, I think) and the user wants to get the information by date. So, "2021-07-03" is a valid member just for one day, and the user should be able to get data from multiple days.
So, is there a way in C# to get all child objects inside of near_earth_objects without knowing their names since there will be the option to search for asteroids from date X to Y in my application?
Using System.Text.Json
The API response will map to the following classes
public class Neo
{
public Links Links { get; set; }
public int ElementCount { get; set; }
public Dictionary<string, List<NearEarthObject>> NearEarthObjects { get; set; }
}
public class Links
{
public string Next { get; set; }
public string Prev { get; set; }
public string Self { get; set; }
}
public class NearEarthObject
{
public Links Links { get; set; }
public string Id { get; set; }
public string Name { get; set; }
// Other properties
}
The NearEarthObjects is simply a Dictionary, where the key is the formatted date and value is a List containing NearEarthObject
The PropertyNamingPolicy will allow us to support the API's underscore property naming convention.
public class UnderscoreNamingPolicy : JsonNamingPolicy
{
public override string ConvertName(string name)
{
return name.Underscore();
}
}
Example usage
// using using System.Text.Json;
var response = await new HttpClient().GetStringAsync(url);
var neo = JsonSerializer.Deserialize<Neo>(response, new JsonSerializerOptions
{
PropertyNamingPolicy = new UnderscoreNamingPolicy()
});
foreach(var neos in neo.NearEarthObjects)
{
Console.WriteLine(neos.Key);
}
use System.Text.Json, JsonNamingPolicy
demo code
public class DynamicNamePolicy : JsonNamingPolicy
{
public override string ConvertName(string name)
{
var today = DateTime.Today.ToString("yyyy-MM-dd");
if (name.Equals("DateData")) //model property name
return today; //convert to json string property name
return name;
}
}
//data deserialize
string data = ""; //json string
var obj = JsonSerializer.Deserialize<NearEarthObject>(data, new JsonSerializerOptions
{
PropertyNamingPolicy = new DynamicNamePolicy(),
});

JsonException: A possible object cycle was detected which is not supported. This can either be due to a cycle or if the object depth is larger than

In my web API when I run project to get data from the database got this error
.net core 3.1
JsonException: A possible object cycle was detected which is not supported. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32.
These are my codes:
my Model
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string ProductText { get; set; }
public int ProductCategoryId { get; set; }
[JsonIgnore]
public virtual ProductCategory ProductCategory { get; set; }
}
my productCategory class is:
public class ProductCategory
{
public int Id { get; set; }
public string Name { get; set; }
public string CatText { get; set; }
public string ImagePath { get; set; }
public int Priority { get; set; }
public int Viewd { get; set; }
public string Description { get; set; }
public bool Active { get; set; }
public DateTime CreateDate { get; set; }
public DateTime ModifyDate { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
my repo is
public async Task<IList<Product>> GetAllProductAsync()
{
return await _context.Products.Include(p => p.ProductCategory).ToListAsync();
}
my interface
public interface IProductRepository
{
...
Task<IList<Product>> GetAllProductAsync();
...
}
and this is my controller in api project
[Route("api/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
private readonly IProductRepository _productRepository;
public ProductsController(IProductRepository productRepository)
{
_productRepository = productRepository;
}
[HttpGet]
public ActionResult Get()
{
return Ok(_productRepository.GetAllProduct());
}
}
When I run API project and put this URL: https://localhost:44397/api/products
I got that error,
I can't resolve it
this is happening because your data have a reference loop.
e.g
// this example creates a reference loop
var p = new Product()
{
ProductCategory = new ProductCategory()
{ products = new List<Product>() }
};
p.ProductCategory.products.Add(p); // <- this create the loop
var x = JsonSerializer.Serialize(p); // A possible object cycle was detected ...
You can not handle the reference loop situation in the new System.Text.Json yet (netcore 3.1.1) unless you completely ignore a reference and its not a good idea always. (using [JsonIgnore] attribute)
but you have two options to fix this.
you can use Newtonsoft.Json in your project instead of System.Text.Json (i linked an article for you)
Download the System.Text.Json preview package version 5.0.0-alpha.1.20071.1 from dotnet5 gallery (through Visual Studio's NuGet client):
option 1 usage:
services.AddMvc()
.AddNewtonsoftJson(
options => {
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
// if you not using .AddMvc use these methods instead
//services.AddControllers().AddNewtonsoftJson(...);
//services.AddControllersWithViews().AddNewtonsoftJson(...);
//services.AddRazorPages().AddNewtonsoftJson(...);
option 2 usage:
// for manual serializer
var options = new JsonSerializerOptions
{
ReferenceHandling = ReferenceHandling.Preserve
};
string json = JsonSerializer.Serialize(objectWithLoops, options);
// -----------------------------------------
// for asp.net core 3.1 (globaly)
services.AddMvc()
.AddJsonOptions(o => {
o.JsonSerializerOptions
.ReferenceHandling = ReferenceHandling.Preserve
});
these serializers have ReferenceLoopHandling feature.
Edit : ReferenceHandling changed to ReferenceHandler in DotNet 5
but if you decide to just ignore one reference use [JsonIgnore] on one of these properties. but it causes null result on your API response for that field even when you don't have a reference loop.
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string ProductText { get; set; }
public int ProductCategoryId { get; set; }
// [JsonIgnore] HERE or
public virtual ProductCategory ProductCategory { get; set; }
}
public class ProductCategory
{
public int Id { get; set; }
// [JsonIgnore] or HERE
public ICollection<Product> products {get;set;}
}
.NET 5 Web API
public static void ConfigureServices(this IServiceCollection services, IConfiguration configuration)
{
services.AddControllers()
.AddJsonOptions(o => o.JsonSerializerOptions
.ReferenceHandler = ReferenceHandler.Preserve);
}
I have the same issue, my fix was to add async and await keyword since I am calling an async method on my business logic.
Here is my original code:
[HttpGet]
public IActionResult Get()
{
//This is async method and I am not using await and async feature .NET which triggers the error
var results = _repository.GetAllDataAsync();
return Ok(results);
}
To this one:
HttpGet]
public async Task<IActionResult> Get()
{
var results = await _repository.GetAllDataAsync();
return Ok(results);
}
In .Net 6, you can use System.Text.Json to initialize a startup action with AddControllersWithViews like this in Program.cs,
using System.Text.Json.Serialization;
builder.Services.AddControllersWithViews()
.AddJsonOptions(x => x.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);
also you can use AddMvc like this,
builder.Services.AddMvc()
.AddJsonOptions(x => x.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);
but quote from Ryan
asp.net core 3.0+ template use these new
methodsAddControllersWithViews,AddRazorPages,AddControllers instead of
AddMvc.
I will recommend to use the first solution.
Ensure you have [JsonIgnore] on the correct fields to avoid a circular reference.
In this case you will need
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string ProductText { get; set; }
[JsonIgnore]
public virtual ProductCategory ProductCategory { get; set; }
}
You probably don't need the ProductCategoryId field (depends if you are using EF and code first to define your DB)
Edit - In answer to noruk
There is often confusion in connected objects and navigation properties. You can get the data you want in JSON but also define the EF structures to get the correct DB structure (foreign keys, indexes, etc).
Take this simple example. A Product (for example a T-Shirt) has many sizes or SKUs (e.g. Small, Large, etc)
public class Product
{
[Key]
[MaxLength(50)]
public string Style { get; set; }
[MaxLength(255)]
public string Description { get; set; }
public List<Sku> Skus { get; set; }
}
public class Sku
{
[Key]
[MaxLength(50)]
public string Sku { get; set; }
[MaxLength(50)]
public string Barcode { get; set; }
public string Size { get; set; }
public decimal Price { get; set; }
// One to Many for Product
[JsonIgnore]
public Product Product { get; set; }
}
Here you can serialise a Product and the JSON data will include the SKUs. This is the normal way of doing things.
However if you serialise a SKU you will NOT get it's parent product. Including the navigation property will send you into the dreaded loop and throw the "object cycle was detected" error.
I know this is limiting in some use cases but I would suggest you follow this pattern and if you want the parent object available you fetch it separately based on the child.
var parent = dbContext.SKUs.Include(p => p.Product).First(s => s.Sku == "MY SKU").Product
I fixed my API Core Net6.0 adding [JsonIgnore]:
public class SubCategoryDto
{
public int Id { get; set; }
public string Name { get; set; }
public string Image { get; set; }
public int CategoryId { get; set; }
[JsonIgnore]
public Category Category { get; set; }
}
For net core 3.1 you have to add in Startup.cs:
services.AddMvc.AddJsonOptions(o => {
o.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
o.JsonSerializerOptions.MaxDepth = 0;
})
and import at least this package using nuget.org include prerelease:
<PackageReference Include="System.Text.Json" Version="5.0.0-rc.1.20451.14" />
following code is working for me in dotnet 5.0 :
services.AddControllersWithViews()
.AddJsonOptions(o => o.JsonSerializerOptions
.ReferenceHandler = ReferenceHandler.Preserve);
Finally fixed mine with System.Text.Json not NewtonSoft.Json using
var options = new JsonSerializerOptions()
{
MaxDepth = 0,
IgnoreNullValues = true,
IgnoreReadOnlyProperties = true
};
Using options to serialize
objstr = JsonSerializer.Serialize(obj,options);
My project built with a similar error.
Here's the code before
public class PrimaryClass {
public int PrimaryClassId
public ICollection<DependentClass> DependentClasses { get; set; }
}
public class DependentClass {
public int DependentClassId { get; set; }
public int PrimaryClassId { get; set; }
public PrimaryClass primaryClass { get; set; }
}
I took away the PrimaryClass object from the DependentClass model.
Code after
public class PrimaryClass {
public int PrimaryClassId
public ICollection<DependentClass> DependentClasses { get; set; }
}
public class DependentClass {
public int DependentClassId { get; set; }
public int PrimaryClassId { get; set; }
}
I also had to adjust the OnModelCreating method from
modelBuilder.Entity<PrimaryClass>().HasMany(p => p.DependentClasses).WithOne(d => d.primaryClass).HasForeignKey(d => d.PrimaryClassId);
to
modelBuilder.Entity<PrimaryClass>().HasMany(p => p.DependentClasses);
The DbSet query that's running is
public async Task<List<DependentClass>> GetPrimaryClassDependentClasses(PrimaryClass p)
{
return await _dbContext.DependentClass.Where(dep => dep.PrimaryClassId == p.PrimaryClassId).ToListAsync();
}
The error could have been with any of these 3 sections of code, but removing the primary object reference from the dependent class and adjusting the OnModelCreating resolved the error, I'm just not sure why that would cause a cycle.
In my case the problem was when creating the entity relationships. I linked the main entity using a foreign key inside the dependent entity like this
[ForeignKey("category_id")]
public Device_Category Device_Category { get; set; }
also I referred the dipendend entity inside the main entity as well.
public List<Device> devices { get; set; }
which created a cycle.
Dependent Entity
public class Device
{
[Key]
public int id { get; set; }
public int asset_number { get; set; }
public string brand { get; set; }
public string model_name { get; set; }
public string model_no { get; set; }
public string serial_no { get; set; }
public string os { get; set; }
public string os_version { get; set; }
public string note { get; set; }
public bool shared { get; set; }
public int week_limit { get; set; }
public bool auto_acceptance { get; set; }
public bool booking_availability { get; set; }
public bool hide_device { get; set; }
public bool last_booked_id { get; set; }
//getting the relationships category 1 to many
public int category_id { get; set; }
[ForeignKey("category_id")]
public Device_Category Device_Category { get; set; }
public List<Booking> Bookings { get; set; }
}
Main Entity
public class Device_Category
{
public int id { get; set; }
public string name { get; set; }
public List<Device> devices { get; set; }
}
}
So I commented the
public List<Device> devices { get; set; }
inside main entity (Device_Category) and problem solved

C# rename JSON dynamic property name

I get Match data from a server with json format. in this json, There are two properties (key) whose names are variable and dynamic and depending on the fixture.
for example, I get Match data for fixture_id 256
{
"api": {
"results": 1,
"fixtures": [
{
"fixture_id": 256,
"league_id": 21,
"homeTeam": {
"team_id": 12,
"team_name": "Tottenham"
},
"awayTeam": {
"team_id": 13,
"team_name": "Everton"
},
"lineups": {
"Tottenham": {
"coach": "qqqq",
"formation": "4-2-3-1"
},
"Everton": {
"coach": "rrrr",
"formation": "4-2-3-1"
}
}
}
]
}
}
the class for this json
public class rootMatch
{
[JsonProperty("api")]
public Api Api { get; set; }
}
public class Api
{
[JsonProperty("results")]
public long Results { get; set; }
[JsonProperty("fixtures")]
public List<Fixture> Fixtures { get; set; }
}
public partial class Fixture
{
[JsonProperty("fixture_id")]
public long FixtureId { get; set; }
[JsonProperty("league_id")]
public long LeagueId { get; set; }
[JsonProperty("homeTeam")]
public Team HomeTeam { get; set; }
[JsonProperty("awayTeam")]
public Team AwayTeam { get; set; }
[JsonProperty("lineups")]
public Lineups Lineups { get; set; }
}
public class Lineups
{
[JsonProperty("Tottenham")]
public Team Tottenham{ get; set; }
[JsonProperty("Everton")]
public Team Everton{ get; set; }
}
public class Team
{
[JsonProperty("coach")]
public string Coach { get; set; }
[JsonProperty("formation")]
public string Formation { get; set; }
}
But, the rootMatch class work only, for this json.
Is there a way to change name of Data property during deserialize, change it to static name?
First TeamName at lineups -> change to the HomeTeam
and
Second TeamName at lineups -> change to the AwayTeam
for example, in this json "lineups"
(Tottenham convert -> HomeTeam)
and
(Everton convert -> AwayTeam)
You can add a property
[JsonExtensionData]
public Dictionary<string, object> AdditionalData { get; set; }
This will contain all properties that couldn't be match. Then you could iterate over the dictionary and process this data as you like.
The "problematic" JSON is the object with two properties where the property names are the team names. These are not known in advance. To handle this you can change the type to a dictionary with a string key.
To create meaningful names I adjusted your types a bit:
This is team:
public class Team
{
[JsonProperty("team_id")]
public int TeamId { get; set; }
[JsonProperty("team_name")]
public string TeamName { get; set; }
}
and this is a lineup:
public class Lineup
{
[JsonProperty("coach")]
public string Coach { get; set; }
[JsonProperty("formation")]
public string Formation { get; set; }
}
The fixture then becomes:
public partial class Fixture
{
[JsonProperty("fixture_id")]
public long FixtureId { get; set; }
[JsonProperty("league_id")]
public long LeagueId { get; set; }
[JsonProperty("homeTeam")]
public Team HomeTeam { get; set; }
[JsonProperty("awayTeam")]
public Team AwayTeam { get; set; }
[JsonProperty("lineups")]
public Dictionary<string, Lineup> Lineups { get; set; }
}
Notice how the lineups JSON property is mapped to a Dictionary<string, Lineup> in C#.
Here is how you can get the lineups for the home and away teams:
var root = JsonConvert.DeserializeObject<rootMatch>(json);
var fixture = root.Api.Fixtures.First();
var homeTeamLineup = fixture.Lineups[fixture.HomeTeam.TeamName];
var awayTeamLineup = fixture.Lineups[fixture.AwayTeam.TeamName];
The team names are used as keys in the dictionary.
I use First to pick the first fixture. You would probably use LINQ or a loop to process all the fixtures.

nested json deserialization - need another set of eyes

I have been trying to get this json to deserialize for two days now using RestSharp. I have gone through the RestSharp github site, looked at countless examples, and spent much time here on Stack Overflow to try and find the answer to no avail. My code had previously worked perfectly but the vendor changed their API version and I was forced to do an update to keep using the application for my legal practice. My json is as follows(client info has been removed and replaced with generic info):
{
"data":[
{
"id":1035117666,
"client":
{
"id":905422394,
"name":"client1"
},
"display_number":"11-00012",
"description":"General",
"practice_area":
{
"id":4269978,
"name":"Business"
},
"status":"Open",
"open_date":"2011-12-14",
"close_date":null,
"billing_method":"hourly"
},
{
"id":1035117768,
"client":
{
"id":905422506,
"name":"client2"
},
"display_number":"12-00037",
"description":"HOA",
"practice_area":
{
"id":4269978,
"name":"Business"
},
"status":"Open",
"open_date":"2012-08-07",
"close_date":null,
"billing_method":"hourly"
}
],
"meta":
{
"paging":
{
"next":"https://app.goclio.com/api/v4/matters.json?fields=id%2C+client%7Bid%2C+name%7D%2C+display_number%2C+description%2C+practice_area%7Bid%2C+name%7D%2C+status%2C+open_date%2C+close_date%2C+billing_method&limit=2&page_token=BAh7BjoLb2Zmc2V0aQc%3D--b1ea3eba20c8acefbcdfc7868debd1e0ee630c64&status=Open"
},
"records":91
}
}
I built the following schema within my c# code:
public class MatterList
{
public List<Matter> matters { get; set; }
public Meta meta { get; set; }
}
public class Meta
{
public Paging paging { get; set; }
public int records { get; set; }
}
public class Paging
{
public string previous { get; set; }
public string next { get; set; }
}
[DeserializeAs(Name = "data")]
public class Matter
{
public int id { get; set; }
public Client client { get; set; }
public string display_number { get; set; }
public string description { get; set; }
public PracticeArea practice_area { get; set; }
public string status { get; set; }
public DateTime open_date { get; set; }
public DateTime close_date { get; set; }
public string billing_method { get; set; }
public string type = "matter";
}
public class PracticeArea
{
public int id { get; set; }
public string name { get; set; }
}
public class Client
{
public int id { get; set; }
public string name { get; set; }
}
When I run the RestSharp deserialize method I am sending the result to an object of type MatterList using the following line of code
MatterList matterList = jsonHandler.Deserialize<MatterList>(response);
I have so far attempted to deserialize without the Meta or Paging POCO classes with the accompanying change to the MatterList class (taking out the Meta property).
I have tried with and without the [DeserializeAs(Name="data")] directive.
I have tried to set the RootElement of the json response prior to deserialization.
I have tried to shorthand the deserialization by combining it with the Execute request code
IRestResponse<MatterList> matterList = client.Execute<MatterList>(request);
I have created a container class called MatterContainer which I placed between MatterList and Matter classes in the schema:
public class MatterList
{
public List<MatterContainer> matters { get; set; }
}
public class MatterContainer
{
public Matter matter { get; set; }
}
public class Matter
{
public int id { get; set; }
public Client client { get; set; }
public string display_number { get; set; }
public string description { get; set; }
public PracticeArea practice_area { get; set; }
public string status { get; set; }
public DateTime open_date { get; set; }
public DateTime close_date { get; set; }
public string billing_method { get; set; }
public string type = "matter";
}
I know I am getting the json response back from the server correctly so my request is proper and MatterList is not null after deserialization. The problem is that I cannot get the deserialization to actually populate the List matters within the MatterList class.
I have been looking at this off and on for two days and cannot get past this hurdle. If anyone sees what I did wrong I would greatly appreciate the insight, I am at a point where I cannot progress further with my application.
Thanks!
I think your [DeserializeAs(Name = "data")] attribute is in the wrong place. Try putting it in the root class instead:
public class MatterList
{
[DeserializeAs(Name = "data")]
public List<Matter> matters { get; set; }
public Meta meta { get; set; }
}
alternatively, try renameing that property to data

MVC 4 Post on Nested JSON is null

I'm currently working on a mobile app that communicates with an MVC4 API.
The problem I just noticed is that it seems to be unable to parse nested objects for some reason.
Example:
I'm POSTing the following data towards the url (http://localhost/DoSomething):
{
"id":610,
"dynamic":[
{
"fieldId":2756,
"fieldValue":""
},
{
"fieldId":2757,
"fieldValue":""
}
],
"person":{
"name":"test",
"age":"123",
"dateOfBirth":"test",
"groups":[
{
"groupId":1182
},
{
"groupId":1311
},
{
"groupId":673
}
]
}
}
Knowing that MVC will try to serialize it against the provided models, I have created the following model for the request:
public class PersonRequest : RequestBase
{
public class Field
{
public int fieldId { get; set; }
public string fieldValue { get; set; }
}
public class Group
{
public int groupId { get; set; }
}
public class Person
{
public string name { get; set; }
public string age { get; set; }
public string dateOfBirth { get; set; }
public IEnumerable<Group> groups { get; set; }
}
public int id { get; set; }
public IEnumerable<Field> dynamic { get; set; }
public Person person { get; set; }
}
In order to handle the input I have created the following route (which works):
routes.MapRoute(
name: "PersonRequest",
url: "DoSomething",
defaults: new { controller = "Person", action = "Generate" }
);
And my actual routing method:
[HttpPost]
public ActionResult Generate(PersonRequest request)
{
return Json(request, JsonRequestBehavior.AllowGet);
}
The response is however:
{"id":610,"person":null,"dynamic":null}
After searching for a possible solution, people said you would have to use IEnumerable for such situations instead of a List. Sadly, this didn't seem to be working for me.
Just some extra info:
I could always use JSON.stringify on the clientside on the dataobject person and dynamic, and eventually deserialize it myself on the backend (as shown in this topic: parse Json text to C# object in asp mvc 4), but there has to be a better workaround for this problem.
Change: Changed groups to dynamic in the resulting json.
Solved: https://stackoverflow.com/a/29349804/2076351
Why are you using IEnumerable<>, the query is fired at the end,
use List<> and check.
public class PersonRequest : RequestBase
{
public class Field
{
public int fieldId { get; set; }
public string fieldValue { get; set; }
}
public class Group
{
public int groupId { get; set; }
}
public class Person
{
public string name { get; set; }
public string age { get; set; }
public string dateOfBirth { get; set; }
public List<Group> groups { get; set; }
}
public int id { get; set; }
public List<Field> dynamic { get; set; }
public Person person { get; set; }
}
Solved.
Seems that my backend was properly setup. The fix was the way the data was sent towards the backend.
If you are sending a nested object towards the backend, to be parsed. You'll have to do two things:
Use JSON.stringify on the entire data send to the data
Use a proper content-type, e.g.:
xhr.open("POST", url);
xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
xhr.setRequestHeader("hash", params.hash);
xhr.send(JSON.stringify(params.data));

Categories

Resources