Web API - Error in sample generation - c#

I'm facing a strange problem in ASP.NET web api. I made a generic class to be the returned model for all web methods. This is the model's code :-
public class RequestResponse<T>
{
public bool Sucess { get; set; }
public string Message { get; set; }
public T ReturnedData { get; set; }
public List<T> ReturnedDataList { get; set; }
}
Whenever I try using it in HTTP method like this :
public RequestResponse<BillsModel> CreateBill([FromBody] BillsModel billToAdd, MainHelpers.UserType userType)
This is BillsModel class :
public class BillsModel
{
public Guid Id { get; set; }
public DateTime BillDate { get; set; }
public long Number { get; set; }
public Guid OrderId { get; set; }
public int OrderType { get; set; }
public Guid PlaceId { get; set; }
public double TaxPercentage { get; set; }
public double? DiscountPercentage { get; set; }
public Guid CreatedBy { get; set; }
public DateTime CreatedOn { get; set; }
public Guid? ModifiedBy { get; set; }
public DateTime? ModifiedOn { get; set; }
}
And then running the api to see the methods and choosing a method, I see the below :
{
"Sucess" : true,
"Message" : "sample string 2",
"ReturnedData" : {
"$id" : "2",
"Id" : "14b479ec-c128-4916-8ed5-c0067c20fd9f",
"BillDate" : "2016-05-20T21:57:32.530957+02:00",
"Number" : 3,
"OrderId" : "dd266c00-0167-49e8-a8f0-0996aca21490",
"OrderType" : 5,
"PlaceId" : "98059b6e-acfb-4c89-8c6a-72ef30cce4d6",
"TaxPercentage" : 7.1,
"DiscountPercentage" : 1.1,
"CreatedBy" : "ba106082-44be-4a53-9d32-40bf4ee32bde",
"CreatedOn" : "2016-05-20T21:57:32.530957+02:00",
"ModifiedBy" : "3d164ffd-aa1c-40d4-a646-9d6ca91615db",
"ModifiedOn" : "2016-05-20T21:57:32.530957+02:00"
},
"ReturnedDataList" : {
"$id" : "3",
"$values" : [{
"$ref" : "2"
}, {
"$ref" : "2"
}
]
}
}
As you can see, it works fine when it's about an object but when it comes to a list, an unidentified object is shown "$ref". The same for "$id" and "$values".
Can you help me with that please ?

You can disable this behavior by adding this at the end of your Application_Start() method Global.asax:
GlobalConfiguration
.Configuration
.Formatters
.JsonFormatter
.SerializerSettings
.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.None;

I created the following web service and am using a generic type to return the packaged data and I'm not getting what you're getting. Can you run this on a separate project and see what JSON data is being returned?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
public class Account
{
public int AccountID { get; set; }
}
public class BusinessAccount : Account
{
public string BusinessAddress { get; set; }
}
public class Package<T>
{
public T Data { get; set; }
}
/// <summary>
/// using fiddler, I made a request to http://localhost/api/account
/// and got
///
/// {"Data":{"BusinessAddress":"ABC","AccountID":123}}
///
/// without making any configuration.
///
///
/// </summary>
namespace test
{
public class AccountController : ApiController
{
// GET api/<controller>
public Package<BusinessAccount> Get()
{
return new Package<BusinessAccount>()
{
Data = new BusinessAccount()
{
AccountID = 123,
BusinessAddress = "ABC"
}
};
}
}
}

Related

Unexpected parameters definition using Swashbuckle

I have such controller
[HttpGet]
public Task<IActionResult> GetCsvAudit([FromQuery] ReportDto request)
{
return Task.FromResult<IActionResult>(Ok());
}
public class ReportDto
{
public DateTimeRange ActionDateRange { get; set; }
public PhiResourceType OrderBy { get; set; }
public string PhiResourceTypeIdentifier { get; set; }
}
DateTimeRange is configured for Swagger as follow:
c.MapType<DateTimeRange>(() => new OpenApiSchema()
{
Description = "Date range",
Type = "string",
Title = "Date range",
Example = new OpenApiString("2020-11-15T00:42:13+00:00 - 2020-11-16T00:42:13+00:00"),
});
The problem is the Swagger still wants me to provide every DateTimeRange property in query while I'm using the [FromQuery] attribute, when I want it to expects me to provide the string 2020-11-15T00:42:13+00:00,2020-11-16T00:42:13+00:00 for it.
If you mark field/property as internal or protected or private, it will be ignored automatically by swashbuckle in swagger documentation:
public class DateTimeRange
{
internal string Start { get; set; }
internal string End { get; set; }
}
Result:

C# JsonSerializer.Deserialize array

All,
Edit: Firstly thanks for everyone's help. Secondly I'm new to Stack Overflow so apologises if I've added this edit incorrectly.
Following the commments and replies I've updated my class structure to:
services class:
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json;
namespace RTT_API
{
class services
{
public List<service> service = new List<service>();
public services()
{
}
}
}
Service class:
using System;
using System.Collections.Generic;
using System.Text;
namespace RTT_API
{
class service
{
public string atocCode{get; set;}
public service()
{
}
}
}
Unfortunately I'm still getting the same error. I think I still haven't quite matched my class structure to the JSON structure? Unfortunately I'm not sure where my mistake is. If it helps to highlight my mistake using a comparison then the following works:
Location class
using System;
using System.Collections.Generic;
using System.Text;
namespace RTT_API
{
class location
{
public string name { get; set; }
public string crs { get; set; }
public location()
{
}
}
}
Location deserilisation command and test output:
location locations = JsonSerializer.Deserialize<location>(channelResponse.RootElement.GetProperty("location").GetRawText());
MessageBox.Show(locations.crs);
Original question:
My JSON is as follows:
{
"location": {
"name": "Bournemouth",
"crs": "BMH",
"tiploc": "BOMO"
},
"filter": null,
"services": [
{
"locationDetail": {
"realtimeActivated": true,
"tiploc": "BOMO",
"crs": "BMH",
"description": "Bournemouth",
"wttBookedArrival": "011630",
"wttBookedDeparture": "011830",
"gbttBookedArrival": "0117",
"gbttBookedDeparture": "0118",
"origin": [
{
"tiploc": "WATRLMN",
"description": "London Waterloo",
"workingTime": "230500",
"publicTime": "2305"
}
],
"destination": [
{
"tiploc": "POOLE",
"description": "Poole",
"workingTime": "013000",
"publicTime": "0130"
}
],
"isCall": true,
"isPublicCall": true,
"realtimeArrival": "0114",
"realtimeArrivalActual": false,
"realtimeDeparture": "0118",
"realtimeDepartureActual": false,
"platform": "3",
"platformConfirmed": false,
"platformChanged": false,
"displayAs": "CALL"
},
"serviceUid": "W90091",
"runDate": "2013-06-11",
"trainIdentity": "1B77",
"runningIdentity": "1B77",
"atocCode": "SW",
"atocName": "South West Trains",
"serviceType": "train",
"isPassenger": true
}
]
}
My class structure is as follows:
servicelist class:
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json;
namespace RTT_API
{
class servicelist
{
public List<services> service = new List<services>();
public servicelist()
{
}
}
}
services class:
using System;
using System.Collections.Generic;
using System.Text;
namespace RTT_API
{
class services
{
public int serviceUid;
public services()
{
}
}
}
For deserialisation I have tried:
services servicelist = JsonSerializer.Deserialize<services>(channelResponse.RootElement.GetProperty("services").GetRawText());
and
servicelist servicelist = JsonSerializer.Deserialize<servicelist>(channelResponse.RootElement.GetProperty("services").GetRawText());;
In both cases I get 'System.Text.Json.JsonException'
I think there is a mismatch betwee the class structure and the JSON but I can't work what the problem is? It's the first time I've tried to desarialise an array.
Thanks
using System;
using System.Collections.Generic;
using System.Text;
namespace RTT_API
{
class location
{
public string name { get; set; }
public string crs { get; set; }
public location()
{
}
}
}
You can generate exact C# classes according to your JSON using tools for exactly that purpose. I used https://json2csharp.com/ , another is https://jsonutils.com/ - these are web services and don't require installation on computer, another option is generating classes through Visual Studio (with Web Essentials installed), there you would use Edit - Paste special - paste JSON as class.
Once you have the valid classes (I pasted generated classes below) you can deserialize entire Root object and then access any part of it, including services part:
// jsonInputText holds entire JSON string you posted
Root root = JsonSerializer.Deserialize<Root>(jsonInputText);
List<Service> serviceList = root.services;
Generated classes:
public class Location
{
public string name { get; set; }
public string crs { get; set; }
public string tiploc { get; set; }
}
public class Origin
{
public string tiploc { get; set; }
public string description { get; set; }
public string workingTime { get; set; }
public string publicTime { get; set; }
}
public class Destination
{
public string tiploc { get; set; }
public string description { get; set; }
public string workingTime { get; set; }
public string publicTime { get; set; }
}
public class LocationDetail
{
public bool realtimeActivated { get; set; }
public string tiploc { get; set; }
public string crs { get; set; }
public string description { get; set; }
public string wttBookedArrival { get; set; }
public string wttBookedDeparture { get; set; }
public string gbttBookedArrival { get; set; }
public string gbttBookedDeparture { get; set; }
public List<Origin> origin { get; set; }
public List<Destination> destination { get; set; }
public bool isCall { get; set; }
public bool isPublicCall { get; set; }
public string realtimeArrival { get; set; }
public bool realtimeArrivalActual { get; set; }
public string realtimeDeparture { get; set; }
public bool realtimeDepartureActual { get; set; }
public string platform { get; set; }
public bool platformConfirmed { get; set; }
public bool platformChanged { get; set; }
public string displayAs { get; set; }
}
public class Service
{
public LocationDetail locationDetail { get; set; }
public string serviceUid { get; set; }
public string runDate { get; set; }
public string trainIdentity { get; set; }
public string runningIdentity { get; set; }
public string atocCode { get; set; }
public string atocName { get; set; }
public string serviceType { get; set; }
public bool isPassenger { get; set; }
}
public class Root
{
public Location location { get; set; }
public object filter { get; set; }
public List<Service> services { get; set; }
}
If you need to deserialize only just a part of your json then you can use the JObject and JToken helper classes for that.
var json = File.ReadAllText("Sample.json");
JObject topLevelObject = JObject.Parse(json);
JToken servicesToken = topLevelObject["services"];
var services = servicesToken.ToObject<List<Service>>();
The topLevelObject contains the whole json in a semi-parsed format.
You can use the indexer operator to retrieve an object / array by using one of the top level keys.
On a JToken you can call the ToObject<T> to deserialize the data into a custom data class.
In order to be able to parse your json I had to adjust the services type because the W90091 as serviceUid can't be parsed as int. So here is my Service class definition:
public class Service
{
public string ServiceUid;
}
One thing to note here is that casing does not matter in this case so please use CamelCasing in your domain models as you would normally do in C#.
Thanks for everyone's help.
Firstly I had to make a few changes to the class names as they didn't match the JSON. I also had to change the syntax of two commands which I've detailed below:
I changed the definition of the list of objects from:
public List<services> service = new List<services>();
to:
public List<service> destination { get; set; };
and deserilisation command from:
services servicelist = JsonSerializer.Deserialize<services>(channelResponse.RootElement.GetProperty("services").GetRawText());
to
var servicelist = JsonSerializer.Deserialize<List<service>>(channelResponse.RootElement.GetProperty("services").GetRawText());
The change from services to var might not be the best solution. I think it's the first change, and matching the class names to the JSON, that fundamentally fixed the issue.

Variable cannot be non-input type - GraphQL .Net Conventions

I have graphql.net implementation using conventions
I have my model defined as below.
public partial class Project
{
public Project()
{
ProjectGroup = new HashSet<ProjectGroup>();
ProjectUser = new HashSet<ProjectUser>();
Datasource = new HashSet<Datasource>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ProjectGroup> ProjectGroup { get; set; }
public virtual ICollection<ProjectUser> ProjectUser { get; set; }
public virtual ICollection<Datasource> Datasource { get; set; }
}
I am trying to update only name of above class.
using above class (which is basically kind of entity framework class, but that is irrelevant of this question)
So I have defined mutation as below.
public sealed class Mutation
{
public async Task<Project> SaveProject([Inject] IProjectRepository projectRepository, projectModels.Master.Project project)
{
return Mapper.Map<Project>(await projectRepository.SaveProject(project));
}
}
and I am calling this mutation as below.
axios
.post('https://localhost:44375/api/Graph', {
query: `mutation ($project: Project) {
saveProject(project: $project) {
name
}
}`,
variables: {
'project': { 'name' : data.label },
},
})
In response I am getting below error.
{"errors":[{"message":"Variable \"project\" cannot be non-input type \"Project\".","locations":[{"line":1,"column":11}],"extensions":{"code":"VALIDATION_ERROR"}}]}
what am I doing wrong?
From graphql.net convention's official repo, I found one example and there was one attribute used for input type. After use of that it is working.
https://github.com/graphql-dotnet/conventions/blob/master/samples/DataLoaderWithEFCore/DataLoaderWithEFCore/GraphApi/Schema/InputTypes/UpdateMovieTitleParams.cs
So it requires attribute something in a following way.
[InputType]
public class UpdateMovieTitleParams
{
public Guid Id { get; set; }
public string NewTitle { get; set; }
}

C# Json to custom class

I have json:
[
{
"Name" : "SHA of timestamp",
"ProjectURL" : "URL to Proj",
"AccountIdentifier" : "Account Identifier",
"Repositories":
{
"GitLink1":
{
"Login" : "login1",
"Password" : "pas1"
},
"GitLink2":
{
"Login" : "login2",
"Password" : "pas2"
},
...
"GitLink999":
{
"Login" : "login999",
"Password" : "pass999"
}
}
},
...
{
Same entry
}
]
I need to fill it in IEnumerable of my created classes
public class Git
{
public string Login { get; set; }
public string Password { get; set; }
}
public class Repositories
{
public Git git { get; set; }
}
public class ConfigEntry
{
public string Name { get; set; }
public string ProjectURL { get; set; }
public string AccountIdentifier { get; set; }
public Repositories Repositories { get; set; }
}
Using
IEnumerable<ConfigurationEntry> config = JsonConvert.DeserializeObject<IEnumerable<ConfigurationEntry>>(CONFIGJSON);
Where CONFIGJSON contains frst json file.
Gives me this data of ConfigEntry class :
Name : "filled with some data"
ProjectURL : same
AccountIdentifier : same
Repositories : NULL
How i can fill Repositories field with all data i have in config JSON?
Is there any ideas.
It looks like you probably shouldn't have a Repositories class - instead, change your ConfigEntry.Repositories property to:
public Dictionary<string, Git> Repositories { get; set; }
Then you'll get a dictionary where the key of "GetLink1" has a value with Login of "login1" etc.
Your class definitions aren't quite right.
Your "repositories" class only contains a single Git, but needs to be a Dictionary.
I think if you use this you will be close:
public class Git
{
public string Login { get; set; }
public string Password { get; set; }
}
public class ConfigEntry
{
public string Name { get; set; }
public string ProjectURL { get; set; }
public string AccountIdentifier { get; set; }
public Dictionary<string,Git> Repositories { get; set; }
}
you may also need a non-generic IEnumerable, so it knows which objects to actually create:
JsonConvert.DeserializeObject<List<ConfigurationEntry>>(CONFIGJSON);

Consuming JSON in C# from mailchimp campaignEmailStatsAIMAll

I am trying to program .net c# code to read the results from a call to campaignEmailStatsAIMAll().
Here is a sample of the JSON results I get:
{
"total":3,
"data":{
"some...#company.com":[
{
"action":"click",
"timestamp":"2012-10-18 20:55:52",
"url":"http:\/\/www.someurl.com?ct=t(First_Chimp_Test10_18_2012)",
"ip":"66.66.666.666"
},
{
"action":"open",
"timestamp":"2012-10-18 20:55:52",
"url":null,
"ip":"66.66.666.666"
}
],
"anothe...#corporation.com":[
{
"action":"open",
"timestamp":"2012-10-18 20:18:05",
"url":null,
"ip":"22.222.222.222"
},
{
"action":"open",
"timestamp":"2012-10-18 20:18:18",
"url":null,
"ip":"22.222.222.222"
},
{
"action":"click",
"timestamp":"2012-10-18 20:18:18",
"url":"http:\/\/www.someurl.com?ct=t(First_Chimp_Test10_18_2012)",
"ip":"22.222.222.222"
},
{
"action":"click",
"timestamp":"2012-10-18 20:21:57",
"url":"http:\/\/www.someurl.com?ct=t(First_Chimp_Test10_18_2012)",
"ip":"22.222.222.222"
}
],
"thir...#business.com":[
]
}
}
The problem I am having is how to define the C# object to accept this JSON string. The email addresses are being treated as the name part of a JSON name/value pair. I know there are 3rd party wrappers for .NET but I only want to call this API (maybe a couple of others down the line) and would prefer to code it myself. I want to be able to iterate through the object to extract the email address then action/timestamp/url for each one.
When I drop this JSON into the object creator at json2sharp.com, I get this:
public class SomebodyCompanyCom {
public string action { get; set; }
public string timestamp { get; set; }
public string url { get; set; }
public string ip { get; set; }
}
public class AnotherpersonCorporationCom {
public string action { get; set; }
public string timestamp { get; set; }
public string url { get; set; }
public string ip { get; set; }
}
public class Data {
public List<SomebodyCompanyCom> __invalid_name__somebody#company.com { get; set; }
public List<AnotherpersonCorporationCom> __invalid_name__anotherperson#corporation.com { get; set; }
public List<object> __invalid_name__thirdguy#business.com { get; set; }
}
public class RootObject {
public int total { get; set; }
public Data data { get; set; }
}
A generator like that can only ever make best-guesses. Since JSON is generally schema-less, you need to analyze the data and figure out the best way to model it.
In your case, you have two basic types:
the individual entries consisting of action, timestamp, url, and ip
the overall container with total and data
A reasonable model might look like this:
public class ItemInfo {
public string action { get; set; }
public string timestamp { get; set; }
public string url { get; set; }
public string ip { get; set; }
}
public class ContainerType {
public int total;
public Dictionary<string, ItemInfo[]> data;
}
If you use a decent JSON library like JSON.Net, you could deserialize the JSON string into an ContatinerType structure like so:
string rawJsonString = MagicallyGetTheJsonFromAWebservice();
ContainerType container = JsonConvert.DeserializeObject<ContainerType>(rawJsonString);
It might need a bit of tweaking to get the type deserialization to work exactly, but that's the basic idea.
Edit: sample console program to do the deserialization:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
namespace ConsoleApplication1
{
class Program
{
static string testData = #"{
'total':3,
'data':{
'some...#company.com':[
{
'action':'click',
'timestamp':'2012-10-18 20:55:52',
'url':'http:\/\/www.someurl.com?ct=t(First_Chimp_Test10_18_2012)',
'ip':'66.66.666.666'
},
{
'action':'open',
'timestamp':'2012-10-18 20:55:52',
'url':null,
'ip':'66.66.666.666'
}
],
'anothe...#corporation.com':[
{
'action':'open',
'timestamp':'2012-10-18 20:18:05',
'url':null,
'ip':'22.222.222.222'
},
{
'action':'open',
'timestamp':'2012-10-18 20:18:18',
'url':null,
'ip':'22.222.222.222'
},
{
'action':'click',
'timestamp':'2012-10-18 20:18:18',
'url':'http:\/\/www.someurl.com?ct=t(First_Chimp_Test10_18_2012)',
'ip':'22.222.222.222'
},
{
'action':'click',
'timestamp':'2012-10-18 20:21:57',
'url':'http:\/\/www.someurl.com?ct=t(First_Chimp_Test10_18_2012)',
'ip':'22.222.222.222'
}
],
'thir...#business.com':[
]
}
}";
public class ItemInfo {
public string action { get; set; }
public string timestamp { get; set; }
public string url { get; set; }
public string ip { get; set; }
}
public class ContainerType {
public int total;
public Dictionary<string, ItemInfo[]> data;
}
static void Main(string[] args)
{
ContainerType container = JsonConvert.DeserializeObject<ContainerType>(testData);
if((container.total != 3)
|| (container.data.Count != 3)
|| (container.data["some...#company.com"][1].action != "open")
|| (container.data["anothe...#corporation.com"][2].url != "http://www.someurl.com?ct=t(First_Chimp_Test10_18_2012)"))
{
Console.WriteLine("Failed to deserialize");
}
else
{
Console.WriteLine("Successfully deserialized");
}
foreach (string email in container.data.Keys)
{
Console.WriteLine(email);
for (int x = 0; x < container.data[email].Count(); x++)
{
Console.WriteLine("\t" + x.ToString() + ":");
Console.WriteLine("\t\taction : " + container.data[email][x].action);
Console.WriteLine("\t\ttimestamp: " + container.data[email][x].timestamp);
Console.WriteLine("\t\turl : " + container.data[email][x].url);
Console.WriteLine("\t\tip : " + container.data[email][x].ip);
}
}
}
}
}

Categories