Fluent validation custom response ASP.NET Core Web API - c#

I am working on an ASP.NET Core 6 Web API project. We use Fluent Validation. How can I return custom response. Please advice.
I would like to send custom response on different validation error such as page, limit, date etc
This is the response I get by default:
{
"type": "",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "00-097c689850af328c49705cea77fc6fbd-0c7defe165d9693f-00",
"errors": {
"Page": [
"Page value should be greater than 0"
],
"Limit": [
"Limit value should be greater than 0"
]
}
}
And this is the response I would like to get:
{
"traceId": "e45b3398-a2a5-43eb-8851-e45de1184021",
"timestamp": "2021-06-28T00:32:06.548Z",
"errors": [
{
"errorCode": "Contract Violation",
"message": "Page should be greater than 0",
"priority": "MEDIUM",
"properties": {
"name": "string",
"value": "string"
}
}
]
}
This is my code:
public async Task<IActionResult> Get([FromQuery] DataParameter input)
{
}
public class DataParameter
{
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public int Page { get; set; }
public int Limit { get; set; }
}
public class QueryParameterValidator : AbstractValidator<QueryParameter>
{
public QueryParameterValidator()
{
RuleFor(model => model.Page)
.GreaterThan(0)
.WithMessage("Page value should be greater than 0");
RuleFor(model => model.Limit)
.GreaterThan(0)
.WithMessage("Limit value should be greater than 0");
RuleFor(model => model.StartDate)
.Matches(#"^\d{4}\-(0[1-9]|1[012])\-(0[1-9]|[12][0-9]|3[01])$")
.WithMessage("Transaction from date should be in the pattern of YYYY-MM-DD");
RuleFor(model => model.EndDate)
.Matches(#"^\d{4}\-(0[1-9]|1[012])\-(0[1-9]|[12][0-9]|3[01])$")
.WithMessage("Transaction to date should be in the pattern of YYYY-MM-DD");
}
}

Here is a whole working demo you could follow:
Model:
public class ErrorDetail
{
public string traceId { get; set; }
public DateTime timestamp { get; set; }
public List<Error> errors { get; set; } = new List<Error>();
}
public class Error
{
public string errorCode { get; set; }
public string message { get; set; }
public string priority { get; set; }
public Properties properties { get; set; }=new Properties();
}
public class Properties
{
public string name { get; set; }
public string value { get; set; }
}
Static class:
public static class CustomProblemDetails
{
public static ErrorDetail ErrorDetail { get; set; } =new ErrorDetail();
public static IActionResult MakeValidationResponse(ActionContext context)
{
var problemDetails = new ValidationProblemDetails(context.ModelState)
{
Status = StatusCodes.Status400BadRequest,
};
foreach (var keyModelStatePair in context.ModelState)
{
var errors = keyModelStatePair.Value.Errors;
if (errors != null && errors.Count > 0)
{
if (errors.Count == 1)
{
var errorMessage = GetErrorMessage(errors[0]);
ErrorDetail.errors.Add(new Error()
{
errorCode= "Contract Violation",
message = errorMessage,
priority= "MEDIUM",
properties= new Properties() {
name = "string",
value = "string"
}
});
}
else
{
var errorMessages = new string[errors.Count];
for (var i = 0; i < errors.Count; i++)
{
errorMessages[i] = GetErrorMessage(errors[i]);
ErrorDetail.errors.Add(new Error()
{
errorCode = "Contract Violation",
message = errorMessages[i],
priority = "MEDIUM"
});
}
}
}
}
ErrorDetail.traceId = context.HttpContext.TraceIdentifier;
ErrorDetail.timestamp = DateTime.Now;
var result = new BadRequestObjectResult(ErrorDetail);
result.ContentTypes.Add("application/problem+json");
return result;
}
static string GetErrorMessage(ModelError error)
{
return string.IsNullOrEmpty(error.ErrorMessage) ?
"The input was not valid." :
error.ErrorMessage;
}
}
Register:
builder.Services.AddControllers().AddFluentValidation().ConfigureApiBehaviorOptions(options =>
{
options.InvalidModelStateResponseFactory = CustomProblemDetails.MakeValidationResponse;
});
Result:

Related

SQL Server / EF Core not respect date from the client

I'm a bit newbie with the MS tech stack, I developed a REST API using c# and EF core
I have this body request
{
"name": "Test Fecha",
"awardDate": "2020-05-19T00:00:00.000Z",
"governancePermit": "TEST",
"totalTickets": 5000,
"legals": "TEST",
"logo": "TEST",
"ticket": "TEST",
"ticketLifeTime": "200000",
"ticketPrice": 2000,
"saleStartDate": "2020-05-19T00:00:00.000Z",
"saleEndDate": "2020-05-19T00:00:00.000Z"
}
Thats the body of a post request to create a new resourse. That body is procesed by the next code:
Controller:
[HttpPost("/api/contest")]
public async Task<IActionResult> Create([FromBody] ContestCreateRequest request)
{
var activeContest = await _contestService.GetByStatus("Active");
if (activeContest == null)
{
var contest = new Contest
{
Name = request.Name,
AwardDate = DateTime.Parse(request.AwardDate),
TotalTickets = request.TotalTickets,
GovernancePermit = request.GovernancePermit,
Logo = request.Logo,
Ticket = request.Ticket,
Legals = request.Legals,
Status = "Active",
TicketLifeTime = request.TicketLifeTime,
TicketPrice = request.TicketPrice,
SaleStartDate = DateTime.Parse(request.SaleStartDate),
SaleEndDate = DateTime.Parse(request.SaleEndDate),
CreatedAt = DateTime.Parse(DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ")),
UpdatedAt = DateTime.Parse(DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ")),
};
var result = await _contestService.Create(contest);
if (result)
{
var response = new GeneralResponse
{
Message = "Contest Create",
Data = null,
Status = 201,
Success = true
};
return Ok(response);
}
else
{
var response = new GeneralResponse
{
Message = "Contest not create",
Data = null,
Status = 400,
Success = false
};
return BadRequest(response);
}
}
else
{
var response = new GeneralResponse
{
Message = "Only one contest can be active",
Data = null,
Status = 400,
Success = false
};
return BadRequest(response);
}
}
As you can see I am only parsing the dates from string to a DateTime Object
Then the the object(entity) is inserted in the data base with the following code:
public async Task<bool> Create(Contest contest)
{
await _dataContext.Contest.AddAsync(contest);
var create = await _dataContext.SaveChangesAsync();
return create > 0;
}
Model:
[Table("Contest")]
public class Contest
{
[Key]
public int ContestId { get; set; }
[Column("id_udlap")]
public int IdUdlap { get; set; }
[Column("name")]
public string Name { get; set; }
[Column("start_date")]
public DateTime StartDate { get; set; }
[Column("end_date")]
public DateTime EndDate { get; set; }
[Column("award_date")]
public DateTime AwardDate { get; set; }
[Column("avaible_tickets")]
public int AvaibleTickets { get; set; }
[Column("total_tickets")]
public int TotalTickets { get; set; }
[Column("status")]
public string Status { get; set; }
[Column("dynimic_fields")]
public string DynamycFields { get; set; }
[Column("custom_message")]
public string CustomMessage { get; set; }
[Column("grateful_message")]
public string GratefulMessage { get; set; }
[Column("ticket_life_time")]
public string TicketLifeTime { get; set; }
[Column("ticket_price")]
public double TicketPrice { get; set; }
[Column("governance_permit")]
public string GovernancePermit { get; set; }
[Column("legals")]
public string Legals { get; set; }
[Column("logo")]
public string Logo { get; set; }
[Column("ticket")]
public string Ticket { get; set; }
[Column("sale_start_date")]
public DateTime SaleStartDate { get; set; }
[Column("sale_end_date")]
public DateTime SaleEndDate { get; set; }
[Column("created_at")]
public DateTime CreatedAt { get; set; }
[Column("updated_at")]
public DateTime UpdatedAt { get; set; }
public List<Ticket> Tickets { get; set; }
public Contest() {}
}
But when I retrieve the object with this code:
public async Task<Contest> GetByStatus(string status)
{
var result = await _dataContext.Contest.SingleOrDefaultAsync(c => c.Status == status);
return result;
}
In this case status is "Active", that returns this.
{
"status": 200,
"message": "Active Contest",
"data": {
"contestId": 1,
"name": "Test Fecha",
"startDate": "0001-01-01T00:00:00",
"endDate": "0001-01-01T00:00:00",
"awardDate": "2020-05-18T19:00:00",
"avaibleTickets": 0,
"totalTickets": 5000,
"status": "Active",
"dynamycFields": null,
"customMessage": null,
"gratefulMessage": null,
"ticketLifeTime": "200000",
"ticketPrice": 2000,
"governancePermit": "TEST",
"legals": "TEST",
"logo": "TEST",
"ticket": "TEST",
"saleStartDate": "2020-05-18T19:00:00",
"saleEndDate": "2020-05-18T19:00:00",
"createdAt": "2020-05-19T19:04:10.517",
"updatedAt": "2020-05-19T19:04:10.518",
"tickets": null
},
"success": true,
"pages": 0,
"totalData": 0
}
This three fields (saleEndDate,saleEndDate,awardDate) are not the value that I charge in the request body.
The fast solution is add the hours that are out of phase, but Why this is happen? Is there other way to fix it or avoid it.
I suspect that is a SQL server configuration issue because I did it with two different instances and I obtain two different results, but I am not sure
Thanks a lot.
With the comments of Caius Jard (thanks a lot) and this question in SO
Convert DateTimeOffset to DateTime and add offset to this DateTime
And this:
Convert datetime without timezone
I was able to achieve the desired behavior
Hera the code that works for me:
var activeContest = await _contestService.GetByStatus("Active");
if (activeContest == null)
{
var AwardDateFormated = DateTimeOffset.Parse(request.AwardDate);
var StartDateFormated = DateTimeOffset.Parse(request.SaleStartDate);
var EndDateFormated = DateTimeOffset.Parse(request.SaleEndDate);
var contest = new Contest
{
Name = request.Name,
AwardDate = AwardDateFormated.UtcDateTime,
TotalTickets = request.TotalTickets,
GovernancePermit = request.GovernancePermit,
Logo = request.Logo,
Ticket = request.Ticket,
Legals = request.Legals,
Status = "Active",
TicketLifeTime = request.TicketLifeTime,
TicketPrice = request.TicketPrice,
SaleStartDate = StartDateFormated.UtcDateTime,
SaleEndDate = EndDateFormated.UtcDateTime,
CreatedAt = DateTime.Parse(DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ")),
UpdatedAt = DateTime.Parse(DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ")),
};
var result = await _contestService.Create(contest);
Now I retrieve exactly the data that I want, I don't really know if this is the best solution but for now works. Hope helps someone else.

How to receive JSON data in Dictionary using FromData in Web api?

Below is JSON example which client will send to my API named as 'GetQuestion'
{
"lstQuestions": [{
"QuestionCategory": 1,
"QuestionText": "what is m in mvc",
"OptionA": "model",
"OptionB": "view",
"OptionC": "controller",
"OptionD": "razor",
"CorrectOption": "A"
},
{
"QuestionCategory": 2,
"QuestionText": "How are you",
"OptionA": "fine",
"OptionB": "not fine",
"OptionC": "ok",
"OptionD": "not ok",
"CorrectOption": "A"
}],
"Status" : 1
}
Below is my controller API code:
public class QuestionDetails
{
public List<Questions> lstQuestions { get; set; }
public int Status { get; set; }
}
public class Questions
{
public string QuestionCategory { get; set; }
public string QuestionText { get; set; }
public string OptionA { get; set; }
public string OptionB { get; set; }
public string OptionC { get; set; }
public string OptionD { get; set; }
public string CorrectOption { get; set; }
}
[Route("GetQuestions")]
[HttpPost]
public HttpResponseMessage SendQuestionDetails([FromBody] QuestionDetails UserDetailInput)
{
HttpResponseMessage mesage = Request.CreateResponse(HttpStatusCode.OK, "Demo"); ;
if (ModelState.IsValid)
{
//in progress
}
return mesage;
}
What I want to do is how to create a class with Dictionary and pass as parameter, I don't want to use List because its heavy and Dictionary is much faster than List.
For example:
public class QuestionDetails
{
public Dictionary<string, Questions> lstQuestions { get; set; }
public int Status { get; set; }
}
public HttpResponseMessage SendQuestionDetails([FromBody] Dictionary<string, QuestionDetails> UserDetailInput)
{
HttpResponseMessage mesage = Request.CreateResponse(HttpStatusCode.OK, "Demo"); ;
if (ModelState.IsValid)
{
//in progress
}
return mesage;
}
Don't know what are you talking about with Dictionary is much faster than List but you just need to send the JSON as
{
"A": {
"lstQuestions": {
"A": {
"QuestionCategory": 1,
"QuestionText": "what is m in mvc",
"OptionA": "model",
"OptionB": "view",
"OptionC": "controller",
"OptionD": "razor",
"CorrectOption": "A"
},
"V": {
"QuestionCategory": 2,
"QuestionText": "How are you",
"OptionA": "fine",
"OptionB": "not fine",
"OptionC": "ok",
"OptionD": "not ok",
"CorrectOption": "A"
}
},
"Status": 1
}
}
Hope below code will clear your query.
public HttpResponseMessage SendQuestionDetails([FromBody] Dictionary<string, QuestionDetails> UserDetailInput)
{
List<Questions> list = new List<Questions> { };
list.Add(new Questions
{
CorrectOption = "CorrectOption1",
OptionA = "OptionA1",
OptionB = "OptionB1",
OptionC = "OptionC1",
OptionD = "OptionD1",
QuestionCategory = "QuestionCategory1",
QuestionText = "QuestionText1"
});
list.Add(new Questions
{
CorrectOption = "CorrectOption2",
OptionA = "OptionA2",
OptionB = "OptionB2",
OptionC = "OptionC2",
OptionD = "OptionD2",
QuestionCategory = "QuestionCategory2",
QuestionText = "QuestionText2"
});
Dictionary<QuestionDetails, int> dictionary = new Dictionary<QuestionDetails, int> { };
QuestionDetails detail = new QuestionDetails { lstQuestions = list, Status = 1 };
HttpResponseMessage mesage = Request.CreateResponse(HttpStatusCode.OK, detail);
return mesage;
return mesage;
}

Use Linq or C# to parse Json

I am getting familiar with C# and Linq and appreciate any help. It should be easy for someone who works with it. I have a Json object that returns contact information. I also have a list of ids. I need to compare the list to the Json object and wherever the value in the list matches the userclientcode in the Json object, I need to extract the following information (only for the matches):
clienttaxonomy (if not empty)
fullname (if not empty)
[0]contactdata ( -> email if not null or empty)
[1]contactdata (-> address if not null or empty)
[2]contactdata (-> phone number if not null or empty)
First List
var fileContactIds = new List<string> { "5678765", "2135123", "12341234", "341234123", "12341234123", "2341234123", "341234123", "123412341", "13342354",
"12342341", "123412322", "163341234", "2345234115", "8967896", "75626234 };
JSON object returned with:
return JsonConvert.DeserializeObject<RelatedContacts>(json)?.list;
This is the Json object:
[![Json object][1]][1]
This is the Json string (unescaped):
{
"type": "com.kurtosys.api.userprofile.domain.RelatedContactList",
"list": [{
"objectlistid": 5678765,
"objectlisttypeid": 4567876,
"objectlistname": "ALL.National",
"clienttaxonomyid": 765677,
"clienttaxonomy": "National Wholesaler",
"order": 1,
"contacts": [{
"personid": 7654345678,
"fullname": "Person Jallo",
"userid": 876567,
"userclientcode": "341234123",
"contactdetails": [{
"contactid": 8765567,
"contacttypeid": 4565,
"contactdata": "person.contact#site.com"
}, {
"contactid": 876545678,
"contacttypeid": 4565,
"contactdata": "Baltimore,MD,21209,United States"
}, {
"contactid": 87654567,
"contacttypeid": 4584,
"contactdata": "410-413-2640"
}]
}]
}, {
"objectlistid": 765678,
"objectlisttypeid": 40400461,
"objectlistname": "RM.Internal",
"clienttaxonomyid": 7567898,
"clienttaxonomy": "Internal Regional Wholesaler",
"order": 2,
"contacts": [{
"personid": 56789876,
"fullname": "Jackson Man",
"userid": 876567,
"userclientcode": "1012275",
"contactdetails": [{
"contactid": 309598309,
"contacttypeid": 76546,
"contactdata": "mister.jackson##site.com.com"
}, {
"contactid": 876567,
"contacttypeid": 4581,
"contactdata": "Baltimore,MD,21209,United States"
}, {
"contactid": 876567,
"contacttypeid": 2342,
"contactdata": "123-413-2604"
}]
}]
}, {
"objectlistid": 309571364,
"objectlisttypeid": 40400461,
"objectlistname": "RM.External",
"clienttaxonomyid": 309580710,
"clienttaxonomy": "External Regional Wholesaler",
"order": 3,
"contacts": [{
"personid": 302736188,
"fullname": "Phal Sumi",
"userid": 303826019,
"userclientcode": "163341234",
"contactdetails": [{
"contactid": 309598253,
"contacttypeid": 2342,
"contactdata": "misters.emailas#site.com"
}, {
"contactid": 309611930,
"contacttypeid": 2342,
"contactdata": "Baltimore,MD,21209,United States"
}, {
"contactid": 34234132,
"contacttypeid": 3422,
"contactdata": "342-803-1793"
}]
}]
}]
}
How do I
1] Select using Linq and Lambdas and put in a list fullname, email, address etc from the deserialized object ?
2]compare with first list and only transfer those items where the userclientcode == the number in list A.
I have tried:
var query5 = relatedContact.Where(s => s.objectlistid == Convert.ToInt64(contacts.Select(t => t.id)))
var selected = relatedContact.Where(p => p.contacts
.Any(a => fileContactIds.Contains(p.contacts))
.ToList();
var query2 = relatedContact.Where(s => s.objectlistid == Convert.ToInt64(contacts.Select(t => t.id)))
.Select(s => new
{
Description = s.clienttaxonomy,
Fullname = s.contacts[0].fullname,
Email = s.contacts[0].contactdetails[0].contactdata,
Address = s.contacts[0].contactdetails[1].contactdata,
PhoneNumber = s.contacts[0].contactdetails[2].contactdata
});
But don't really know what I'm doing it seems. Any suggestions on how to get the required sections ? I think part of the reason is that the contactdata is a list.
Thanks all
You can create a classes for the desearlization of JSON Object like this
public class Rootobject
{
public string type { get; set; }
public List[] list { get; set; }
}
public class List
{
public int objectlistid { get; set; }
public int objectlisttypeid { get; set; }
public string objectlistname { get; set; }
public int clienttaxonomyid { get; set; }
public string clienttaxonomy { get; set; }
public int order { get; set; }
public Contact[] contacts { get; set; }
}
public class Contact
{
public long personid { get; set; }
public string fullname { get; set; }
public int userid { get; set; }
public string userclientcode { get; set; }
public Contactdetail[] contactdetails { get; set; }
}
public class Contactdetail
{
public int contactid { get; set; }
public int contacttypeid { get; set; }
public string contactdata { get; set; }
}
And then to extract the selected information we can also create a another class like
public class ExtractedInfo
{
public string ocClientTaxonomy { get; set; }
public string ocFullName { get; set; }
public CTDetails ocContactDetails { get; set; }
}
public class CTDetails
{
public string ocCTAddress { get; set; }
public string ocCTEmail { get; set; }
public string ocCTPhoneNumber { get; set; }
}
Now we have to find all the data from JSON
var fileContactIds = new List<string> { "5678765", "2135123", "12341234", "341234123", "12341234123", "2341234123", "341234123", "123412341", "13342354", "12342341", "123412322", "163341234", "2345234115", "8967896", "75626234" };
//Read JSON from txt file. You can do it by your way
string myjson = File.ReadAllText("Some.txt");
string ctphno, ctadd, ctemail, cltax, ctfullname;
List<ExtractedInfo> ei = new List<ExtractedInfo>();
CTDetails ctdtl = new CTDetails();
ExtractedInfo eiex = new ExtractedInfo();
//Deserialize the JSON string to Object.
Rootobject AllData = JsonConvert.DeserializeObject<Rootobject>(myjson);
//Finding all data in List Class
foreach(List lst in AllData.list)
{
cltax = lst.clienttaxonomy; // you can directly put eiex.ocClientTaxonomy = lst.clienttaxonomy;
foreach(Contact ct in lst.contacts)
{
//To check if value in the list matches the objectlistid in the Json object
if(fileContactIds.Contains(lst.objectlistid.ToString()))
{
ctfullname = ct.fullname; // you can directly put eiex.ocFullName = ct.fullname;
foreach(Contactdetail ctd in ct.contactdetails)
{
//Here we are trying to find the Match for Email.
if(Regex.IsMatch(ctd.contactdata, #"\A(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\Z", RegexOptions.IgnoreCase))
{
ctemail = ctd.contactdata;
ctdtl.ocCTEmail = ctemail;
}
//Here We trying to find the match for Phone Number.
else if(Regex.IsMatch(ctd.contactdata, #"\(?\d{3}\)?-? *\d{3}-? *-?\d{4}", RegexOptions.IgnoreCase))
{
ctphno = ctd.contactdata;
ctdtl.ocCTPhoneNumber = ctphno;
}
//If NOthing matches than it might be address (Assumed)
else
{
ctadd = ctd.contactdata;
ctdtl.ocCTAddress = ctadd;
}
}
eiex.ocFullName = ctfullname;
}
}
eiex.ocClientTaxonomy = cltax;
eiex.ocContactDetails = ctdtl;
ei.Add(eiex);
}
Hope this helps and fit in your requirements.

RestSharp :Getting null from the result.Data althouh at the time of debug result has JSON returned

I am using RestSharp and trying to deserialize the JSON result set and when I run the code I'm getting null from the result.Data although at the time of debugging the result has JSON returned.
private static void GetPanelList()
{
var client = new RestClient("http://CCCCCC.XXXXXX.com/API/v3/mailinglists/ML_dfgfghfghfh/contacts");
var request = new RestRequest(Method.GET);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("x-api-token", "JHFKFKJFYILIOROIY");
// IRestResponse response = client.Execute(request);
var result = client.Execute<List<PanelList>>(request);
foreach(var i in result.Data)
{
foreach(var j in i.elements)
{
Console.WriteLine(j.firstName);
}
}
Here is my POCO:
public class PanelList
{
public List<elements> elements { get; set; }
}
public class elements
{
public string id { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
public string email { get; set; }
public string externalDataReference { get; set; }
public List<EmbededData> ED { get; set; }
public List<ResponseHistory> RH { get; set; }
public List<EmailHistory> EH { get; set; }
}
and here is how my json result look like that am trying to parse.
{ "result": {
"elements": [
{
"id": "MLRP_b7q690QRSqCxVuR",
"firstName": "S1-User1-firstname",
"lastName": "S1-U2-lastname",
"email": "S1U1#XXXX.org.au",
"externalDataReference": null,
"embeddedData": {
"DateTaken": "20160519",
"TriggerResponseID": "R_3fE6zgBzLa24dgD",
"TriggerSurveyID": "SV_3TXTMnJlsUxVGRL"
},
"language": null,
"unsubscribed": false,
"responseHistory": [
{
"responseId": "R_3fE6zgBzLa24dgD",
"surveyId": "SV_3TXTMnJlsUxVGRL",
"date": "2016-05-20T04:09:09Z",
"emailDistributionId": null,
"finishedSurvey": true
}
],
"emailHistory": [
{
"emailDistributionId": "EMD_41wVLKlQaADQBcF",
"date": "2016-05-24T00:33:02Z",
"type": "Invite",
"result": "Success",
"surveyId": "SV_8wFieltINfFL2gl",
"read": false
}
]
}]}}
Shouldn't you be reading into a result "container" object?
container class:
public class PanelListContainer
{
public PanelList result { get; set; }
}
fetch code:
var result = client.Execute<PanelListContainer>(request);

Deserializing JSON response consisting of list of objects into C# class

I'm working with NewsBlur API which returns a feed list. The response JSON is of type:
{
"authenticated": true,
"user": "sathyabhat",
"feeds": {},
"flat_folders": { },
result: "ok"
}
where feeds: is of type
"feeds": {
"96705": {
"feed_address": "http://www.linuxhaxor.net/",
"updated": "8492 hours",
"favicon_text_color": null,
"subs": 35,
"feed_link": "http://www.linuxhaxor.net/",
"favicon_fetching": true,
"nt": 0,
"updated_seconds_ago": 30573336,
"num_subscribers": 35,
"feed_title": "LinuxHaxor.net",
"favicon_fade": null,
"exception_type": "feed",
"exception_code": 503,
"favicon_color": null,
"active": true,
"ng": 0,
"feed_opens": 0,
"id": 96705,
"ps": 0,
"has_exception": true
},
"768840": {
"feed_address": "http://feeds.feedburner.com/PathikShahDotCom",
"updated": "3 hours",
"favicon_text_color": "black",
"subs": 1,
"feed_link": "http://www.pathikshah.com/blog",
"favicon_fetching": false,
"nt": 0,
"updated_seconds_ago": 13043,
"num_subscribers": 1,
"feed_title": "Pathik Shah",
"favicon_fade": "769456",
"favicon_color": "b2d092",
"active": true,
"ng": 0,
"feed_opens": 0,
"id": 768840,
"ps": 0
},
"768842": {
"feed_address": "http://feeds.preshit.net/preshit/blog",
"updated": "3 hours",
"favicon_text_color": null,
"subs": 1,
"feed_link": "http://preshit.net",
"favicon_fetching": false,
"nt": 3,
"updated_seconds_ago": 13536,
"num_subscribers": 1,
"feed_title": "Preshit Deorukhkar",
"favicon_fade": null,
"favicon_color": null,
"active": true,
"ng": 0,
"feed_opens": 1,
"id": 768842,
"ps": 0
},
"768843": {
"feed_address": "http://quizwith.net/feed/",
"updated": "3 hours",
"favicon_text_color": "white",
"subs": 1,
"feed_link": "http://quizwith.net",
"favicon_fetching": false,
"nt": 0,
"updated_seconds_ago": 11617,
"num_subscribers": 1,
"feed_title": "quizwith.net",
"favicon_fade": "c22900",
"favicon_color": "fe6501",
"active": true,
"ng": 0,
"feed_opens": 0,
"id": 768843,
"ps": 0
}
[... so on ..]
}
So essentially, it's a list of feeds objects. Now, I'm trying to deserialize this into a C# object using JSON.net, but my class members are populated with null values.
My class is modelled on the response that I'm getting:
public class FeedResponse
{
public string authenticated { get; set; }
public string user { get; set; }
public List<feed> feeds { get; set; }
public string flat_folders { get; set; }
public string result { get; set; }
}
public class feed
{
public string feed_address { get; set; }
public string updated { get; set; }
public string favicon_text_color { get; set; }
public int subs { get; set; }
public string feed_link { get; set; }
public bool favicon_fetching { get; set; }
public string nt { get; set; }
public string updated_seconds_ago { get; set; }
public int num_subscribers { get; set; }
public string feed_title { get; set; }
public string favicon_fade { get; set; }
public string exception_type { get; set; }
public string exception_code { get; set; }
public string favicon_color { get; set; }
public bool active { get; set; }
public string ng { get; set; }
public int feed_opens { get; set; }
public int id { get; set; }
public bool has_exception { get; set; }
}
This is the line which attempts to deserialize( using RestSharp):
return Execute<FeedResponse>(request);
Evidently, something goes wrong while trying to parse the JSON for the feed response.
Any pointers as to what I'm doing wrong?
Feeds is a Dictionary<int,Feed> not a List<Feed> (theres is Keys).
edit: a little more details, a list of objects in json would look like that:
[{ name: 'elem1'},
{ name: 'elem2'},
{ name: 'elem3'}]
edit2: full sample working:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Script.Serialization;
namespace JSON {
class Program
{
static void Main(string[] args)
{
var json = #"{
""authenticated"": true,
""user"": ""sathyabhat"",
""feeds"": {
""96705"": {
""feed_address"": ""http://www.linuxhaxor.net/"",
""updated"": ""8492 hours"",
""favicon_text_color"": null,
""subs"": 35,
""feed_link"": ""http://www.linuxhaxor.net/"",
""favicon_fetching"": true,
""nt"": 0,
""updated_seconds_ago"": 30573336,
""num_subscribers"": 35,
""feed_title"": ""LinuxHaxor.net"",
""favicon_fade"": null,
""exception_type"": ""feed"",
""exception_code"": 503,
""favicon_color"": null,
""active"": true,
""ng"": 0,
""feed_opens"": 0,
""id"": 96705,
""ps"": 0,
""has_exception"": true
},
""768840"": {
""feed_address"": ""http://feeds.feedburner.com/PathikShahDotCom"",
""updated"": ""3 hours"",
""favicon_text_color"": ""black"",
""subs"": 1,
""feed_link"": ""http://www.pathikshah.com/blog"",
""favicon_fetching"": false,
""nt"": 0,
""updated_seconds_ago"": 13043,
""num_subscribers"": 1,
""feed_title"": ""Pathik Shah"",
""favicon_fade"": ""769456"",
""favicon_color"": ""b2d092"",
""active"": true,
""ng"": 0,
""feed_opens"": 0,
""id"": 768840,
""ps"": 0
},
""768842"": {
""feed_address"": ""http://feeds.preshit.net/preshit/blog"",
""updated"": ""3 hours"",
""favicon_text_color"": null,
""subs"": 1,
""feed_link"": ""http://preshit.net"",
""favicon_fetching"": false,
""nt"": 3,
""updated_seconds_ago"": 13536,
""num_subscribers"": 1,
""feed_title"": ""Preshit Deorukhkar"",
""favicon_fade"": null,
""favicon_color"": null,
""active"": true,
""ng"": 0,
""feed_opens"": 1,
""id"": 768842,
""ps"": 0
},
""768843"": {
""feed_address"": ""http://quizwith.net/feed/"",
""updated"": ""3 hours"",
""favicon_text_color"": ""white"",
""subs"": 1,
""feed_link"": ""http://quizwith.net"",
""favicon_fetching"": false,
""nt"": 0,
""updated_seconds_ago"": 11617,
""num_subscribers"": 1,
""feed_title"": ""quizwith.net"",
""favicon_fade"": ""c22900"",
""favicon_color"": ""fe6501"",
""active"": true,
""ng"": 0,
""feed_opens"": 0,
""id"": 768843,
""ps"": 0
}
},
""flat_folders"": { },
result: ""ok"" }";
var jsonSerializer = new JavaScriptSerializer();
var response = jsonSerializer.Deserialize<FeedResponse(json);
Console.Write(jsonSerializer.Serialize(response));
Console.ReadKey();
}
}
public class FeedResponse
{
public bool authenticated { get; set; }
public string user { get; set; }
public Dictionary<string,feed> feeds { get; set; }
public object flat_folders { get; set; }
public string result { get; set; }
}
public class feed
{
public string feed_address { get; set; }
public string updated { get; set; }
public string favicon_text_color { get; set; }
public int subs { get; set; }
public string feed_link { get; set; }
public bool favicon_fetching { get; set; }
public string nt { get; set; }
public string updated_seconds_ago { get; set; }
public int num_subscribers { get; set; }
public string feed_title { get; set; }
public string favicon_fade { get; set; }
public string exception_type { get; set; }
public string exception_code { get; set; }
public string favicon_color { get; set; }
public bool active { get; set; }
public string ng { get; set; }
public int feed_opens { get; set; }
public int id { get; set; }
public bool has_exception { get; set; }
} }
Use a jsonreader, using json.net, your example should look like this:
public class Feeds
{
public bool authenticated;
public string user;
public Dictionary<string, Feed> feeds;
public void ReadJson(JsonReader reader)
{
while (reader.Read())
{
if (reader.TokenType == JsonToken.EndObject)
break;
if (reader.TokenType == JsonToken.PropertyName && (string)reader.Value == "authenticated")
{
reader.Read();
authenticated = (bool)reader.Value;
}
else if (reader.TokenType == JsonToken.PropertyName && (string)reader.Value == "user")
{
reader.Read();
user = (string)reader.Value;
}
else if (reader.TokenType == JsonToken.PropertyName && (string)reader.Value == "feeds")
{
feeds = new Dictionary<string, Feed>();
while (reader.Read())
{
if (reader.TokenType == JsonToken.EndObject)
break;
if (reader.TokenType == JsonToken.PropertyName)
{
string key = (string)reader.Value;
feeds.Add(key, Feed.ReadFromJson(reader));
}
}
}
}
}
public static Feeds ReadFromJson(Stream stream)
{
using (StreamReader sr = new StreamReader(stream))
{
using (Newtonsoft.Json.JsonReader jsonReader = new Newtonsoft.Json.JsonTextReader(sr))
{
Feeds feeds = new Feeds();
feeds.ReadJson(jsonReader);
return feeds;
}
}
}
}
public class Feed
{
public string feed_address;
public string updated;
public void ReadJson(JsonReader reader)
{
while (reader.Read())
{
if (reader.TokenType == JsonToken.EndObject)
break;
if (reader.TokenType == JsonToken.PropertyName && (string)reader.Value == "feed_address")
{
reader.Read();
feed_address = (string)reader.Value;
}
else if (reader.TokenType == JsonToken.PropertyName && (string)reader.Value == "updated")
{
reader.Read();
updated = (string)reader.Value;
}
}
}
public static Feed ReadFromJson(JsonReader reader)
{
Feed feed = new Feed();
feed.ReadJson(reader);
return feed;
}
}
Since your returned Json string contains names like 96705,768840,768842,768843 which are not valid c# variable names, you can not deserialize your string to a class unless you use a dictionary for feeds. So, if accessing some of the properties of your object as dict["key"] is inevitable, then why bother with declaring classes FeedResponse or feed anyway? Just use Json.Net's JObject, JToken, JProperty as below.
JObject rootObj = JsonConvert.DeserializeObject(jstr) as JObject;
Console.WriteLine(
"Authenticated:" + rootObj["authenticated"] +
" USER:" + rootObj["user"] +
" RESULT:" + rootObj["result"]);
foreach (JProperty feed in rootObj["feeds"])
{
Console.WriteLine(feed.Name + " " + feed.Value["feed_address"]);
}

Categories