Update complex entity in db - c#

I am working with facebook RTU and get json updates on the payments of my application`s users.
To have a better customer service i want to save all the json string facebook sends me , but in an ordered way.
For example,
I am getting this json:
{
"id": "3603105474213890",
"user": {
"name": "Daniel Schultz",
"id": "221159"
},
"application": {
"name": "Friend Smash",
"namespace": "friendsmashsample",
"id": "241431489326925"
},
"actions": [
{
"type": "charge",
"status": "completed",
"currency": "USD",
"amount": "0.99",
"time_created": "2013-03-22T21:18:54+0000",
"time_updated": "2013-03-22T21:18:55+0000"
}
],
"refundable_amount": {
"currency": "USD",
"amount": "0.99"
},
"items": [
{
"type": "IN_APP_PURCHASE",
"product": "http://www.friendsmash.com/og/friend_smash_bomb.html",
"quantity": 1
}
],
"country": "US",
"created_time": "2013-03-22T21:18:54+0000",
"payout_foreign_exchange_rate": 1,
"disputes": [
{
"user_comment": "I didn't receive my item! I want a refund, please!",
"time_created": "2013-03-24T18:21:02+0000",
"user_email": "email\u0040domain.com"
}
]
}
And i want to save it in db but not as a string, as an object this style and also have the option to query by field value from my db.
public class PaymentDetailsDto
{
public string id { get; set; }
public PaidUserDto user { get; set; }
public PaidApplicationDto application { get; set; }
public List<PaymentActionDto> actions { get; set; }
public RefundableAmountDto refundable_amount { get; set; }
public List<PaymentItemDto> items { get; set; }
public string country { get; set; }
public string created_time { get; set; }
public int test { get; set; }
public int payout_foreign_exchange_rate { get; set; }
public List<DisputeDto> disputes { get; set; }
}
How do you suggest representing this Entity in db?
As you see i have three List objects in my Root object and this it what make it a little but complicated to decide how to save it.
We are not gonna use EntityFramework or NHibernate.

Related

Bind appsettings.json array section to generic class with generic list property

I have to read some values from appsettings.json using IOptionsSnapshot and return them through the API endpoint
"adConfig": [
{
"id": 3,
"adDef": [
{
"type": "number",
"key": "somekey",
"displayName": "Rules",
"values": [
0,
1
],
"regex": null
},
{
"type": "select",
"key": "anotherkey",
"displayName": "Some state",
"values": [
"some string value",
"another string value"
],
"regex": null
}
]
},
{
"id": 1
....
}
]
For this porpose I created 2 classes to use to bind the settings.
public class AdDataModel<T>
{
public string type { get; set; }
public string key{ get; set; }
public string displayName { get; set; }
public List<T> Values { get; set; }
public string regex { get; set; }
}
public class AdConfigurationModel
{
public int Id{ get; set; }
public List<AdDataModel<dynamic>> AdDefinition { get; set; }
}
In the Startup.cs I use this code to bind the settings to the classes:
services.Configure<List<AdConfigurationModel>>(Configuration.GetSection("adConfig"));
So far so good. I inject this into the Controller class but the problem is that AdDataModel.Values are aways of type object<string> ( which is not the case for the first element of adDef:Values subsection). Is there any way to achive Values to be from type int or string based on the values in appsettings.json?
Thanks!

C# Web API Nested JSON from Stored Procedure in MSSQL

Overview:
I'm building an API using ASP.NET Web API 2. I'm building Stored Procedures in SQL and linking these in the API to serve data.
I know that SQL can return JSON using FOR JSON AUTO, for example. But, I don't think it's the best place to configure the data and out put JSON. So I'm assuming there must be a way to achieve nested JSON in the API.
This is what I want to achieve:
{
"Apps": [
{
"ItemID": "1",
"Path": "/AppReport",
"Name": "AppReport",
"Reports": [
{
"Reports_ItemID": "11",
"Reports_Path": "/AppReport/SubReport",
"Reports_Name": "SubReport"
},
{
"Reports_ItemID": "12",
"Reports_Path": "/AppReport/SubReport2",
"Reports_Name": "SubReport2"
}
]
},
{
"ItemID": "2",
"Path": "/AppReport2",
"Name": "AppReport2",
"Reports": [
{
"Reports_ItemID": "22",
"Reports_Path": "/AppReport/SubReport",
"Reports_Name": "SubReport"
}
]
}
]
}
At the moment it's coming out as a flat Array of Objects
{
"Apps": [
{
"ItemID": "1",
"Path": "/AppReport",
"Name": "AppReport",
"Reports_ItemID": "11",
"Reports_Path": "/AppReport/SubReport",
"Reports_Name": "SubReport"
},
{
"ItemID": "1",
"Path": "/AppReport",
"Name": "AppReport",
"Reports_ItemID": "12",
"Reports_Path": "/AppReport/SubReport2",
"Reports_Name": "SubReport2"
},
...
]
}
I have a class of the Stored Procedure in the API that looks like this:
namespace API.Stored_Procedures
{
public partial class SP_GetApps
{
public System.Guid ItemID { get; set; }
public string Path { get; set; }
public string Name { get; set; }
public System.Guid Report_ItemID { get; set; }
public string Report_Path { get; set; }
public string Report_Name { get; set; }
}
}
My Controller looks like this:
[HttpGet]
[Route("{uid}", Name ="getReportApps")]
[ResponseType(typeof(SP_GetApps_Result))]
public IHttpActionResult SP_GetApps(string uid)
{
var res = db.Database.SqlQuery<SP_GetApps_Result>
("SP_GetApps {0}", uid);
return Ok(res);
}
In order to achieve the desired JSON Class should look like
public class App
{
public string ItemID { get; set; }
public string Path { get; set; }
public string Name { get; set; }
public List<Report> Reports { get; set; }
}
public class Report
{
public string Reports_ItemID { get; set; }
public string Reports_Path { get; set; }
public string Reports_Name { get; set; }
}

Patching an object with an array of objects

I have a many-to-many relationship between Courses and Students for a school. Here's is what I have for a Course:
public class Course
{
public Guid CourseId { get; set; }
public string Name { get; set; }
public ICollection<CourseStudent> Students { get; set; }
}
public class CourseStudent
{
public Guid CourseId { get; set; }
public Guid StudentId { get; set; }
}
I'm using JsonPatch to PATCH my objects. I'm trying to add to the collection of Students, just to the end of the collection with this:
[
{
"op": "add",
"path": "/Students/-",
"value": [
{
"CourseId": "07264DC9-9FEB-42E2-B1EF-08D58F58C873",
"StudentId": "FB6E6988-4A56-4CA4-86E2-E23090FAD98F"
}
]
}
]
But when I submit this, I get an exception saying:
"ClassName": "Microsoft.AspNetCore.JsonPatch.Exceptions.JsonPatchException",
"Message": "The value '[\r\n {\r\n \"CourseId\": \"07264DC9-9FEB-42E2-B1EF-08D58F58C873\",\r\n \"StudentId\": \"FB6E6988-4A56-4CA4-86E2-E23090FAD98F\"\r\n }\r\n]' is invalid for target location.",
The structure looks correct to me based on the Json Patch docs. Any idea why it won't accept my format?
Ended up figuring it out, the format should be:
[
{
"op": "add",
"path": "/Students/-",
"value":
{
"CourseId": "07264DC9-9FEB-42E2-B1EF-08D58F58C873",
"StudentId": "FB6E6988-4A56-4CA4-86E2-E23090FAD98F"
}
}
]

Deserializing JSON that has an int as a key in C#

I am trying to deserialize this JSON
{
"39": {
"category": "Miscellaneous",
"country_whitelist": [],
"name": "domain.com",
"url_blacklist": [],
"country_blacklist": [],
"url_whitelist": [
"domain.com"
],
"deals": {
"425215": {
"status": "Ok",
"type": "",
"code": "CODE",
"end_date": "2014-03-01 04:00:00",
"title": "RandomTitle",
"url": "http://domain.com/foo",
"text": "Text Text Text",
"long_title": "Longer Text"
},
"425216": {
"status": "Ok",
"type": "",
"code": "CODE2",
"end_date": "2014-03-01 04:00:00",
"title": "RandomTitle2",
"url": "http://domain.com/bar",
"text": "Text Text Text",
"long_title": "Longer Text"
}
},
"88x31": "http://someimage/88x31.png",
"subcategory": "Other"
},
"40": {
"category": "Miscellaneous",
"country_whitelist": [],
"name": "domain.com",
"url_blacklist": [],
"country_blacklist": [],
"url_whitelist": [
"domain.com"
],
"products": {
"425215": {
"status": "Ok",
"type": "",
"code": "CODE",
"end_date": "2014-03-01 04:00:00",
"title": "RandomTitle",
"url": "http://domain.com/foo",
"text": "Text Text Text",
"long_title": "Longer Text"
},
"425216": {
"status": "Ok",
"type": "",
"code": "CODE2",
"end_date": "2014-03-01 04:00:00",
"title": "RandomTitle2",
"url": "http://domain.com/bar",
"text": "Text Text Text",
"long_title": "Longer Text"
}
},
"88x31": "http://someimage/88x31.png",
"subcategory": "Other"
}
}
I tried using Json.NET and I tried using ServiceStack's deserializer but I can't seem to get any type of representation for this JSON.
The main thing that is blocking me I believe is that the keys are Int but I don't have control on the JSON I receive.
This is the C# classes I have built
public class product
{
public string status { get; set; }
public string type { get; set; }
public string code { get; set; }
public string end_date { get; set; }
public string title { get; set; }
public string url { get; set; }
public string text { get; set; }
public string long_title { get; set; }
}
public class Merchant
{
public string category { get; set; }
public List<string> country_whitelist { get; set; }
public string name { get; set; }
public List<string> url_blacklist { get; set; }
public List<string> country_blacklist { get; set; }
public List<string> url_whitelist { get; set; }
public List<product> products { get; set; }
public string subcategory { get; set; }
}
public class Data
{
public Dictionary<int, Merchant> MainMerchants { get; set; }
}
I prefer using ServiceStack but any other deserializer that works will be great
var data = client.Get(json);
Getting your data types mapped correctly:
It is possible to deserialize your JSON. As you correctly identified you can deserialize to a Dictionary<int, Merchant>.
But you will need to change your definition of products in the Merchant class to be a Dictionary<int, Product>. It needs to be a dictionary here to handle your numeric key. List<Product> won't work.
Also to handle the 88x31 property you can use a DataMember(Name = '88x31') mapping to map it to something c# likes, like image88x31. Unfortunately this does mean your DTO properties become opt-in so you will then need to decorate all members. Add using System.Runtime.Serialization;
Once you make those changes such that:
// Note I capitalized Product
public class Product
{
public string status { get; set; }
public string type { get; set; }
public string code { get; set; }
public string end_date { get; set; }
public string title { get; set; }
public string url { get; set; }
public string text { get; set; }
public string long_title { get; set; }
}
/*
* Use DataMember to map the keys starting with numbers to an alternative c# compatible name.
* Unfortunately this requires properties to opt in to the data contract.
*/
[DataContract]
public class Merchant
{
[DataMember]
public string category { get; set; }
[DataMember]
public List<string> country_whitelist { get; set; }
[DataMember]
public string name { get; set; }
[DataMember]
public List<string> url_blacklist { get; set; }
[DataMember]
public List<string> country_blacklist { get; set; }
[DataMember]
public List<string> url_whitelist { get; set; }
[DataMember]
public Dictionary<int, Product> products { get; set; }
[DataMember]
public string sub_category { get; set; }
// This maps the 88x31 key to a c# appropriate name
[DataMember(Name = "88x31")]
public string image88x31 { get; set; }
}
Then you will be able to deserialize into Dictionary<int, Merchant> without any issues.
JsonSerializer.DeserializeFromString<Dictionary<int, Merchant>>("YOUR JSON STRING");
Using in a ServiceStack Service:
If you want to be able to send this request directly to a ServiceStack service, then you can use a RequestBinder to deserialize into this complex type. Given this service:
Request DTO:
[Route("/Merchants", "POST")]
public class MerchantsRequest
{
public Dictionary<int, Merchant> MainMerchants { get; set; }
}
Simple Action Method:
public class MerchantsService : Service
{
public void Post(MerchantsRequest request)
{
var merchant39 = request.MainMerchants.First(p=>p.Key == 39).Value;
Console.WriteLine("Name: {0}\nImage: {1}\nProduct Count: {2}", merchant39.name, merchant39.image88x31, merchant39.products.Count);
var merchant40 = request.MainMerchants.First(p=>p.Key == 40).Value;
Console.WriteLine("Name: {0}\nImage: {1}\nProduct Count: {2}", merchant40.name, merchant40.image88x31, merchant40.products.Count);
}
}
AppHost Configuration:
In your AppHost Configure method you would need to add a binder to the request type. i.e. typeof(MerchantsRequest) like so:
public override void Configure(Funq.Container container)
{
Func<IRequest, object> merchantsRequestBinder = delegate(IRequest request) {
var json = WebUtility.HtmlDecode( request.GetRawBody() );
return new MerchantsRequest { MainMerchants = JsonSerializer.DeserializeFromString<Dictionary<int, Merchant>>(json) };
};
RequestBinders.Add(typeof(MerchantsRequest), merchantsRequestBinder);
...
}
This binder method will convert the json you are sending into a MerchantsRequest. Then you can use it like a regular ServiceStack request.
Full Source Code Here
A fully working example of console application, demonstrating the conversion of the complex JSON to a service request.
Note: I notice in your JSON that you have property deals on one object, and products on another, I assumed this was a typo, as you don't have a corresponding property on in the class for deals.
In your json string, for the products node, should it be this? as the type where it converted from is a List instead of dictionary?
I can get it work change it to following json string
"products": [{
"status": "Ok",
"type": "",
"code": "CODE",
"end_date": "2014-03-01 04:00:00",
"title": "RandomTitle",
"url": "http://domain.com/foo",
"text": "Text Text Text",
"long_title": "Longer Text"
},
{
"status": "Ok",
"type": "",
"code": "CODE2",
"end_date": "2014-03-01 04:00:00",
"title": "RandomTitle2",
"url": "http://domain.com/bar",
"text": "Text Text Text",
"long_title": "Longer Text"
}],

How to convert JSON to C# classes?

I have a complex JSON object that I want represent as C# class. I have a head start on the parent class called "Form", but how can I represent a collection for different types (see the "elements" object below)?
Here is the JSON object:
{
"action": "index.html",
"method": "post",
"elements":
[
{
"type": "fieldset",
"caption": "User information",
"elements":
[
{
"name": "email",
"caption": "Email address",
"type": "text",
"placeholder": "E.g. user#example.com",
"validate":
{
"email": true
}
},
{
"name": "password",
"caption": "Password",
"type": "password",
"id": "registration-password",
"validate":
{
"required": true,
"minlength": 5,
"messages":
{
"required": "Please enter a password",
"minlength": "At least {0} characters long"
}
}
},
{
"name": "password-repeat",
"caption": "Repeat password",
"type": "password",
"validate":
{
"equalTo": "#registration-password",
"messages":
{
"equalTo": "Please repeat your password"
}
}
},
{
"type": "radiobuttons",
"caption": "Sex",
"name": "sex",
"class": "labellist",
"options":
{
"f": "Female",
"m": "Male"
}
}
]
]
}
The class I have start looks like this:
public class Form
{
public Guid id
{
get;
set;
}
public string action
{
get;
set;
}
public string method
{
get;
set;
}
public ??? elements
{
get;
set;
}
public Form()
{
}
}
How do I handle the "elements" property to get the desired JSON output?
I am using WCF 4.0 with these atributes in the web.config: automaticFormatSelectionEnabled="false", defaultOutgoingResponseFormat="Json". Any help or ideas would be greatly appreciated.
If you don't have the liberty of using dynamic types from .NET 4 or would like to leverage the benefits that static typing provide, the JSON Class Generator project on codeplex will generate c# classes given a json input string. (shameless plug) I've also taken code from this project and slapped a web UI on it.
Wow. Fascinating question. Maybe use ExpandoObject / dynamic?
http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject.aspx
http://blogs.msdn.com/b/csharpfaq/archive/2009/10/01/dynamic-in-c-4-0-introducing-the-expandoobject.aspx?PageIndex=4
Or anonymous types I think are serializable with the built-in .NET JSON serializer.
You do not need to try and create the class structure manually.
Sometimes it is rather frustrating too. :)
There is a visual studio command you can use (I think vs2015 and later):
On a new class file click Menu => Edit => Paste Special
Select "Paste JSON as Classes"
Now specifically in your JSON there is an error, you are missing the closing curly-brace of first "element" object.
Below is the corrected JSON:
{
"action": "index.html",
"method": "post",
"elements": [
{
"type": "fieldset",
"caption": "User information",
"elements": [
{
"name": "email",
"caption": "Email address",
"type": "text",
"placeholder": "E.g. user#example.com",
"validate": {
"email": true
}
},
{
"name": "password",
"caption": "Password",
"type": "password",
"id": "registration-password",
"validate": {
"required": true,
"minlength": 5,
"messages": {
"required": "Please enter a password",
"minlength": "At least {0} characters long"
}
}
},
{
"name": "password-repeat",
"caption": "Repeat password",
"type": "password",
"validate": {
"equalTo": "#registration-password",
"messages": {
"equalTo": "Please repeat your password"
}
}
},
{
"type": "radiobuttons",
"caption": "Sex",
"name": "sex",
"class": "labellist",
"options": {
"f": "Female",
"m": "Male"
}
}
]
}
]
}
And the corresponding Classes:
public class Rootobject
{
public string action { get; set; }
public string method { get; set; }
public Element[] elements { get; set; }
}
public class Element
{
public string type { get; set; }
public string caption { get; set; }
public Element1[] elements { get; set; }
}
public class Element1
{
public string name { get; set; }
public string caption { get; set; }
public string type { get; set; }
public string placeholder { get; set; }
public Validate validate { get; set; }
public string id { get; set; }
public string _class { get; set; }
public Options options { get; set; }
}
public class Validate
{
public bool email { get; set; }
public bool required { get; set; }
public int minlength { get; set; }
public Messages messages { get; set; }
public string equalTo { get; set; }
}
public class Messages
{
public string required { get; set; }
public string minlength { get; set; }
public string equalTo { get; set; }
}
public class Options
{
public string f { get; set; }
public string m { get; set; }
}
If you just want to make sure all this unknown data gets deserialized and can be reserialized at some point in the future, I suggest the usage of IExtensibleDataObject.
Here are some samples to get you started. Hope this helps! (If you already knew this and were looking for something different...let me know!)
Forward-Compatible Data Contracts
Data Contract Versioning
Useful clarifying thread on the topic at MSDN forums

Categories