I've seen quite a few ways to output JSON in C#. None of them, however, seem easy. This may very well be due to my lack of knowledge of C#, though.
Question: If you were to write JSON (for use in JavaScript later on), how would you do it? I would prefer to not use third-party tools; however this is not an ultimatum of any kind. Is JSON doable in C# ASP.NET out of the box?
Example of JSON I wish to write:
{
"Trips": [
{
"from": "here",
"to": "there",
"stops": [
{
"place": "middle1",
"KMs": 37
},
{
"place": "middle2",
"KMs": 54
}
]
},
{
"from": "there",
"to": "here",
"stops": [
{
"place": "middle2",
"KMs": 37
},
{
"place": "middle1",
"KMs": 54
}
]
}
]
}
How about JavaScriptSerializer? Nice integrated solution, allowed to be expanded upon with JavaScriptConverters.
Demo of what you were after:
using System;
using System.Web.Script.Serialization;
using System.Text;
public class Stop
{
public String place { get; set; }
public Int32 KMs { get; set; }
}
public class Trip
{
public String from { get; set; }
public String to { get; set; }
public Stop[] stops { get; set; }
}
public class MyJSONObject
{
public Trip[] Trips { get; set; }
public override String ToString()
{
StringBuilder sb = new StringBuilder();
foreach (Trip trip in this.Trips)
{
sb.AppendFormat("Trip:\r\n");
sb.AppendFormat("\t To: {0}\r\n", trip.to);
sb.AppendFormat("\t From: {0}\r\n", trip.from);
sb.AppendFormat("\tStops:\r\n");
foreach (Stop stop in trip.stops)
{
sb.AppendFormat("\t\tPlace: {0}\r\n", stop.place);
sb.AppendFormat("\t\t KMs: {0}\r\n", stop.KMs);
}
sb.AppendLine();
}
return sb.ToString();
}
}
public class Test
{
public static void Main()
{
String example = "{\"Trips\":[{\"from\":\"here\",\"to\":\"there\",\"stops\":[{\"place\":\"middle1\",\"KMs\":37},{\"place\":\"middle2\",\"KMs\":54}]},{\"from\":\"there\",\"to\":\"here\",\"stops\":[{\"place\":\"middle2\",\"KMs\":37},{\"place\":\"middle1\",\"KMs\":54}]}]}";
JavaScriptSerializer serializer = new JavaScriptSerializer();
// Parse the string to our custom object
MyJSONObject result = serializer.Deserialize<MyJSONObject>(example);
Console.WriteLine("Object deserialized:\r\n\r\n{0}\r\n\r\n", result);
// take our custom object and dump it in to a string
StringBuilder sb = new StringBuilder();
serializer.Serialize(result, sb);
Console.WriteLine("Object re-serialized:\r\n\r\n{0}\r\n\r\n", sb);
// check step
Console.WriteLine(example.Equals(sb.ToString()) ? "MATCH" : "NO match");
}
}
And the output:
Object deserialized:
Trip:
To: there
From: here
Stops:
Place: middle1
KMs: 37
Place: middle2
KMs: 54
Trip:
To: here
From: there
Stops:
Place: middle2
KMs: 37
Place: middle1
KMs: 54
Object re-serialized:
{"Trips":[{"from":"here","to":"there","stops":[{"place":"middle1","KMs":37},{"pl
ace":"middle2","KMs":54}]},{"from":"there","to":"here","stops":[{"place":"middle
2","KMs":37},{"place":"middle1","KMs":54}]}]}
MATCH
Press any key to continue . . .
There is JavaScriptSerializer from System.Web.Extensions.dll (.NET 3.5 SP1)
http://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptserializer.aspx
You should really take a look at Json.NET
Not only does this library perform superbly it can parse JSON as well. Which means you can easily round trip stuff and communicate over JSON without involving a WCF service layer. It also does other neat things such as providing a queryable dynamic object model.
Related
This is my Object Class
public class MyObject
{
Public string Var1 { get; set; }
Public string Var2 { get; set; }
}
This is a get function of my controller class
[HttpGet]
public IActionResult GetObjList()
{
return Ok(new GenericModel<List<MyObject>>
{
Data = myobjectList
});
}
The GenericModel contains
public class GenericModel<T>
{
public T Data { get; set; }
public string[] Errors { get; set; }
}
My expected result look like this
{
"Data": [
{
"Var1": "val1",
"Var2": "val2"
}
]
}
But I'm getting this,
{
"data": [
{
"var1": "val1",
"var2": "val2"
}
]
}
I just want to get the output key values as same as the object variables, (in PascalCase)
I tried the solutions to add "AddJsonOptions" into the Startup.cs but they did not work. And I want the response as Pascal case, only for this controller requests, not in all requests including other controllers. (Sounds odd, but I want to try it) Are there any solutions? Is is impossible?
There may be another solution but I suggest building ContentResult yourself with JsonSerializer:
[HttpGet]
public IActionResult GetObjList()
{
return this.Content(JsonSerializer.Serialize(yourResult, yourJsonOption), "application/json");
}
For Pascal Case serialization use this code in Startup.cs:
services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy= null;
);
I have an API that returns the following response as string
[
{
"id": "62a9f8f90346133662624bd3",
"referenceID": "test1",
"additionalInfoList": ["string"]
},
{
"id": "62a9fba50346133662624bd4",
"referenceID": "111",
"additionalInfoList": ["string"]
}
]
edit: where the exact formatting of the string is as follows with escaping backslashes:
"[{\"id\":\"62a9f8f90346133662624bd3\",\"referenceID\":\"test1\",\"additionalInfoList\":[\"string\"]},{\"id\":\"62a9fba50346133662624bd4\",\"referenceID\":\"111\",\"additionalInfoList\":[\"string\"]}]"
and the following class model
public class IncidentModel
{
public string id { get; set; }
public string referenceID { get; set; }
public List<string> AdditionalInfoList { get; set; }
}
The problem arises in the code to deserialize. While I get a list with 2 elements, there is no data, only some generic metadata and fields that are not part of the model.
public async Task<JsonResult> OnGetIncidentsAsync()
{
List<IncidentModel> incidents = new List<IncidentModel>();
using (var httpClient = new HttpClient())
{
using (HttpResponseMessage response = await httpClient.GetAsync("api/Incident/GetAllIncidents.json/"))
{
string apiResponse = await response.Content.ReadAsStringAsync();
incidents = JsonConvert.DeserializeObject<List<IncidentModel>>(apiResponse);
}
}
return new JsonResult(incidents);
}
The attached image shows the data inside incidents.
How can I solve this?
So it turns out in Pages, I had a page named Incident. Because I use Razor pages, this page had a Model, named IncidentModel.cshtml.cs which was overriding the IncidentModel.cs from the Models folder. Renaming the model fixed the problem.
We have an API in our .Net Core 3.1 project that has a controller which returns a model that is documented by Swagger (Generated by Swashbuckle).
The consumer of the endpoint would like to see the options that a given property may return. For several reasons this for now at least is not just an Enum which would make the process a lot easier. It is a string that can return two different values.
Is there any way to decorate the property so that the options are available in Swagger just as if it had been an Enum? For Enums we can use the MapType on startup, but this is just a string so it's not a type we can map perse.
This is how we have done it with Enums previously:
c.MapType<PaginationSessionType>(() => new OpenApiSchema
{
Type = "string",
Enum = typeof(PaginationSessionType).GetEnumNames()
.Select(name => new OpenApiString(name)).Cast<IOpenApiAny>().ToList()
});
http://swagger-net-test.azurewebsites.net/swagger/ui/index#/TestStringEnum/TestStringEnum_Post
I was able to do what you need with "swagger": "2.0" not 100% sure with the version you are using... At that time I got what I needed with the RegularExpression decorator.
here is the code that generates that:
https://github.com/heldersepu/Swagger-Net-Test/blob/master/Swagger_Test/Controllers/TestStringEnumController.cs
using System.Web.Http;
using System.ComponentModel.DataAnnotations;
namespace Swagger_Test.Controllers
{
public class MyEndpointRequestClass
{
[RegularExpression("^(dark-blue|dark-red|light-blue|light-red)")]
public string StringEnumColor { get; set; }
[RegularExpression("^(high|medium|low)")]
public string Transparency { get; set; }
public string Name { get; set; }
}
public class TestStringEnumController : ApiController
{
public string Post([FromUri] MyEndpointRequestClass r)
{
return r.StringEnumColor;
}
public string Get([RegularExpression("^(uno|due)")]string x)
{
return x;
}
}
}
the relevant swagger.json from that is:
...
"MyEndpointRequestClass": {
"properties": {
"StringEnumColor": {
"type": "string",
"pattern": "^(dark-blue|dark-red|light-blue|light-red)"
},
"Transparency": {
"type": "string",
"pattern": "^(high|medium|low)"
},
"Name": {
"type": "string"
}
},
"xml": {
"name": "MyEndpointRequestClass"
},
"type": "object"
},
...
This might not answer you question but hopefully it sends you on the right direction
I'm quite new to Web-Api concepts. Trying to insert data using the basic project provided by Microsoft (Adding a simple Test Client to ASP.NET Web API Help Page).
http://blogs.msdn.com/b/yaohuang1/archive/2012/12/02/adding-a-simple-test-client-to-asp-net-web-api-help-page.aspx
Below is the code for Inserting data into Headoffice table.(HeadofficeID int ,HeadofficeName varchar(50),Notes varchar(1000),Isactive bit)
[POST("create")]
public HttpResponseMessage Post([FromUri]HeadOfficeModel headOffices)
{
if (_headOfficeBLL.Insert(headOffices) > 0)
return Request.CreateResponse(HttpStatusCode.Created, headOffices);
else
return Request.CreateResponse(HttpStatusCode.BadRequest, headOffices);
}
Headofficemodel Class
public partial class HeadOfficeModel : AtlAuthenticationModelBase
{
public int HeadOfficeId { get; set; }
public string HeadOfficeName { get; set; }
public string Notes { get; set; }
public bool IsActive { get; set; }
}
In the front end when i try to send the data from URI or Body only null values are getting inserting. While debugging all i can see in Headoffice model is null values.Below are the different ways i tried to insert data
1) {"HeadOfficeName":"TestHO1", "Notes":"notes", "IsActive":true}
2) {"TestHO1", "notes", true}
3) ={"headOffices":{"HeadOfficeName":"TestHO1","Notes":"notes","IsActive":false}}
and also tried to change the code as below
public HttpResponseMessage Post([FromUri]HeadOfficeModel headOffices)
public HttpResponseMessage Post([FromBody]HeadOfficeModel headOffices)
public HttpResponseMessage Post([ModelBinder]HeadOfficeModel headOffices)
Been trying to fix this from two days. When i send the data as complex type its not working else as separate parameters (changing the method to accept parameters) its working fine
public int Post(string Name, string Notes, bool Active)
{
HeadOfficeModel objHOM = new HeadOfficeModel();
objHOM.HeadofficeName = Name;
objHOM.Notes = Notes;
objHOM.IsActive = Active;
return _headOfficeBLL.Insert(objHOM);
}
Below is the html code where i m hiting while inserting
<script>
testClientModel = {
HttpMethod: '#Model.ApiDescription.HttpMethod',
UriPathTemplate: #Html.Raw(Json.Encode(Model.ApiDescription.RelativePath)),
UriParameters: [
#foreach (var parameter in Model.ApiDescription.ParameterDescriptions)
{
if (parameter.Source == System.Web.Http.Description.ApiParameterSource.FromUri)
{
#:{ name: "#parameter.Name", value: "" },
}
}
],
Samples: {
#Html.Raw(#String.Join(",", Model.SampleRequests.Select(s => String.Format("\"{0}\": \"{1}\"", s.Key, HttpUtility.UrlEncode(s.Value.ToString()))).ToArray()))
},
BaseAddress: '#applicationPath'
};
</script>
Can you please help me where am i going wrong. Attaching screenshot.
Entered both in URI and Body just to show that i tried different ways.
enter image description here
I was playing around with impromptu interface over a jobject and ran into the following issue
https://code.google.com/p/impromptu-interface/issues/detail?id=17
The issue is marked as 'Won't fix' and in the comments the author says that it could be fixed by implementing a custom impromptuobject.
Anyone have a sample of such an implementation? Or know another solution to this problem?
So the problem is that JArray has GetEnumerator() defined as interface-only, which makes the method no longer duck callable by the DLR. So below I've overriden the trygetmember to check if the result is a JArray's and convert it to a JEnumerable that implements GetEnumerator() in a dlr invokable way.
public class NonRecursiveJArrayConversionDictionary : ImpromptuDictionary{
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if(base.TryGetMember(binder, out result)){
if(result is JArray){
result = ((JArray)result).AsJEnumerable();
}
return true;
}
result = null;
return false;
}
}
However, this will only work for json structures that don't have arrays more then one property deep. You'll either have modify the above to recursively check anytime anything is returned maybe with a proxy, or modify the dictionary indexer's set to check and convert when deserialized instead.
Update: Json.net verion >= 5.0.4.16101 and ImpromptuInterface >= 6.1.4 will work out of the box.
void Main()
{
ICustomer customer = Impromptu.ActLike(JObject.Parse(#"
{
Id: 1,
Name:'Test',
Location:'Somewhere',
Employees: [
{ Id:1, EmployerId:39421, Name:'Joe' },
{ Id:2, EmployerId:39421, Name:'Jane' },
]
}
"));
foreach(var employee in customer.Employees){
employee.Id.Dump();
employee.Name.Dump();
}
}
public interface ICustomer
{
int Id { get; set; }
string Name { get; set; }
string Location { get; set; }
IList<IEmployee> Employees { get; }
}
public interface IEmployee
{
int Id { get; set; }
int EmployerId { get; set; }
string Name { get; set; }
}