Adding array title to Linq-to-SQL-to-JSON in Web API - asp.net

I am using Linq to SQL in a Web API web service to retrieve data from a database and return a JSON file.
My question is actually pretty simple, but I have been through the forums and couldn't find an answer. Please find below the description of my issue, as well as my (simplified) sourcecode.
The objects I return have two levels of data. To make you understand, here is how my classes look like :
public class X
{
public int ID { get; set; }
public DateTime date { get; set; }
public virtual ICollection<Y> Ys
public virtual ApplicationUser User { get; set; }
}
public class Y
{
public int ID { get; set; }
public int XID { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
public virtual X x { get; set; }
}
You can see that for each X1 object, I can have several X2 objects nested.
To retrieve that, I use the following Linq to SQL in my WebAPI Controller :
public IHttpActionResult GetXsByUser(string userID)
{
var xs = (from x in db.Xs
where x.User.Id == userID
orderby x.date
select new
{
x_id = x.ID,
date = x.date,
Ys = (from y in db.Ys
where x.User.Id == userID && x1.ID == y.XID
select new
{
unit_price = y.Price,
quantity = y.Quantity
})
});
if (xs == null)
{
return NotFound();
}
return Ok(xs);
}
My web service works fine and returns the following JSON :
[
{
"$id": "1",
"x_id": 1,
"date": "2014-01-24T00:00:00",
"Ys": [
{
"$id": "2",
"unit_price": 2.47,
"quantity": 2
},
{
"$id": "3",
"unit_price": 1.25,
"quantity": 3
},
{
"$id": "4",
"unit_price": 1.99,
"quantity": 2
}
]
},
{
"$id": "5",
"x_id": 2,
"date": "2014-01-28T00:00:00",
"Ys": [
{
"$id": "6",
"unit_price": 6.22,
"quantity": 1
},
{
"$id": "7",
"unit_price": 1.2,
"quantity": 3
}
]
}
]
The problem is, to then deserialize this in my mobile app, I have to use classes as follows :
public class Y
{
public string _$id { get; set; }
public double unit_price { get; set; }
public int quantity { get; set; }
}
public class RootObject
{
public string _$id { get; set; }
public int x_id { get; set; }
public string date { get; set; }
public List<Y> Ys { get; set; }
}
But i would like to be able to use classes as follow :
public class Y
{
public string _$id { get; set; }
public double unit_price { get; set; }
public int quantity { get; set; }
}
public class OnlineX
{
public string _$id { get; set; }
public int x_id { get; set; }
public string date { get; set; }
public List<Y> Ys { get; set; }
}
public class RootObject
{
public List<OnlineX> OnlineXs { get; set; }
}
I have worked with a JSON editor and know that the solution to get this is to have the following JSON file instead of the previous one :
{
"OnlineXs": [
{
"$id": "1",
"x_id": 1,
"date": "2014-01-24T00:00:00",
"Ys": [
{
"$id": "2",
"unit_price": 2.47,
"quantity": 2
},
{
"$id": "3",
"unit_price": 1.25,
"quantity": 3
},
{
"$id": "4",
"unit_price": 1.99,
"quantity": 2
}
]
},
{
"$id": "5",
"x_id": 2,
"date": "2014-01-28T00:00:00",
"Ys": [
{
"$id": "6",
"unit_price": 6.22,
"quantity": 1
},
{
"$id": "7",
"unit_price": 1.2,
"quantity": 3
}
]
}
]
}
Notice that the only thing that changes is that I add a title to my array of Xs ("Online Xs"). That is why I said that my question is simple. But the thing is, I have no idea how to do that in Web API. Is it just a small change in my Linq to SQL request? Should i build a custom JSON serializer?
I hope that my question is clear enough, and if you want some more information, I'll be happy to provide them.
Thanks a lot in advance
EDIT :
Ok, I've found the solution, it was simple indeed. Here it is :
I had to replace :
return Ok(xs);
by
return Ok(new { OnlineXs = xs });

Just to rephrase your answer, when you return the IHttpActionResult, just assign the query result to a named property and return it like:
return Ok(new { OnlineXs = xs});

Related

How to show recursive list from coming api in .net core

I have function like this and this function get all subfolders. My Folder Model:
public partial class Folder
{
public Folder()
{
Files = new HashSet<File>();
InverseParentFolder = new HashSet<Folder>();
}
[Key]
public int Id { get; set; }
[Required]
[StringLength(50)]
public string FolderName { get; set; }
public Guid FileGuid { get; set; }
public int Size { get; set; }
public int? ParentFolderId { get; set; }
public int AppUserId { get; set; }
public bool IsDeleted { get; set; }
[Column(TypeName = "datetime")]
public DateTime CreatedAt { get; set; }
[ForeignKey(nameof(AppUserId))]
[InverseProperty(nameof(User.Folders))]
public virtual User AppUser { get; set; }
[ForeignKey(nameof(ParentFolderId))]
[InverseProperty(nameof(Folder.InverseParentFolder))]
public virtual Folder ParentFolder { get; set; }
[InverseProperty(nameof(File.Folder))]
public virtual ICollection<File> Files { get; set; }
[InverseProperty(nameof(Folder.ParentFolder))]
public virtual ICollection<Folder> InverseParentFolder { get; set; }
}
And my function :
public async Task<List<Folder>> GetAllSubFolders(int folderId)
{
List<Folder> result = new List<Folder>();
await GetFolders(folderId, result);
return result;
}
//Recursive function that take all subfolders inside of one folder.
private async Task GetFolders(int folderId, ICollection<Folder> result)
{
using var context = new FilemanagementContext();
var folders =await context.Folders.Where(I => I.ParentFolderId== folderId && I.IsDeleted==false).ToListAsync();
if (folders.Count >0)
{
foreach (var folder in folders)
{
if (folder.InverseParentFolder==null)
folder.InverseParentFolder = new List<Folder>();
await GetFolders(folder.Id, folder.InverseParentFolder);
if (!result.Contains(folder))
{
result.Add(folder);
}
}
}
}
Simple response message :
{
"id": 3,
"folderName": "Child Edited Name",
"fileGuid": "d056e29b-2034-4ecc-a12e-e79d5d2ce494",
"size": 148,
"parentFolderId": 2,
"appUserId": 2,
"isDeleted": false,
"createdAt": "2021-03-02T11:22:52.653",
"appUser": null,
"parentFolder": null,
"files": [],
"inverseParentFolder": [
{
"id": 6,
"folderName": "Child Folder 3",
"fileGuid": "3d559aa8-0216-4498-9de2-896d51110773",
"size": 0,
"parentFolderId": 3,
"appUserId": 2,
"isDeleted": false,
"createdAt": "2021-03-03T13:17:27.523",
"appUser": null,
"parentFolder": null,
"files": [],
"inverseParentFolder": []
}
]
}
I will zip this parent and child folders from coming information above. I can already zip some folders and files also i will zip folders on controller.
So my question is how do I zip the folders coming from this recursive function one by one?

LINQ query to select inner list only(Issue in JSON response)

I am facing issue in LINQ query, basically, it not returning records as per required format of JSON. I have two entity Users and And Address both have foreign key relation.
I have 2 entity as below
public class Users
{
public int Id { set; get; }
public string FName { set; get; }
public string LName { set; get; }
public string UserName { set; get; }
public string Password { set; get; }
public ICollection<Address> Address{ set; get; }
public string Contact { set; get; }
public decimal Salary { set; get; }
public string xyz { set; get; }
}
public class Address
{
public int AddressID { set; get; }
public string Address1 { set; get; }
public string Address2 { set; get; }
public string City { set; get; }
}
Context Class
public class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options): base(options)
{
}
public DbSet<Users> Users { get; set; }
}
API
[HttpGet("getaddressbyuser/{userId}")]
[Route("getaddressbyuser")]
public async Task<ActionResult<List<ICollection<Address>>>> GetAddressByUser(int userId)
{
var addr = await _context.Users.Where(x => x.Id == userId).Select(y => y.Address).ToListAsync();
if (addr == null)
{
return NotFound();
}
return addr;
}
When I called the API
http://localhost:55433/api/Users/getaddressbyuser/2
It' returns the following json
[
[
{
"addressID": 1,
"address1": "Noida",
"address2": "Noida 201301",
"city": "Noida"
},
{
"addressID": 2,
"address1": "Noida",
"address2": "Noida 201301",
"city": "Noida"
}
]
]
While I wnat to records as below
[
{
"addressID": 1,
"address1": "Noida",
"address2": "Noida 201301",
"city": "Noida"
},
{
"addressID": 2,
"address1": "Noida",
"address2": "Noida 201301",
"city": "Noida"
}
]
Note: I am using DotNet Core 2.2, Thanks
var addr = await _context.Users.Where(x => x.Id == userId).SelectMany(y=>y.Address).ToListAsync();
The problem is with select. Address is a list thus you need to use select many to flatten the results. In your select statement you tell him from a list of users select for each user their adresses so you have a list of users where each user has a list of addresses. So the it's not JSON problem or anything. If you use debuggger you will see that in C# it's a list containing lists. Plus considering that you want this JSON your return type is incorrect. Simply use IEnumarable<Address>
Change your return type.
Try with type <List<Address>>
If you want to use ICollection you can use only ICollection<Address>

JSON.NET error "Cannot deserialize the current JSON array (e.g. [1,2,3]) into type because the type requires a JSON object (e.g. {"name":"value"})"

I have the following WebCleint to call a Restful web service inside my .net console application:-
try
{
using (WebClient wc = new WebClient())
{
wc.Encoding = Encoding.UTF8;
string url = "https://*****/paged?hapikey=*********&properties=website&properties=i_scan&limit=2";//web service url
string tempurl = url.Trim();
var json = wc.DownloadString(tempurl);//get the json
Marketing ipfd = JsonConvert.DeserializeObject<Marketing>(json);//deserialize
}
}
catch (Exception e)
{
//code goes here..
}
where i am using JSON.Net to Deserialize the json object, which will be as follow:-
{
"has-more": true,
"offset": 622438650,
"companies": [
{
"portalId": *******,
"companyId": *****,
"isDeleted": false,
"properties": {
"website": {
"value": "****.net",
"timestamp": 1520938239457,
"source": "CALCULATED",
"sourceId": null,
"versions": [
{
"name": "website",
"value": "*****.net",
"timestamp": 1520938239457,
"source": "CALCULATED",
"sourceVid": [
731938234
]
}
]
}
},
"additionalDomains": [],
"stateChanges": [],
"mergeAudits": []
},
{
"portalId": ******,
"companyId": ******,
"isDeleted": false,
"properties": {
"website": {
"value": "****.***.***",
"timestamp": 1512488590073,
"source": "CALCULATED",
"sourceId": null,
"versions": [
{
"name": "website",
"value": "****.***8.****",
"timestamp": 1512488590073,
"source": "CALCULATED",
"sourceVid": []
}
]
},
"i_scan": {
"value": "Yes",
"timestamp": 1543409493459,
"source": "******",
"sourceId": "**************",
"versions": [
{
"name": "i_scan",
"value": "Yes",
"timestamp": 1543409493459,
"sourceId": *****",
"source": "CRM_UI",
"sourceVid": [],
"requestId": "******"
}
]
}
},
"additionalDomains": [],
"stateChanges": [],
"mergeAudits": []
}
]
}
Here are my classes:-
public class Marketing
{
public Companies companies { get; set; }
}
public class Companies
{
public IList<string> companyId { get; set; }
public IList<Properties> properties { get; set; }
}
public class Properties
{
public IList<Website> website { get; set; }
public IList<I_Scan> i_scan { get; set; }
}
public class Website
{
public string value { get; set; }
}
public class i_Scan
{
public string value { get; set; }
}
but currently i am getting this exception, when i try to de-serialize the JSON object:-
Newtonsoft.Json.JsonSerializationException was caught
HResult=-2146233088
Message=Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'MMarketing.Companies' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List<T> that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.
Path 'companies', line 1, position 49.
Source=Newtonsoft.Json
StackTrace:
so i am not sure why JSON.NET is unable to do the Deserialize correctly, as in my case the classes are compatible with the returned json object??
At a first glance it looks like you switched two properties in Making them a List and vice versa.
public class Marketing
{
public List<Companies> companies { get; set; }
}
Is "companies": [ in the json, while "companyId": *****, is the id as a string, not array. Properties is not an array also, but the property versions of properties is.
public class Companies
{
public string companyId { get; set; }
public Properties properties { get; set; }
}
If I'm coming to json blind I like to use http://json2csharp.com/ to generate my class structure for me
public class Version
{
public string name { get; set; }
public string value { get; set; }
public object timestamp { get; set; }
public string source { get; set; }
public List<object> sourceVid { get; set; }
}
public class Website
{
public string value { get; set; }
public object timestamp { get; set; }
public string source { get; set; }
public object sourceId { get; set; }
public List<Version> versions { get; set; }
}
public class Version2
{
public string name { get; set; }
public string value { get; set; }
public long timestamp { get; set; }
public int sourceId { get; set; }
public string source { get; set; }
public List<object> sourceVid { get; set; }
public int requestId { get; set; }
}
public class IScan
{
public string value { get; set; }
public long timestamp { get; set; }
public int source { get; set; }
public int sourceId { get; set; }
public List<Version2> versions { get; set; }
}
public class Properties
{
public Website website { get; set; }
public IScan i_scan { get; set; }
}
public class Company
{
public int portalId { get; set; }
public int companyId { get; set; }
public bool isDeleted { get; set; }
public Properties properties { get; set; }
public List<object> additionalDomains { get; set; }
public List<object> stateChanges { get; set; }
public List<object> mergeAudits { get; set; }
}
public class Marketing
{
public bool has_more { get; set; }
public int offset { get; set; }
public List<Company> companies { get; set; }
}
var result = JsonConvert.DeserializeObject<Marketing>(json);

Write Custom c# class to represent the JSON?

I have a JSON string for which I need to create the C# class and then parse the entire List in similar format. JSON String contain "0" and "1". I have annotated the class properties with
[JsonProperty("0")]
but look like its not working.
{
"draw": 4,
"recordsTotal": 57,
"recordsFiltered": 57,
"data": [
{
"0": "Charde",
"1": "Marshall",
"2": "Regional Director",
"3": "San Francisco",
"4": "16th Oct 08",
"5": "$470,600",
"DT_RowId": "row_13"
},
{
"0": "Colleen",
"1": "Hurst",
"2": "Javascript Developer",
"3": "San Francisco",
"4": "15th Sep 09",
"5": "$205,500",
"DT_RowId": "row_9"
},
{
"0": "Dai",
"1": "Rios",
"2": "Personnel Lead",
"3": "Edinburgh",
"4": "26th Sep 12",
"5": "$217,500",
"DT_RowId": "row_20"
}]
}
Class that I have tried for this JSON
public class UserData
{
[JsonProperty("0")]
public string Name { get; set; }
[JsonProperty("1")]
public string Email { get; set; }
//Having more JSON properites
[JsonProperty("DT_RowId")]
public long UserId { get; set; }
}
public class JsonValdate
{
public string draw { get; set; }
public int length { get; set; }
public int start { get; set; }
public int recordsFiltered { get; set; }
public int recordsTotal { get; set; }
[JsonProperty("data")]
public UserData[] data { get; set; }
}
It might be that some of JSON data cannot be converted to UserData Properties.
Right off the bat, you can see "DT_RowId": "row_20" cannot be converted to long UserId.
Use try catch block outside of conversion, and see the exception.
For example,
private string Json
{
get {
return #"
{
""draw"": 4,
...
}";
}
}
try
{
JsonValdate result = JsonConvert.DeserializeObject<JsonValdate>(Json);
}
catch (Exception ex)
{
// Debug
}
Here is how I test
Since the conversion is not working, do not put all fields at once.
Start with the following working fields. Then add one field at a time.
private string Json
{
get { return #"
{
""draw"": 4,
""recordsTotal"": 57,
""recordsFiltered"": 57,
""data"": [
{
""0"": ""Charde"",
""1"": ""Marshall""
},
{
""0"": ""Colleen"",
""1"": ""Hurst""
},
{
""0"": ""Dai"",
""1"": ""Rios""
}]
}";
}
}
public class UserData
{
[JsonProperty("0")]
public string Name { get; set; }
[JsonProperty("1")]
public string Email { get; set; }
}
public class JsonValdate
{
public string draw { get; set; }
public int length { get; set; }
public int start { get; set; }
public int recordsFiltered { get; set; }
public int recordsTotal { get; set; }
[JsonProperty("data")]
public UserData[] data { get; set; }
}
protected void Page_Load(object sender, EventArgs e)
{
try
{
JsonValdate result = JsonConvert.DeserializeObject<JsonValdate>(Json);
}
catch (Exception ex)
{
}
}

How to parse nested JSON string using .NET

I'm trying to parse a nested JSON string returned from the GCM (Google Could Messaging) server using VB.NET. The JSON string looks like this:
{ "multicast_id": 216,
"success": 3,
"failure": 3,
"canonical_ids": 1,
"results": [
{ "message_id": "1:0408" },
{ "error": "Unavailable" },
{ "error": "InvalidRegistration" },
{ "message_id": "1:1516" },
{ "message_id": "1:2342", "registration_id": "32" },
{ "error": "NotRegistered"}
]
}
I would like to get the results array in the above string.
I found the following example helpful, example but it does not show how to get to the nested parts, specifically message_id, error and registration_id inside the results array.
Thanks
I'll give an answer using c# and Json.net
var jobj = JsonConvert.DeserializeObject<Response>(json);
You can also use JavaScriptSerializer
var jobj2 = new JavaScriptSerializer().Deserialize<Response>(json);
public class Result
{
public string message_id { get; set; }
public string error { get; set; }
public string registration_id { get; set; }
}
public class Response
{
public int multicast_id { get; set; }
public int success { get; set; }
public int failure { get; set; }
public int canonical_ids { get; set; }
public List<Result> results { get; set; }
}

Resources