Nancy with Complex object using JSON not working - nancy

Client code:
var basket = {
products: [],
user: { name: "schugh" }
};
$("#basket table tr").each(function (index, item) {
var product = $(item).data('product');
if (product) {
basket.products.push(product);
}
});
$.ajax({
url: "http://localhost:12116/basketrequest/1",
async: true,
cache: false,
type: 'POST',
data: JSON.stringify(basket),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function (result) {
alert(result);
},
error: function (jqXHR, exception) {
alert(exception);
}
});
Server code:
Post["/basketrequest/{id}"] = parameters =>
{
var basketRequest = this.Bind(); //basketrequest is null
return Response.AsJson(basketRequest , HttpStatusCode.OK);
};
Other model classes:
[Serializable]
public class BasketRequest
{
public User User;
public List<Product> Products;
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public decimal Price { get; set; }
public ProductStatus ProductStatus { get; set; }
}
public enum ProductStatus
{
Created,
CheckedBy,
Published
}
public class User
{
public string Name { get; set; }
}
The code in the Nancy Module this.Bind(); returns null. If I change the Complex object to just List<Product>, i.e. with no wrapper BasketRequest, the object is fine...
Any pointers?
EDIT: JSON posted:
{
"User": {
"Name": "SChugh"
},
"Products": [{
"Id": 1,
"Name": "Tomato Soup",
"Category": "Groceries",
"Price": 1
}, {
"Id": 2,
"Name": "Yo-yo",
"Category": "Toys",
"Price": 3.75
}]
}

Your BasketRequest object should implement properties instead of fields. So
public class BasketRequest
{
public User User { get; set; }
public List<Product> Products { get; set; }
}
also you should probably use the generic method too
this.Bind<BasketRequest>();

Related

ASP NET Core reading complex configuration with array and JsonConverter

I have this appsettings.json
"TopLevel": [
{
"Name": "Name",
"TopChild": {
"File": {
"URL": "SomeUrl",
"Username": "Admin",
"Password": "123"
}
},
"TopChild2": [
{
"Name": "SimpleMapper",
"Mapper": {
"Param1": "",
"Param2": "",
"Param3": ""
}
},
{
"Name": "SimpleFileConverter",
"Converter": {
"ToWhat": "",
"Writer": ""
}
}
],
"TopChild3": {
"SomeProperty3": {
"WhatIsThis": "",
"SomeProp": "",
"Password": ""
}
}
},
{
"Name": "Name",
"TopChild": {
"DB": {
"ConnectionString": "String",
"Username": "Admin",
"Password": "123"
}
},
"TopChild2": [
{
"Name": "SomeConverter",
"Converter": {
"Param1": "",
"Param2": ""
}
},
{
"Name": "SimpleFileConverter",
"Mapper": {
"Param1": "",
"Param2": "",
"Param3": ""
}
}
],
"TopChild3": {
"SomePropety4": {
"SomeProp7": "",
"Password": ""
}
}
}
]
And this is the code I wrote for a model
public class TopLevelConfigRoot
{
[JsonProperty("TopLevel")]
public List<TestObject> TopLevel { get; set; }
}
public class TestObject
{
[JsonProperty("Name")]
public string Name { get; set; }
[JsonProperty("TopChild")]
[JsonConverter(typeof(TopChildConverter))]
public TopChildBase TopChild { get; set; }
[JsonProperty("TopChild2")]
[JsonConverter(typeof(TopChild2Converter))]
public List<TopChild2> TopChild2 { get; set; }
[JsonProperty("TopChild3")]
[JsonConverter(typeof(TopChild3Converter))]
public TopChild3Base TopChild3 { get; set; }
}
public abstract class TopChildBase { }
public abstract class TopChild3Base { }
public class File : TopChildBase
{
[JsonProperty("URL")]
public string URL { get; set; }
[JsonProperty("Username")]
public string Username { get; set; }
[JsonProperty("Password")]
public string Password { get; set; }
}
public class DB : TopChildBase
{
[JsonProperty("ConnectionString")]
public string ConnectionString { get; set; }
[JsonProperty("Username")]
public string Username { get; set; }
[JsonProperty("Password")]
public string Password { get; set; }
}
public interface ITopChild2
{
public string Name { get; set; }
public ITopChild2Type ITopChild2Type { get; set; }
}
public class TopChild2 : ITopChild2
{
[JsonProperty("Name")]
public string Name { get; set; }
[JsonProperty("ITopChild2Type")]
public ITopChild2Type ITopChild2Type { get; set; }
}
public abstract class ITopChild2Type
{ }
public class Mapper : ITopChild2Type
{
[JsonProperty("Param1")]
public string Param1 { get; set; }
[JsonProperty("Param2")]
public string Param2 { get; set; }
[JsonProperty("Param3")]
public string Param3 { get; set; }
}
public class Converter : ITopChild2Type
{
[JsonProperty("Param1")]
public string Param1 { get; set; }
[JsonProperty("Param2")]
public string Param2 { get; set; }
}
public class SomeProperty3 : TopChild3Base
{
[JsonProperty("WhatIsThis")]
public string WhatIsThis { get; set; }
[JsonProperty("SomeProp")]
public string SomeProp { get; set; }
[JsonProperty("Password")]
public string Password { get; set; }
}
public class SomePropety4 : TopChild3Base
{
[JsonProperty("SomeProp7")]
public string SomeProp7 { get; set; }
[JsonProperty("Password")]
public string Password { get; set; }
}
public class TopChildConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(TopChildBase);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
if (jo.ContainsKey(nameof(File)))
{
return jo[nameof(File)].ToObject<File>();
}
else if (jo.ContainsKey(nameof(DB)))
{
return jo[nameof(DB)].ToObject<DB>();
}
return null;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public class TopChild3Converter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(TopChild3Base);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
if (jo.ContainsKey(nameof(SomeProperty3)))
{
return jo[nameof(SomeProperty3)].ToObject<SomeProperty3>();
}
else if (jo.ContainsKey(nameof(SomePropety4)))
{
return jo[nameof(SomePropety4)].ToObject<SomePropety4>();
}
return null;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public class TopChild2Converter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(TopChild2);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray ja = JArray.Load(reader);
List<TopChild2> topChild2List = new List<TopChild2>();
foreach (var item in ja)
{
JObject jObject = (JObject)item;
TopChild2 TopChild2 = null;
string name = jObject[nameof(TopChild2.Name)].ToString();
if (jObject.ContainsKey(nameof(Mapper)))
{
var root = jObject[nameof(Mapper)];
Mapper mapper = new Mapper
{
Param1 = root[nameof(Mapper.Param1)].Value<string>(),
Param2 = root[nameof(Mapper.Param2)].Value<string>(),
Param3 = root[nameof(Mapper.Param3)].Value<string>()
};
TopChild2 = new TopChild2 { Name = name, ITopChild2Type = mapper };
}
else if (jObject.ContainsKey(nameof(Converter)))
{
var root = jObject[nameof(Converter)];
Converter converter = new Converter
{
Param1 = root[nameof(Converter.Param1)].Value<string>(),
Param2 = root[nameof(Converter.Param2)].Value<string>()
};
TopChild2 = new TopChild2 { Name = name, ITopChild2Type = converter };
}
topChild2List.Add(TopChild2);
}
return topChild2List;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And now with that model I've tried using any known implementation of config inside of controller.
The only thing I came up is inserting IConfiguration via Dependency Injection and use _config = config;
And then selected it by
var section = _config.GetSection("TopLevel");
and then used it like this
var itemArray = section.AsEnumerable();
How can I then use this itemArray to convert this to either model, or a JsonString which i will then easily serialize it with Newtonsoft
var t = JsonConvert.DeserializeObject<TopLevelConfigRoot>(jsonString);
But I haven't find a way to map this itemArray to model I provided, nor I had success with writing recursive method to actually map it to JsonString.
Converter I wrote is
public string ConvertBackToJSonString(KeyValuePair<string, string>[] itemArray)
{
var root = new JObject();
var current = root;
foreach (var item in itemArray)
{
var parts = item.Key.Split(':');
var key = parts[parts.Length - 1];
var parent = current;
for (int i = 0; i < parts.Length - 1; i++)
{
var part = parts[i];
if (!current.ContainsKey(part))
current.Add(part, new JObject());
current = (JObject)current[part];
}
if (!string.IsNullOrEmpty(item.Value))
current.Add(key, new JValue(item.Value));
else
current.Add(key, new JObject());
current = parent;
}
return JsonConvert.SerializeObject(root, Formatting.Indented);
}
But that didn't work. How should this be approached to/done?
You could bind the section to a model directly with IConfigration.Bind() method
For example,in your case :
public class BindTarget
{
public string Name { get; set; }
public SomeChildClass1 TopChild { get; set; }
public List<SomeChildClass2> TopChild2 { get; set; }
public SomeChildClass3 TopChild3 { get; set; }
}
public class SomeChildClass1
{
public File File { get; set; }
public DB DB { get; set; }
}
public class SomeChildClass2
{
public string Name { get; set; }
public Mapper Mapper { get; set; }
public Converter Converter { get; set; }
}
public class SomeChildClass3
{
public SomeProperty3 SomeProperty3 { get; set; }
public SomePropety4 SomePropety4 { get; set; }
}
bind as below:
var targetobj = new List<BindTarget>();
Configuration.GetSection("TopLevel").Bind(targetobj);
var str = JsonConvert.SerializeObject(targetobj,new JsonSerializerSettings() { NullValueHandling=NullValueHandling.Ignore});
result:
The document related

EF Core self joined ICollection query

I have a problem with querying data from a self reference datatable having an ICollection.
Here are my table structures:
public class SoqHeading
{
public int Id { get; set; }
public string ItemNo { get; set; }
public string Heading { get; set; }
public SoqRevision SoqRevision_NP { get; set; }
public ICollection<SoqItem> Items { get; set; }
public int? ParentSoqHeadingId { get; set; }
public SoqHeading Parent_NP { get; set; }
public ICollection<SoqHeading> Children_NP { get; set; }
}
public class SoqItem
{
public int Id { get; set; }
public SoqPreliminaryOrContract_enum PreliminaryOrContract { get; set; }
public int SoqHeadingId { get; set; }
public SoqHeading SoqHeading_NP { get; set; }
}
I have a self referenced relation in the SoqHeading table which is 1 to many. The SoqHeading table can have zero or many SoqItems.
This is how the data is in my database
what i want is to query all from the root node of the SoqHeading table with its children and the items.
like this
{
"ItemNo": "C",
"Items": [
{
"ItemNo": "c-1",
},
{
"ItemNo": "c-2",
}
],
"Children_NP": [
{
"ItemNo": "C.1",
"Children_NP": [
{
"ItemNo": "C.1.1",
"Items": [
{
"ItemNo": "c.1.1-1",
},
{
"ItemNo": "c.1.1-2",
}
],
"Children_NP": [],
},
{
"ItemNo": "C.1.2",
"Items": [
{
"ItemNo": "c.1.2-1",
},
{
"ItemNo": "c.1.2-2",
}
],
"Children_NP": [],
}
],
},
{
"ItemNo": "C.2",
"Children_NP": [],
}
]
}
But currently i get data from the query like this
[
{
"ItemNo": "C"
},
{
"ItemNo": "C.1.2",
}
]
where C.1.2 has to be inside C, but it not. C and C.1.2 are in the same level which i dont wont to have produced by the following query
var entity = await _context.SoqHeadings
.Include(i=>i.Items)
.Include(i => i.Children_NP)
.Where(w => w.SoqRevision_NP.ProjectId == 10)
.ToListAsync();
please help
so it seems to be EFcore has no support to included the children on the first child node https://github.com/dotnet/efcore/issues/14144
so the work around is something like this here https://khalidabuhakmeh.com/recursive-data-with-entity-framework-core-and-sql-server

EF return columns data as list or array

I have two tables, which have following foreign key
Poll(CreatedBy) --> CreateBy(ID)
On performing join, I am getting data as following:
I want to write an query using EF (if possible) to return result as:
[{
"id": 1,
"users": [
{
"email": "abc.com"
}
]
},
{
"id": 3,
"users": [
{
"email": "xyz.test"
},
{
"email": "tet.com"
}
]
}]
Assuming that the class structure like below:
public class Poll
{
public int ID { get; set; }
public string Name { get; set; }
public List<CreateBy> CreateBy { get; set; }
}
public class CreateBy
{
public int ID { get; set; }
public string EmailAddress { get; set; }
public string DisplayName { get; set; }
[ForeignKey("Poll")]
public int PollId { get; set; }
}
Data source:
Linq query:
var result = (from p in _context.Poll.ToList()
join c in _context.CreateBy on p.ID equals c.PollId
group p by p.ID into g
select new
{
id = g.Key,
users = g.Select(x => x.CreateBy.Where(z => z.PollId == g.Key).Select(y => new { email = y.EmailAddress })).FirstOrDefault()
}).ToList();
Result:

Getting null values in action with List parameter

Im passing array data from angularjs to mvc controller
This is my angularjs code
$http({
url: '/Login/InsertDetails',
method: 'POST',
data: JSON.stringify(Data),
headers: { 'content-type': 'application/json' }
})
Json Data is
$scope.details = [{
name: "ammin",
age: "16",
city: "NY",
add: "true",
sal: 100
}, {
name: "joe",
age: "80",
city: "CH",
add: false,
sal: 200
}];
var Data = {
one: $scope.details
}
And in my controller i have method Insert with list parameter
public JsonResult Insert(List<Personel> P)
{
}
View Model class is like this
public class Personel
{
public string name { get; set; }
public int age { get; set; }
public string city { get; set; }
public string add { get; set; }
public string sal { get; set; }
}
But im getting null values in action method when i post the data from angularjs. what type of parameter should i use in action method

Post an array of complex objects with JSON, MVC model is do not bind

here is my codes.
json_model
var mix = {
MixName: $("#mixname").val(),
MixDesc: tinyMCE.activeEditor.getContent(),
Price: $("#price").val(),
DiseaseMixs: [],
MixProducts: []
}
Add items to DiseaseMixs and MixProducts
$("#DiseaseList").find("tbody tr").each(function (index) {
mix.DiseaseMixs.push({
MixID: parseInt(MixID),
DiseaseID: parseInt($(".diseaseid").eq(index).html()),
ImpactDegree: parseInt($(".derece option:selected").eq(index).html()),
Description: $(".diseaseMixDesc input").eq(index).val()
});
})
$("#productList").find("tbody tr").each(function (index) {
mix.MixProducts.push({
MixID: parseInt(MixID),
ProductID: parseInt($(".productid").eq(index).html()),
MeasureTypeID: parseInt($(".birim option:selected").eq(index).val()),
MeasureAmount: $(".measureAmount input").eq(index).val()
});
})
and end of this process, here is a sample json object that is post.
{
"MixName": "asdasddas",
"MixDesc": "<p>sadasd</p>",
"Price": "123",
"DiseaseMixs": [{
"MixID": 1,
"DiseaseID": 2,
"ImpactDegree": 5,
"Description": "asads"
}, {
"MixID": 1,
"DiseaseID": 3,
"ImpactDegree": 4,
"Description": "aqqq"
}],
"MixProducts": [{
"MixID": 1,
"ProductID": 2,
"MeasureTypeID": 3,
"MeasureAmount": "3"
}, {
"MixID": 1,
"ProductID": 3,
"MeasureTypeID": 2,
"MeasureAmount": "45"
}]
}
ajax post
$.ajax({
url: 'SaveMix',
type: 'POST',
data: JSON.stringify(mix),
contentType: 'application/json; charset=utf-8',
success: function (result) {
console.log(result);
},
error: function (xhr, status) {
alert(status);
console.log(xhr);
}
})
and MVC Model and JSONResult function
Model
public class MixModel
{
public string MixName { get; set; }
public string MixDesc { get; set; }
public float Price { get; set; }
DiseaseMix[] DiseaseMixs { get; set; } //DiseaseMix EntityFramework entity
MixProduct[] MixProducts { get; set; } //MixProduct EF
}
function
[HttpPost]
public JsonResult SaveMix(MixModel mix)
{
bool result = false;
//do something
return Json(new { result = result }, JsonRequestBehavior.AllowGet);
}
and here is the result I get is.
No matter how I tried, I could not bind the model.
What am I doing wrong? Please give me some help.
Thanks in advance.
Model binding is failing because those 2 properties are currently private(which is the default when you don't specify anything explicitly).
Change the 2 properties to public so that model binder can set the values of those.
public class MixModel
{
public string MixName { get; set; }
public string MixDesc { get; set; }
public float Price { get; set; }
public DiseaseMix[] DiseaseMixs { get; set; } //DiseaseMix EntityFramework entity
public MixProduct[] MixProducts { get; set; } //MixProduct EF
}
I also suggests to not mix your view models with entities generated by your ORM. That creates a tightly coupled solution.

Resources