After refactoring a class, serializing it with
file.Write(JsonConvert.SerializeObject(this, Formatting.Indented));
works as expected and produces the json file described in this issue.
However, deserializing no longer works.
http://json2csharp.com was able to retrieve the object structure properly.
Source/destination types
public class VinciModel
{
public string Name { get; set; }
public string VidiWorkspaceDir { get; set; }
public string ViDiWorkspaceName { get; set; }
public int NRows { get; set; }
public List<List<object>> Rows { get; set; }
public int NChocolates { get; set; }
public List<StreamInfo> StreamList { get; set; }
public float BlisterWidth { get; set; }
public float BlisterHeight { get; set; }
}
public class Socket
{
public int Index { get; set; }
public string StreamName { get; set; }
public Rectangle ROI { get; set; }
}
public class StreamInfo
{
public string StreamName { get; set; }
public List<Socket> Sockets { get; set; } = new List<Socket>();
}
}
Source/destination JSON
{
"Name": "sdgsd",
"VidiWorkspaceDir": "D:\\VidiProjects\\VidiWorkspace-demo",
"ViDiWorkspaceName": "box2shrink_2",
"NRows": 1,
"Rows": [
[
{
"Index": 0,
"StreamName": "mystream",
"ROI": "0, 0, 5, 51"
},
{
"Index": 0,
"StreamName": "1",
"ROI": "5, 0, 5, 51"
},
{
"Index": 0,
"StreamName": "2",
"ROI": "11, 0, 5, 51"
},
{
"Index": 0,
"StreamName": "mystream",
"ROI": "17, 0, 5, 51"
}
]
],
"NChocolates": 4,
"StreamList": [
{
"StreamName": "mystream",
"Sockets": [
{
"Index": 0,
"StreamName": "mystream",
"ROI": "0, 0, 5, 51"
},
{
"Index": 0,
"StreamName": "mystream",
"ROI": "17, 0, 5, 51"
}
]
},
{
"StreamName": "1",
"Sockets": [
{
"Index": 0,
"StreamName": "1",
"ROI": "5, 0, 5, 51"
}
]
},
{
"StreamName": "2",
"Sockets": [
{
"Index": 0,
"StreamName": "2",
"ROI": "11, 0, 5, 51"
}
]
}
],
"BlisterWidth": 23.0,
"BlisterHeight": 51.0
}
Error
Newtonsoft.Json.JsonReaderException:
'Unexpected character encountered while parsing value: {. Path 'Rows[0]', line 8, position 7.'
Steps to reproduce
using (var file = new System.IO.StreamReader(path))
JsonConvert.DeserializeObject<VinciModel>(file.ReadToEnd());
Worked before the class got refactored
I also tried :
var obj = JsonConvert.DeserializeObject<JObject>(file.ReadToEnd());
and got :
'Error reading string. Unexpected token: StartObject. Path 'Rows[0][0]'.'
The problem was that my class had a non default constructor and no default constructor. The error message is irrelevant.
Related
I am relatively new to .Net API development, so apologies if the answer is obvious here.
I have JSON coming in via a request. It is a nested JSON structure with a few subclasses.
I need to get this JSON in and subsequently restructure and distribute it into two, flat classes for storing in two database tables.
What is the best way to go about this? I have used DTOs and Automapper on my path to learning but I am not sure how to go about this with a nested structure.
The incoming JSON looks like this:
{
"id": xxx,
"parent_id": xxx,
"number": "xxx",
"order_key": "xxx",
"created_via": "xxx",
"version": "xxx",
"status": "xxx",
"currency": "xxx",
"date_created": "xxx",
"date_created_gmt": "xxx",
"date_modified": "xxx",
"date_modified_gmt": "xxx",
"discount_total": "xxx",
"discount_tax": "xxx",
"shipping_total": "xxx",
"shipping_tax": "xxx",
"cart_tax": "xxx",
"total": "xxx",
"total_tax": "xxx",
"prices_include_tax": xxx,
"customer_id": xxx,
"customer_ip_address": "xxx",
"customer_user_agent": "xxx",
"customer_note": "",
"billing": {
"first_name": "xxx",
"last_name": "xxx",
"company": "",
"address_1": "",
"address_2": "",
"city": "",
"state": "",
"postcode": "",
"country": "",
"email": "xxx",
"phone": "xxx"
},
"shipping": {
"first_name": "xxx",
"last_name": "xxx",
"company": "",
"address_1": "",
"address_2": "",
"city": "",
"state": "",
"postcode": "",
"country": ""
},
"payment_method": "xxx",
"payment_method_title": "xxx",
"transaction_id": "",
"date_paid": xxx,
"date_paid_gmt": xxx,
"date_completed": xxx,
"date_completed_gmt": xxx,
"cart_hash": "xxx",
"meta_data": [
{
"id": xxx,
"key": "xxx",
"value": "xxx"
}
],
"line_items": [
{
"id": xxx,
"name": "xxx",
"product_id": xxx,
"variation_id": xxx,
"quantity": xxx,
"tax_class": "",
"subtotal": "xxx",
"subtotal_tax": "xxx",
"total": "xxx",
"total_tax": "xxx",
"taxes": [],
"meta_data": [],
"sku": "",
"price": xxx
}
],
"tax_lines": [],
"shipping_lines": [],
"fee_lines": [],
"coupon_lines": [],
"refunds": [],
"_links": {
"self": [
{
"href": "xxx"
}
],
"collection": [
{
"href": "xxx"
}
],
"customer": [
{
"href": "xxx"
}
]
}
}
Only certain parts of it are relevant. It needs to be mapped into 2 classes as follows:
public class OnlineOrderHeader
{
[Key]
[Required]
public int OnlineOrderID { get; set; }
[Required]
public int AppOrderID { get; set; }
public int OrderNumber { get; set; }
[MaxLength(50)]
public string OrderKey { get; set; }
[MaxLength(50)]
public string CreatedVia { get; set; }
[MaxLength(50)]
public string Version { get; set; }
[MaxLength(50)]
public string Status { get; set; }
[MaxLength(3)]
public string Currency { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateModified { get; set; }
public decimal DiscountTotal { get; set; }
public decimal DiscountTax { get; set; }
public decimal CartTax { get; set; }
public decimal CartTotal { get; set; }
public int PriceIncludesTax { get; set; } //Check
public int CustomerID { get; set; }
[MaxLength(50)]
public string CustomerFirstName { get; set; }
[MaxLength(50)]
public string CustomerLastName { get; set; }
[MaxLength(50)]
public string CustomerEmail { get; set; }
[MaxLength(50)]
public string CustomerPhone { get; set; }
[MaxLength(50)]
public string CustomerEmployeeNo { get; set; }
[MaxLength(50)]
public string PaymentMethod { get; set; }
public int TransactionID { get; set; }
public DateTime DatePaid { get; set; }
[MaxLength(500)]
public string OrderURL { get; set; }
[MaxLength(500)]
public string CustomerNotes { get; set; }
[MaxLength(50)]
public string CuisineOrderStatus { get; set; }
}
And
public class OnlineOrderLines
{
[Key]
[Required]
public int OnlineOrderLineID { get; set; }
[Required]
public int AppOrderLineID { get; set; }
[Required]
public int ProductID { get; set; }
[MaxLength(50)]
public string SKU { get; set; }
public int Quantity { get; set; }
public decimal SubTotal { get; set; }
public decimal SubTotalTax { get; set; }
public decimal Money { get; set; }
[MaxLength(100)]
public string ProductDescription { get; set; }
[MaxLength(50)]
public string Category { get; set; }
}
I am not sure how to get one object with all necessary subclasses using a DTO.
Once I have that object, splitting it up into classes should be relatively stratforward, I would assume.
Any tips would be greatly appreciated!
Firstly, make sure your JSON is valid. The one above isn't. I think the data types need to be more obvious. You have "xxx" and xxx - the latter should be numbers I assume? anyway, I usually just use strings as primitives and worry about the numerical data types later.
Once you have a valid JSON (i.e. take the response string from the request and remove the escape characters - easily done with regex), you can paste it to an online JSON to C# converter (like this one).
This should give you a class structure. You can rename the RootObject to the main name of your API response class. I also check the primitive data types, as those converters are not perfect. As I mentioned above, I usually change the primitives to be strings and deal with data types later, when mapping to view models/entities.
Then, in your API calling class:
Make that API request (i.e httpClient.GetAsync(requestUrl))
Read a response as a string (i.e response.ReadAsStringAsync()
Use Newtonsoft.JSON to deserialize it.
var response = httpClient.GetAsync(requestUrl);
var json = await response.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RootObject>(json);
Once you have that RootObject model (which is a raw API response deserialized into an object), you can use the aforementioned Automapper to create OnlineOrderHeader and OnlineOrderLine View Models (simplify/flatten).
I am currently using .Net Core 3.0 and I'm building a Web API.
I am trying to eager load some related data to the frontend.
I have the following classes and dbcontext setup.
public class EntityCommon
{
public int Id { get; set; }
public DateTime CreatedDatetime { get; set; }
}
public class UserAmountClaim: EntityCommon
{
public int UserId { get; set; }
public AmountClaimType Type { get; set; }
public int Amount{ get; set; }
public int? RefereeId { get; set; }
public AmountClaimStatus Status { get; set; }
public DateTime? ClaimedDatetime { get; set; }
[NotMapped]
public virtual User Referee { get; set; }
}
public class User : EntityCommon
{
public string Name { get; set; }
public string Password { get; set; }
public UserStatus Status { get; set; }
[NotMapped]
public virtual UserAmountClaim UserAmountClaim { get; set; }
}
DbContext
modelbuilder.Entity<UserAmountClaim>().ToTable("UserAmountClaim ", "dbo");
modelbuilder.Entity<UserAmountClaim>().Property(ucc => ucc.UserId).HasColumnName("fkUserId");
modelbuilder.Entity<UserAmountClaim>().Property(ucc => ucc.RefereeId).HasColumnName("fkRefereeId");
modelbuilder.Entity<UserAmountClaim>().Property(ucc => ucc.Type).HasConversion(new EnumToStringConverter<UserAmountClaim>());
modelbuilder.Entity<UserAmountClaim>().Property(ucc => ucc.CreatedDatetime).ValueGeneratedOnAdd();
modelbuilder.Entity<UserAmountClaim>().HasOne(ucc => ucc.Referee).WithOne(user => user.UserAmountClaim);
Startup.cs
services.AddControllers().AddNewtonsoftJson(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
options.SerializerSettings.Converters.Add(new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-ddTHH:mm:ss" });
options.SerializerSettings.Converters.Add(new StringEnumConverter());
options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
});
And also data such as below
Id fkUserId Type Amount fkRefereeId Status ClaimedDateTime CreatedDatetime
52 1 ReferralCommission 100 2 6 NULL 2020-04-18 15:19:34.203
53 1 ReferralCommission 100 2 6 NULL 2020-04-18 15:19:40.343
54 1 ReferralCommission 100 1 6 NULL 2020-04-18 15:36:44.017
55 1 ReferralCommission 100 1 6 NULL 2020-04-18 15:51:31.757
But when i execute the following code
var result = _dbContext.UserAmountClaim.Where(x => x.UserId == userId &&
x.Status == AmountClaimStatus.PendingUser &&
x.Type == AmountClaimType.ReferralCommission)
.Include(x => x.Referee)
.ToListAsync();
The referee is missing in the first and third item
{
"userId": 1,
"type": "ReferralCommission",
"amount": 100,
"status": "PendingUser",
"id": 52,
"createdDatetime": "2020-04-18T15:19:34"
},
{
"userId": 1,
"type": "ReferralCommission",
"amount": 100,
"refereeId": 2,
"status": "PendingUser",
"referee": {
"id": 2,
"name": "82109380918",
"password": "",
"status": "Valid",
"createdDatetime": "2020-04-16T17:45:31",
},
"id": 53,
"createdDatetime": "2020-04-18T15:19:40"
},
{
"userId": 1,
"type": "ReferralCommission",
"credit": 100,
"status": "PendingUser",
"id": 54,
"createdDatetime": "2020-04-18T15:36:44"
},
{
"userId": 1,
"type": "ReferralCommission",
"amount": 100,
"refereeId": 1,
"status": "PendingUser",
"referee": {
"id": 1,
"name": "31829389031",
"password": "",
"status": "Valid",
"createdDatetime": "2020-04-16T17:45:31",
},
"id": 54,
"createdDatetime": "2020-04-18T15:36:44"
}
This is happening all over other places that has the similar structure.
Anyone can enlighten me what's causing this and how to solve this ?
As pointed out by #IvanStoev, it was a mistaken in setting up the relationship and property class
Modifying following works for me.
public class User : EntityCommon
{
public string Name { get; set; }
public string Password { get; set; }
public UserStatus Status { get; set; }
[NotMapped]
public virtual List<UserAmountClaim> UserAmountClaim { get; set; }
}
DbContext
modelbuilder.Entity<UserAmountClaim>().ToTable("UserAmountClaim ", "dbo");
modelbuilder.Entity<UserAmountClaim>().Property(ucc => ucc.UserId).HasColumnName("fkUserId");
modelbuilder.Entity<UserAmountClaim>().Property(ucc => ucc.RefereeId).HasColumnName("fkRefereeId");
modelbuilder.Entity<UserAmountClaim>().Property(ucc => ucc.Type).HasConversion(new EnumToStringConverter<UserAmountClaim>());
modelbuilder.Entity<UserAmountClaim>().Property(ucc => ucc.CreatedDatetime).ValueGeneratedOnAdd();
modelbuilder.Entity<UserAmountClaim>().HasOne(ucc => ucc.Referee).WithMany(user => user.UserAmountClaim);
Big thanks to #IvanStoev
I am calling RestApi which is giving me Nested JSON in response. I have to handle this nested Json and represent on same Partial View. I will share my model , view and controller code.
I created controller to call api and collect data. Its populating root class but not populating child class
JSON:
{
"id": 98,
"title": "all numbers",
"clues_count": 5,
"clues": [
{
"id": 602,
"answer": "<i>Eight</i>",
"question": "It was \"enough\" for Dick van Patten",
"value": 100,
"airdate": "1984-09-14T12:00:00.000Z",
"category_id": 98,
"game_id": null,
"invalid_count": null
},
{
"id": 608,
"answer": "16",
"question": "A teen's \"sweet\" age",
"value": 200,
"airdate": "1984-09-14T12:00:00.000Z",
"category_id": 98,
"game_id": null,
"invalid_count": null
},
{
"id": 614,
"answer": "<i>The Grapes of Wrath</i>",
"question": "",
"value": null,
"airdate": "1984-09-14T12:00:00.000Z",
"category_id": 98,
"game_id": null,
"invalid_count": null
},
{
"id": 620,
"answer": "Nathaniel Hawthorne",
"question": "",
"value": null,
"airdate": "1984-09-14T12:00:00.000Z",
"category_id": 98,
"game_id": null,
"invalid_count": null
},
{
"id": 626,
"answer": "F. Scott Fitzgerald",
"question": "",
"value": null,
"airdate": "1984-09-14T12:00:00.000Z",
"category_id": 98,
"game_id": null,
"invalid_count": 1
}
]
}
Model:
public class Clue
{
public int id { get; set; }
public string answer { get; set; }
public string question { get; set; }
public int? value { get; set; }
public DateTime airdate { get; set; }
public int category_id { get; set; }
public object game_id { get; set; }
public int? invalid_count { get; set; }
}
public class RootObject
{
public int id { get; set; }
public string title { get; set; }
public int clues_count { get; set; }
public List<Clue> clues { get; set; }
}
Controller
public async Task<ActionResult> Category( int id)
{
// List<Category> clueInfo = new List<Category>();
List<RootObject> cluess = new List<RootObject>();
RootObject category = new RootObject();
category.clueslist = new List<Clues>();
using (var client = new HttpClient())
{
//Passing service base url
client.BaseAddress = new Uri(Baseurl);
client.DefaultRequestHeaders.Clear();
//Define request data format
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//Sending request to find web api REST service resource GetAllEmployees using HttpClient
HttpResponseMessage Res = await client.GetAsync("api/category?id=" + id);
Debug.WriteLine("My debug string here");
//Checking the response is successful or not which is sent using HttpClient
if (Res.IsSuccessStatusCode)
{
//Storing the response details recieved from web api
var ClueResponse = Res.Content.ReadAsStringAsync().Result;
//Deserializing the response recieved from web api and storing into the Employee list
category = JsonConvert.DeserializeObject<RootObject>(ClueResponse);
}
// return View();
return PartialView("Category", cluess);
}
Result on view
title
clues_count
dining out
20
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)
{
}
}
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});