I am writing my first Azure FunctionApp, and I am looking to return a valid JSON Object from a deserialized class that was created from a Faker JSON object as shown below:
Class:
public class TestData
{
//based on faker API
public int userId { get; set; }
public int id { get; set; }
public string title { get; set; }
public bool completed { get; set; }
}
Function App Entry point and return code:
public static Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,ExecutionContext context, ILogger log)
{
var configBuilder = new ConfigurationBuilder()
.SetBasePath(context.FunctionAppDirectory)
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
var config = configBuilder.Build();
log.LogInformation("Config Loaded");
// for later use when i get past the current issue
var settings = new AppSettings();
config.Bind("AppSettings", settings);
//Setting this seems to be the route cause, but need to respond correctly?
req.HttpContext.Response.Headers.Add("Content-Type", "application/json");
var worker = new WorkerClass();
var results = worker.DoSomeWork();
return Task.FromResult(results);
//Also Tried:
//return Task.FromResult<IActionResult>(new OkObjectResult(results));
}
Test Worker Class for returned object data and JSON Serialization:
public class WorkerClass
{
public IActionResult DoSomeWork()
{
try
{
var testdata = new TestData
{
userId = 123,
id = 1,
title = "delectus aut autem",
completed = true
};
//formatted as Json correctly
var results = JsonConvert.SerializeObject(testdata, Formatting.Indented);
return new OkObjectResult(results);
}
catch (Exception dswEx)
{
return new BadRequestObjectResult($"\{ERROR: {dswEx.Message}\}");
}
}
}
I have tried several ways to try and reach a clean JSON output using the response object types, as it would be good to add logic based on ObjectResults further down the line, but I suspect I am missing the obvious.
If the return is set to: return Task.FromResult(results); the the response is an escaped JSON output:
"{\"userId\": 1,\"id\": 1,\"title\": \"delectus aut autem\",\"completed\": false}"
if the return is amended to be: return Task.FromResult<IActionResult>(new OkObjectResult(results)); then the response is escaped but encapsulated in a Object wrapper:
{
"value": "{\r\n \"userId\": 1,\"id\": 1,\"title\": \"delectus aut autem\",\"completed\": false}",
"formatters": [],
"contentTypes": [],
"declaredType": null,
"statusCode": 200
}
All I would like to achieve is to have the correct response header as application/json and a result set presented as follows:
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
Thanks in advance.
You want to return new JsonResult(data), where data is your managed structures (not serialized json data like you were doing for some odd reason).
You don't need to serialize an object to json, Net will do it for you.
return Ok(testdata);
Related
I have managed to ignore the null buts but I have not been able to do the same with the empty strings. My project is an Azure Function running on .NET Core 3.1.
The configuration I have is the following in the startup
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System.Text.Json.Serialization;
builder.Services.AddMvcCore()
.AddNewtonsoftJson(options =>
{
options.UseMemberCasing();
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
options.SerializerSettings.MetadataPropertyHandling = MetadataPropertyHandling.Ignore;
options.SerializerSettings.Formatting = Formatting.Indented;
options.SerializerSettings.DefaultValueHandling = DefaultValueHandling.Ignore;
options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
})
.AddJsonOptions(option =>
{
option.JsonSerializerOptions.IgnoreNullValues = true;
option.JsonSerializerOptions.WriteIndented = true;
option.JsonSerializerOptions.IgnoreReadOnlyProperties = true;
});
Any idea why I don't ignore empty strings?
Your settings seems to be correct.
However you might need to decorate your properties with the Default value attribute for DefaultValueHandling.Ignore to kick in
Below is a working sample
static void Main()
{
var planet = new Planet();
planet.planetName = "";
var settings = new JsonSerializerSettings();
settings.DefaultValueHandling = DefaultValueHandling.Ignore;
Console.WriteLine(JsonConvert.SerializeObject(planet, settings));
}
public class Planet
{
public string planetID { get; set; } = Guid.NewGuid().ToString();
[DefaultValue("")]
public string planetName { get; set; }
}
Output : planetName property is ignored while serializing
EDIT :
Tried to implement AZ function app with a startup file and added settings for serialization. But I found that adding newtonsoft serializer settings to builder services has no effect on serialization.
Best workaround would be to add settings to function while serializing like below example from AZ function that i tried out.
FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
var content = await new StreamReader(req.Body).ReadToEndAsync();
var settings = new JsonSerializerSettings();
settings.DefaultValueHandling = DefaultValueHandling.Ignore;
settings.NullValueHandling = NullValueHandling.Ignore;
Planet deserializedPlanet = JsonConvert.DeserializeObject<Planet>(content);
string stringified = JsonConvert.SerializeObject(deserializedPlanet,settings);
string responseMessage = $"JSON serialized output{stringified}";
return new OkObjectResult(responseMessage);
}
Input :
Output :
Maybe override the tostring method and pass in your JSON options there or just do some isnullorempty checks before you jsonStringify.
or use
[JsonProperty("{propertyname}",DefaultValueHandling = DefaultValueHandling.Ignore)]
static void Main()
{
var planet = new Planet();
planet.planetName = "";
Console.WriteLine(planet.ToString());
}
public class Planet
{
public string planetID { get; set; } = Guid.NewGuid().ToString();
// optionally use this if you want fine grain controll
//[JsonProperty("planetName",DefaultValueHandling = DefaultValueHandling.Ignore)]
public string planetName { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this,
new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore
});
}
}
The model that I am waiting for is the following
{
"product": {
"id": 1,
"name": "room_premium",
"city": "medellin"
},
"parameters": [
{
"name": "PolicyHolder",
"inputParameterId": 495,
"inputParameterType": "Object",
"inputParameterSchemaList": [
{
"propertyId": "562",
"propertyName": "Birthdate",
"propertyValue": "",
"propertyTypeDescription": "Text",
"propertyTypeListValue": ""
},
{
"propertyId": "566",
"propertyName": "IdentificationNumber",
"propertyValue": "",
"propertyTypeDescription": "Text",
"propertyTypeListValue": ""
}
]
}
]}
But I should ignore the
"propertyValue": "",
"propertyTypeListValue": ""
I have an Windows WEB API with the following method:
public async Task<IHttpActionResult> SaveContract([FromBody] ModelDTO model)
{
string custName = model.CustomerName;
...
}
The Model I want looks like this:
public class ModelDTO
{
public int CustomerNumber { set; get; }
public string CustomerName { set; get; }
public string CustomerMail { set; get; }
public string imageDataBase64 { set; get; }
}
I want to call the API with my iOS App (Swift 4) with Alamofire 4.7.2
My dev server has a self-signed certificate. So I need to disable the evaluation.
let defaultManager: Alamofire.SessionManager = {
let serverTrustPolicies: [String: ServerTrustPolicy] = [
"devserver": .disableEvaluation
]
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
return Alamofire.SessionManager(
configuration: configuration,
serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies))
}()
let webApi: String = "https://devserver:7208/api/KP/SaveContract"
let data = UIImageJPEGRepresentation(photoImageView.image!,1) //got the Data form an image view
var imgString: String = ""
imgString = data.base64EncodedString()
let Param = Parameters = [
"CustomerNumber": 1,
"CustomerName": "Test Name",
"CustomerMail": "test#test.com",
"imageDataBase64": imgString]
defaultManager.upload(
multipartFormData: { multipartFormData in
for (key, value) in contAsJsonParam {
multipartFormData.append("\(value)".data(using: .utf8)!, withName:key)
}
},
to: webApi,
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.responseJSON { response in
debugPrint(response)
//lbl sichtbar machen
}
case .failure(let encodingError):
print(encodingError)
}
})
Call the api without image with Alamofire.request works, but with image request, it dosen't work. (bad ssl error)
So I try the upload method, but upload dosen't work in any way (with or without image string)
If I call the Api with Alamofire.upload I got a system.net.http.unsupportedmediatypeexception
"No MediaTypeFormatter is available to read an object of type
'ModelDTO' from content with media type 'multipart/form-data'."
I try to make the upload class as json by at "headers: Content-Type:application/json"
but no effect.
I try to fix it by putting
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("multipart/form-data"));
in the WebApiConfig. Then i got an other error
I got an NullReferenceException in the api at the line "string custName = model.CustomerName;"
You can use this code. I have tested this code with multipart data. It's working fine for me.
let url = "https://devserver:7208/api/KP/SaveContract"
//imp data need to be dynamic
let parameters: NSMutableDictionary = ["userId":"1123",
"caption":"hello how are you",
"keyword":"First Post",
"askPrice":"12345",
"postScope":"1",
"commentStatus":"1",
"gender":"male",
"email":"asd#asd.com"
]
var dictionaryHeaders = [String : String]()
dictionaryHeaders = ["Content-Type": "application/x-www-form-urlencoded" ]
Alamofire.upload(multipartFormData: { (multipartFormData) in
for (key, value) in parameters {
multipartFormData.append("\(value)".data(using: String.Encoding.utf8)!, withName: key as! String)
}
self.postImage = UIImage(named:"YOUR IMAGE NAME")
if let data = UIImageJPEGRepresentation(self.postImage,1) {
multipartFormData.append(data, withName: "postPic", fileName: "image.png", mimeType: "image/png")
}
}, usingThreshold: UInt64.init(), to: url, method: .post, headers: dictionaryHeaders ) { (result) in
switch result{
case .success(let upload, _, _):
upload.responseJSON{ response in
print(response)
}
case .failure(let error):
print("Error in upload: \(error.localizedDescription)")
}
}
I've created a small class like this one.
public class Device
{
public Device()
{
}
[DeserializeAs(Name = "u_friendly_name")]
//[SerializeAs(Name = "u_friendly_name")]
public string Name { get; set; }
}
}
In order to map "Name" to "u_friendly_name" (as it should be in json) I've added a DeserializeAs declaration. My first question here is.. do I need both DeserializeAs and SerializeAs if I want to POST/GET json or would DeserializeAs suffice?
Doing a GET like this works perfectly. It automatically manages to get the json and cast it to a device as required. However, doing a POST wasn't as simple...
public Device GetDevice(string guid)
{
var request = new RestRequest("api/now/table/x_device", Method.GET);
request.RootElement = "result";
var devices = client.Execute<List<Device>>(request);
return devices.Data.FirstOrDefault(d => d.Guid == guid);
}
I'm trying to do a POST like this.
public void RegisterDevice()
{
Device device = new Device();
device.Name = Environment.MachineName;
var request = new RestRequest("api/now/table/x_device", Method.POST);
request.RequestFormat = DataFormat.Json;
request.AddBody(device); // HERE
Console.WriteLine(request.Parameters[0]);
var response = client.Execute(request);
}
The problem is that the object isn't translated into json. Instead of having "u_friendly_name" in the json output I see "Name". I know this can be solved using Json.Net such as:
string json = JsonConvert.SerializeObject(device);
But that would also require me to add another dependency to my project and add the JsonProperty declaration to my class. I have been able to get POST working with Json.NET, but not GET and vise versa.
Anyone who can point me in the right direction of how to serialize my class using (preferably) only RestSharp?
Edit: After reading a lot of posts on stackoverflow, I get the impression that RestSharp just isn't suitable for deserialization. I found the following link that uses a custom json serializer and while POST works great, I'm not sure what I'm doing wrong with GET: http://bytefish.de/blog/restsharp_custom_json_serializer/
public Device GetDevice(string guid)
{
var request = new RestRequest("api/now/table/x_device", Method.GET);
request.RequestFormat = DataFormat.Json;
request.JsonSerializer = NewtonsoftJsonSerializer.Default;
request.RootElement = "result";
var devices = client.Execute<List<Device>>(request);
return devices.Data.FirstOrDefault(d => d.Guid == guid);
}
The only thing that works is setting both:
[JsonProperty(PropertyName = "u_friendly_name")]
[DeserializeAs(Name = "u_friendly_name")]
But that shouldn't be necessary I hope?
Edit2: I have manged to get POST to work by adding the "AddHandlers" to the RestSharp client as mentioned in the blog post above. I have also changed my class and added a "Devices" definition.
public class Devices
{
[JsonProperty(PropertyName = "result")]
public List<Device> devices { get; set; }
}
public class Device
{
public Device()
{
}
[DeserializeAs(Name = "u_friendly_name")]
//[SerializeAs(Name = "u_friendly_name")]
public string Name { get; set; }
}
}
Now my function looks like this and works as intended:
public Device GetDevice(string guid)
{
var request = new RestRequest("api/now/table/x_uia_sdm_device", Method.GET);
request.RequestFormat = DataFormat.Json;
request.JsonSerializer = NewtonsoftJsonSerializer.Default;
IRestResponse response = client.Execute(request);
var deviceList = JsonConvert.DeserializeObject<Devices>(response.Content);
return deviceList.devices.FirstOrDefault(d => d.Guid == guid);
}
Json:
{
"result": [
{
"u_guid": "12345678",
"u_friendly_name": "DeviceA",
"sys_mod_count": "1",
"sys_updated_on": "2017-03-09 11:58:50",
"sys_tags": "",
"u_config_override": "log_level_local = 5",
"u_running_version": "1.0.0",
"sys_id": "abcdefg",
"sys_created_on": "2017-03-07 12:25:06",
"u_reference_record": "",
"u_status": "Provisioned",
"sys_created_by": "foo#bar.no"
},
{
"u_guid": "34567890",
"u_friendly_name": "DeviceB",
"sys_mod_count": "0",
"sys_updated_on": "2017-03-08 13:23:06",
"sys_tags": "",
"u_config_override": "",
"u_running_version": "",
"sys_id": "abcdefg",
"u_application": "",
"u_reference_record": "",
"u_status": "",
"sys_created_by": "foo#bar.no"
}
]
}
Even though it now works my initial question still stands. If possible I would love to remove Json.Net and only use RestSharp.
i'm using ASP MVC 5. I have an action in a controller that return a json object:
[HttpGet]
public JsonResult GetUsers()
{
return Json(....., JsonRequestBehavior.AllowGet);
}
Now i want to use the JSON.Net library and i see that in ASP MVC 5 is yet present. In effect i can write
using Newtonsoft.Json;
without import the library from NuGet.
Now i've tried to write:
public JsonResult GetUsers()
{
return JsonConvert.SerializeObject(....);
}
But i have an error during compilation: I cann't convert the return type string to JsonResult.
How can i use the Json.NET inside an action? What is the correct return type of an action?
I'd prefer to create an object extension that results in a custom ActionResult as it can be applied inline to any object when returning it
The bellow extension make use of Newtonsoft Nuget to serialize objects ignoring null properties
public static class NewtonsoftJsonExtensions
{
public static ActionResult ToJsonResult(this object obj)
{
var content = new ContentResult();
content.Content = JsonConvert.SerializeObject(obj, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
content.ContentType = "application/json";
return content;
}
}
The bellow example demonstrate how to use the extension.
public ActionResult someRoute()
{
//Create any type of object and populate
var myReturnObj = someObj;
return myReturnObj.ToJsonResult();
}
Enjoy.
You can use ContentResult instead like this:
return Content(JsonConvert.SerializeObject(...), "application/json");
public string GetAccount()
{
Account account = new Account
{
Email = "james#example.com",
Active = true,
CreatedDate = new DateTime(2013, 1, 20, 0, 0, 0, DateTimeKind.Utc),
Roles = new List<string>
{
"User",
"Admin"
}
};
string json = JsonConvert.SerializeObject(account, Formatting.Indented);
return json;
}
or
public ActionResult Movies()
{
var movies = new List<object>();
movies.Add(new { Title = "Ghostbusters", Genre = "Comedy", Year = 1984 });
movies.Add(new { Title = "Gone with Wind", Genre = "Drama", Year = 1939 });
movies.Add(new { Title = "Star Wars", Genre = "Science Fiction", Year = 1977 });
return Json(movies, JsonRequestBehavior.AllowGet);
}
You basically need to write a custom ActionResult that is indicated here in this post
[HttpGet]
public JsonResult GetUsers()
{
JObject someData = ...;
return new JSONNetResult(someData);
}
The JSONNetResult function is:
public class JSONNetResult: ActionResult
{
private readonly JObject _data;
public JSONNetResult(JObject data)
{
_data = data;
}
public override void ExecuteResult(ControllerContext context)
{
var response = context.HttpContext.Response;
response.ContentType = "application/json";
response.Write(_data.ToString(Newtonsoft.Json.Formatting.None));
}
}
You might want to consider using IHttpActionResult since this will give you the benefit of serialization automatically (or you could do it yourself), but also allows you to control the returned error code in case errors, exceptions or other things happen in your function.
// GET: api/portfolio'
[HttpGet]
public IHttpActionResult Get()
{
List<string> somethings = DataStore.GetSomeThings();
//Return list and return ok (HTTP 200)
return Ok(somethings);
}
I'm unable to globally configure my ASP.NET MVC 4 application (using Web API controllers) so that actions that return HttpResponseMessage with a Json string that includes values of type Newtonsoft.Json.Linq.JValue on dynamic properties.
I can produce required output by serializing the JSON string manually in each action, but ideally I'd like to be able to globally configure the correct behaviour on Application_Start.
To demonstrate my problem, consider the following class:
public class Content : IAccountEntity
{
public string StringProperty { get; set; }
public dynamic DynamicProperty { get; set; }
}
Given the following global serialization configuration:
protected void Application_Start()
{
...
ConfigureJsonFormat();
}
private static void ConfigureJsonFormat()
{
var formatters = GlobalConfiguration.Configuration.Formatters;
formatters.Remove(formatters.XmlFormatter);
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
json.SerializerSettings.DateFormatHandling = Newtonsoft.Json.DateFormatHandling.IsoDateFormat;
json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
And the following MVC Action code
public HttpResponseMessage Get(HttpRequestMessage request)
{
... // Other initialisation code
content.StringProperty = "Hello world";
content.DynamicProperty.Email = new Newtonsoft.Json.Linq.JValue("test#test.com");
return Request.CreateResponse(HttpStatusCode.OK, content);
}
I get the following Json response. As you can see, it is missing the value for the dynamic sub property 'email'.
[
{
"stringProperty": "Hello world",
"dynamicProperty": {
email:[]
}
}
]
I can resolve the problem on a per action basis by taking a more manual approach to creating the JSON string response as follows:
public HttpResponseMessage Get(HttpRequestMessage request)
{
... // Other initialisation code
content.StringProperty = "Hello world";
content.DynamicProperty.Email = new Newtonsoft.Json.Linq.JValue("test#test.com");
var jsonSerializerSettings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() };
string jsonStr = JsonConvert.SerializeObject(content, Formatting.Indented, jsonSerializerSettings);
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(jsonStr, Encoding.UTF8, "application/json")
};
}
Which produces the desired output:
[
{
"stringProperty": "Hello world",
"dynamicProperty": {
email: "test#test.com"
}
}
]
I would prefer however to be able to replicate the above behaviour using the global configuration.
Is there a way I can modify my method ConfigureJsonFormat so that the values for dynamics are included on the Json response without having to code for this on an action by action basis?
I'm hoping that I can set something on json.SerializerSettings, but have not been able to find the correct setting as yet.