I am posting to an API that may return either 1 of the following 2 formats of JSON strings:
{
"MessageType": 6,
"Message": "Unable to SAVE new record. Invalid posted data."
}
or
{
"Model": {
"Id": "1-6Q0RZ9",
...
},
"ResponseResult": {
"MessageType": 10,
"Message": "Successfully saved, Record Id = 1-6Q0RZ9"
}
}
I need to retrieve the results from MessageType and have tried every if condition I can think of to read the results, because the syntax or retrieving the key:value is different for each JSON string, and there are no other flags to trigger one or the other. So the code I used is:
string result = eml.PostData("API/Save", dataJSON.ToString());
var returnresult = new JavaScriptSerializer().Deserialize<dynamic>(result);
try {
var responseresults = returnresult["ResponseResult"];
rr = responseresults["MessageType"];
rrtxt = responseresults["Message"];
} catch (Exception ex) {
rr = returnresult["MessageType"];
rrtxt = returnresult["Message"];
}
Which works great. If there is a valid Db post it returns the second JSON which is parsed correctly by the TRY statement, if not it throws a "key not found" error and parses the returned string in the CATCH statement (the first JSON example). Obviously this is horrible code but I cannot think of another way to do this, and I was wondering if anyone had suggestions? (please?)
Thanx in advance.
How about deserializing the response to an object with all of the properties on each return type and then just checking the values?
public class ReturnObject
{
public YourModel Model {get;set;}
public ResultObject ResponseResult {get;set;}
public int? MessageType {get;set;}
public string Message {get;set;}
}
string result = eml.PostData("API/Save", dataJSON.ToString());
var returnresult = new JavaScriptSerializer().Deserialize<ReturnObject>(result);
{
if(returnresult.MessageType.HasValue)
{
var messageType = returnResult.MessageType.Value;
etc etc.
}
}
Related
I would need some help for a string conversion from unicode (\u03a9\u039c\u0395\u0393\u0391) to normal string (ΩΜΕΓΑ).
I made a function that theoretically should work but it doesn't work instead. I don't understand what I'm doing wrong.
I receive json data with webclient.DownloadString:
{"id": "94401626335", "username": "\u03a9\u039c\u0395\u0393\u0391"}
I get the \u03a9\u039c\u0395\u0393\u0391 and send it to the function:
DecodeFromUtf8(username)
public string DecodeFromUtf8(string utf8String)
{
try
{
var output = WebUtility.HtmlDecode(utf8String);
return output;
}
catch (Exception ex)
{
return utf8String;
}
}
the function always returns me: \u03a9\u039c\u0395\u0393\u0391
and not: ΩΜΕΓΑ
Why?
i can't use external libraries like system.text.json
Thanks
use a json deserializer , let it do the work
public class Ooo {
public string id { get; set; }
public string username { get; set; }
}
var json = #"{""id"": ""94401626335"", ""username"": ""\u03a9\u039c\u0395\u0393\u0391""}";
var ooo = System.Text.Json.JsonSerializer.Deserialize<Ooo>(json) ;
Console.WriteLine("string = " + ooo.username);
}
gives (my console has a glyph misssing, but its show correctly in the debugger)
I have a simple web API that performs some basic validation using validation attributes. If the request does not meet the validation requirements, I then extract the error messages from the ModelStateDictionary and return these to the caller as a dictionary, where the key is the path to the property that has an error and the message indicates what the problem is.
The problem is that, given certain input's, the error messages in the ModelStateDictionary often contains information that I would not want to return to the client (such as the full name (including namespace) of the object that the converter attempted to map the JSON to).
Is it possible to differentiate between validation errors (generated by Validation Attributes / IValidatableObject implementations) and errors generated by attempting to map invalid JSON to a certain object?
For example:
My models
public class Order
{
[Required]
public Customer Customer { get; set; }
}
public class Customer
{
[Required]
public string Name { get; set; }
}
My action method
public IActionResult Post(Order order)
{
if (!ModelState.IsValid)
{
var errors = GetErrors(ModelState);
return BadRequest(errors);
}
return Ok();
}
private Dictionary<string, string> GetErrors(ModelStateDictionary modelState)
{
var errors = new Dictionary<string, string>();
foreach (var error in modelState)
{
string message = null;
if (error.Value.Errors.Any(e => e.Exception != null))
{
message = "Unable to interpret JSON value.";
}
else
{
message = string.Join(". ", string.Join(". ", error.Value.Errors.Select(e => e.ErrorMessage)));
}
errors.Add(error.Key, message);
}
return errors;
}
My example inputs:
Missing required data
{
"Customer": {
"Name": null // name is required
}
}
Because Customer.Name is decorated with the RequiredAttribute, this generates an error message of:
{
"Customer.Name": "The Name field is required."
}
This is something that's OK to return to the caller, so no issues here.
JSON value doesnt map to .Net object type
{
"Customer": 123 // deserializing will fail to map this value to a Customer object
}
Because deserializing the value 123 to a Customer object fails, this generates an error message of:
{
"Customer": "Error converting value 123 to type 'MyProject.Models.Customer'. Path 'Customer', line 2, position 19."
}
This is not OK to return to the caller, as it contains the full namespace path to the object being mapped to. In this case, something generic (such as "Bad JSON value.") should be used as the error message.
Can anyone help me find a solution to hide these error messages which contain information that should not be returned to the caller?
As can be seen in my code above, I thought I might be able to check the ModelEntry.Exception property and use this to determine whether the error message needs to be shielded from the caller, but this is null in both my examples.
On solution may be to check if the error message starts with Error converting value, and if so, shield the message from the caller. This doesnt seem very robust, and I'm not sure how reliable this will be in a real-world example.
The default response type for HTTP 400 responses is ValidationProblemDetails class. So, we will create a custom class which inherits ValidationProblemDetails class and define our custom error messages.
public class CustomBadRequest : ValidationProblemDetails
{
public CustomBadRequest(ActionContext context)
{
ConstructErrorMessages(context);
Type = context.HttpContext.TraceIdentifier;
}
private void ConstructErrorMessages(ActionContext context)
{
//this is the error message you get...
var myerror = "Error converting value";
foreach (var keyModelStatePair in context.ModelState)
{
var key = keyModelStatePair.Key;
var errors = keyModelStatePair.Value.Errors;
if (errors != null && errors.Count > 0)
{
if (errors.Count == 1)
{
var errorMessage = GetErrorMessage(errors[0]);
if (errorMessage.StartsWith(myerror))
{
Errors.Add(key, new[] { "Cannot deserialize" });
}
else
{
Errors.Add(key, new[] { errorMessage });
}
}
else
{
var errorMessages = new string[errors.Count];
for (var i = 0; i < errors.Count; i++)
{
errorMessages[i] = GetErrorMessage(errors[i]);
if (errorMessages[i] == myerror)
{
errorMessages[i] = "Cannot deserialize";
}
}
Errors.Add(key, errorMessages);
}
}
}
}
string GetErrorMessage(ModelError error)
{
return string.IsNullOrEmpty(error.ErrorMessage) ?
"The input was not valid." :
error.ErrorMessage;
}
}
Configure in your Startup.cs:
services.AddControllers().ConfigureApiBehaviorOptions(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
var problems = new CustomBadRequest(context);
return new BadRequestObjectResult(problems);
};
});
Be sure your Controller contains [ApiController] attribute.
Result:
Typically when model binding fails because the JSON payload is not properly formatted, then the incoming model object ("order" in your case) will be null. Just put in a simple null check and return BadRequest with a generic error message.
if (order == null)
return BadRequest("Invalid JSON");
I'm doing C# JSON <-> PHP JSON for the first time.
Thought I'd get on an easy road but seems like I've hit the rock.
I'm fairly sure that JSON from Newtonsoft allows "[" character but not sure why i have this error instead?
Here's my C# code:
public class SystemJSON
{
public bool Status { get; set; }
public string Message { get; set; }
public string ResponseData { get; set; }
}
public static class SystemCall
{
public static String Post(string uri, NameValueCollection pairs)
{
byte[] response = null;
using (WebClient wc = new WebClient())
{
response = wc.UploadValues(uri, pairs);
}
return Encoding.Default.GetString(response);
}
}
string system_Response = SystemCall.Post("http://127.0.0.1:8080/edsa-NEFS%20(PHP)/api.php", new NameValueCollection()
{
{"do_work", Functions.Get_Department_List.ToString()},
{"api_data", null }
});
**SystemJSON systemJSON = JsonConvert.DeserializeObject<SystemJSON>(system_Response);** //<-- Error happens here.
if(systemJSON.Status == true)
{
//do stuff here
}else
{
MessageBox.Show(this, systemJSON.Message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
And here's my PHP code:
<?php
// Load Request
$function_name = isset($_POST['do_work']) ? $_POST['do_work'] : '';
$api_data = isset($_POST['api_data']) ? $_POST['api_data'] : '';
// Validate Request
if (empty($function_name))
{
SystemResponse(false, 'Invalid Request');
}
if (!function_exists($function_name))
{
SystemResponse(false, 'API Method Not Implemented');
}
// Call API Method
call_user_func($function_name, $api_data);
/* Helper Function */
function SystemResponse($responseStatus, $responseMessage, $responseData = '')
{
exit(json_encode(array(
'Status' => $responseStatus,
'Message' => $responseMessage,
'ResponseData' => $responseData
)));
}
/* API Methods */
function Get_Department_List($api_data)
{
//Test ------------------------------------------START
$node = array();
$dept = array();
$responseData = array();
$dept['id'] = 1;
$dept['name'] = "General";
$dept['description'] = "Forms, Samples, Templates, Catalogs, etc";
$dept['status'] = 1;
array_push($node, $dept);
$dept['id'] = 2;
$dept['name'] = "Test";
$dept['description'] = "Testing";
$dept['status'] = 1;
array_push($node, $dept);
$responseData["dept"] = $dept;
SystemResponse(true, 'SUCCESS', $responseData);
//Test ------------------------------------------END
}
?>
And here's my error:
Newtonsoft.Json.JsonReaderException HResult=0x80131500
Message=Unexpected character encountered while parsing value: {. Path
'ResponseData', line 1, position 51.
The problem is that your C# SystemJSON class does not match the structure of the incoming JSON correctly.
ResponseData in your C# SystemJSON class is listed as a string but your PHP appears to be pushing out a complex object inside that property. You can't deserialise an object into a string - there is no way for the deserialiser to know how to translate the object structure into a suitable string, and anyway it's not generally a useful or logical thing to do. So instead it throws an error to say the object structure doesn't match.
The specific error you're seeing means the deserialiser is expecting a " to denote the start of a string but instead it's seeing { denoting the start of another object.
Why is this happening? Well, your PHP code will produce a JSON response which looks like this:
{
"Status": true,
"Message": "SUCCESS",
"ResponseData": {
"dept": {
"id": 2,
"name": "Test",
"description": "Testing",
"status": 1
}
}
}
Live demo here
As you can see, ResponseData contains an object, which has a "dept" which in turn is another object with four more properties.
To deserialise this properly, your SystemJSON class will need to be altered, and you'll also need two sub-classes to help it out:
public class SystemJSON
{
public bool Status { get; set; }
public string Message { get; set; }
public ResponseData ResponseData { get; set; }
}
public class ResponseData {
public Department dept {get; set; }
}
public class Department {
public string id {get; set; }
public string description {get; set; }
public int status {get; set; }
}
You will now be able to deserialise the JSON correctly. Here is a live demo of the deserialisation.
P.S the [ character appears to be irrelevant here...it's unclear why you referred to that in your question.
P.P.S. From looking at your PHP I'm guessing that you may be intending to return different data structures in ResponseData depending on which parameter was specified for do_work - i.e. depending on which PHP function is called. If so then you'll need to amend your C# accordingly so that it deserialises to a different concrete class depending on which API method it requests. Or you could possibly cheat and specify ResponseData as dynamic, which will then accept any data structure it received, albeit with the caveat that it's now effectively loosely-typed and so you lose certain benefits when compiling the code such as checking for valid usage of property names, data types etc.
When I send a request to a service (that I do not own), it may respond either with the JSON data requested, or with an error that looks like this:
{
"error": {
"status": "error message",
"code": "999"
}
}
In both cases the HTTP response code is 200 OK, so I cannot use that to determine whether there is an error or not - I have to deserialize the response to check.
So I have something that looks like this:
bool TryParseResponseToError(string jsonResponse, out Error error)
{
// Check expected error keywords presence
// before try clause to avoid catch performance drawbacks
if (jsonResponse.Contains("error") &&
jsonResponse.Contains("status") &&
jsonResponse.Contains("code"))
{
try
{
error = new JsonSerializer<Error>().DeserializeFromString(jsonResponse);
return true;
}
catch
{
// The JSON response seemed to be an error, but failed to deserialize.
// Or, it may be a successful JSON response: do nothing.
}
}
error = null;
return false;
}
Here, I have an empty catch clause that may be in the standard execution path, which is a bad smell... Well, more than a bad smell: it stinks.
Do you know a better way to "TryParse" the response in order to avoid a catch in the standard execution path ?
[EDIT]
Thanks to Yuval Itzchakov's answer I improved my method like that :
bool TryParseResponse(string jsonResponse, out Error error)
{
// Check expected error keywords presence :
if (!jsonResponse.Contains("error") ||
!jsonResponse.Contains("status") ||
!jsonResponse.Contains("code"))
{
error = null;
return false;
}
// Check json schema :
const string errorJsonSchema =
#"{
'type': 'object',
'properties': {
'error': {'type':'object'},
'status': {'type': 'string'},
'code': {'type': 'string'}
},
'additionalProperties': false
}";
JsonSchema schema = JsonSchema.Parse(errorJsonSchema);
JObject jsonObject = JObject.Parse(jsonResponse);
if (!jsonObject.IsValid(schema))
{
error = null;
return false;
}
// Try to deserialize :
try
{
error = new JsonSerializer<Error>.DeserializeFromString(jsonResponse);
return true;
}
catch
{
// The JSON response seemed to be an error, but failed to deserialize.
// This case should not occur...
error = null;
return false;
}
}
I kept the catch clause... just in case.
#Victor LG's answer using Newtonsoft is close, but it doesn't technically avoid the a catch as the original poster requested. It just moves it elsewhere. Also, though it creates a settings instance to enable catching missing members, those settings aren't passed to the DeserializeObject call so they are actually ignored.
Here's a "catch free" version of his extension method that also includes the missing members flag. The key to avoiding the catch is setting the Error property of the settings object to a lambda which then sets a flag to indicate failure and clears the error so it doesn't cause an exception.
public static bool TryParseJson<T>(this string #this, out T result)
{
bool success = true;
var settings = new JsonSerializerSettings
{
Error = (sender, args) => { success = false; args.ErrorContext.Handled = true; },
MissingMemberHandling = MissingMemberHandling.Error
};
result = JsonConvert.DeserializeObject<T>(#this, settings);
return success;
}
Here's an example to use it:
if(value.TryParseJson(out MyType result))
{
// Do something with result…
}
With Json.NET you can validate your json against a schema:
string schemaJson = #"{
'status': {'type': 'string'},
'error': {'type': 'string'},
'code': {'type': 'string'}
}";
JsonSchema schema = JsonSchema.Parse(schemaJson);
JObject jobj = JObject.Parse(yourJsonHere);
if (jobj.IsValid(schema))
{
// Do stuff
}
And then use that inside a TryParse method.
public static T TryParseJson<T>(this string json, string schema) where T : new()
{
JsonSchema parsedSchema = JsonSchema.Parse(schema);
JObject jObject = JObject.Parse(json);
return jObject.IsValid(parsedSchema) ?
JsonConvert.DeserializeObject<T>(json) : default(T);
}
Then do:
var myType = myJsonString.TryParseJson<AwsomeType>(schema);
Update:
Please note that schema validation is no longer part of the main Newtonsoft.Json package, you'll need to add the Newtonsoft.Json.Schema package.
Update 2:
As noted in the comments, "JSONSchema" have a pricing model, meaning it isn't free. You can find all the information here
A slightly modified version of #Yuval's answer.
static T TryParse<T>(string jsonData) where T : new()
{
JSchemaGenerator generator = new JSchemaGenerator();
JSchema parsedSchema = generator.Generate(typeof(T));
JObject jObject = JObject.Parse(jsonData);
return jObject.IsValid(parsedSchema) ?
JsonConvert.DeserializeObject<T>(jsonData) : default(T);
}
This can be used when you don't have the schema as text readily available for any type.
Just to provide an example of the try/catch approach (it may be useful to somebody).
public static bool TryParseJson<T>(this string obj, out T result)
{
try
{
// Validate missing fields of object
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.MissingMemberHandling = MissingMemberHandling.Error;
result = JsonConvert.DeserializeObject<T>(obj, settings);
return true;
}
catch (Exception)
{
result = default(T);
return false;
}
}
Then, it can be used like this:
var result = default(MyObject);
bool isValidObject = jsonString.TryParseJson<MyObject>(out result);
if(isValidObject)
{
// Do something
}
You may deserialize JSON to a dynamic, and check whether the root element is error. Note that you probably don't have to check for the presence of status and code, like you actually do, unless the server also sends valid non-error responses inside a error node.
Aside that, I don't think you can do better than a try/catch.
What actually stinks is that the server sends an HTTP 200 to indicate an error. try/catch appears simply as checking of inputs.
Add an Error property to your class, or even better use a base class with this error property, like this:
public class BaseResult
{
public Error Error { get; set; }
public bool HasError => String.IsNullOrEmpty(Error?.Code);
}
public class Error
{
public string Status { get; set; }
public string Code { get; set; }
}
Any result class inherits from this base result:
public class MyOkResponseClass : BaseResult
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public int Prop3 { get; set; }
}
Then you can check the property HasError. No exceptions, no extended methods and no weird checks.
To test whether a text is valid JSON regardless of schema, you could also do a check on the number of quotation marks:" in your string response, as shown below :
// Invalid JSON
var responseContent = "asgdg";
// var responseContent = "{ \"ip\" = \"11.161.195.10\" }";
// Valid JSON, uncomment to test these
// var responseContent = "{ \"ip\": \"11.161.195.10\", \"city\": \"York\", \"region\": \"Ontartio\", \"country\": \"IN\", \"loc\": \"-43.7334,79.3329\", \"postal\": \"M1C\", \"org\": \"AS577 Bell Afgh\", \"readme\": \"https://ipinfo.io/missingauth\"}";
// var responseContent = "\"asfasf\"";
// var responseContent = "{}";
int count = 0;
foreach (char c in responseContent)
if (c == '\"') count++; // Escape character needed to display quotation
if (count >= 2 || responseContent == "{}")
{
// Valid Json
try {
JToken parsedJson = JToken.Parse(responseContent);
Console.WriteLine("RESPONSE: Json- " + parsedJson.ToString(Formatting.Indented));
}
catch(Exception ex){
Console.WriteLine("RESPONSE: InvalidJson- " + responseContent);
}
}
else
Console.WriteLine("RESPONSE: InvalidJson- " + responseContent);
I am struggling with a subject that has a lot of variants in this forum but I can't seem to find one that suits me, and I think it's because of the way that my JSON array is :(
I'm not an expert but I already manage to "almost" get the end...
I need to get hand in "Success" and "Status" value. But also the different "Addresses".
My JSON (is called responseFromServer):
{
"success":true,
"addresses":
[
{"DPID":658584,"SourceDesc":"Postal\\Physical","FullAddress":"1/8 Jonas Street, Waimataitai, Timaru 7910"},
{"DPID":658585,"SourceDesc":"Postal\\Physical","FullAddress":"2/8 Jonas Street, Waimataitai, Timaru 7910"},
{"DPID":658583,"SourceDesc":"Postal\\Physical","FullAddress":"3/8 Jonas Street, Waimataitai, Timaru 7910"}
],
"status":"success"
}
Then, based on lot of examples in this forum, taking bits and pieces I created my classes:
public class jsonDataTable
{
public bool success { get; set; }
public IEnumerable<dtaddresses> addresses { get; set; }
public string status { get; set; }
}
public class dtaddresses
{
public int DPID { get; set; }
public string SourceDesc { get; set; }
public string FullAddress { get; set; }
}
Then I'm going to Deserialize:
public void _form_OnCallingAction(object sender, ActionEventArgs e)
{
...
...
JavaScriptSerializer js = new JavaScriptSerializer();
jsonDataTable jsonArray = js.Deserialize<jsonDataTable>(responseFromServer);
...
string tb = jsonArray.status.ToString();
string tb2 = jsonArray.success.ToString();
...
...
List<dtaddresses> _listAddresses = new List<dtaddresses>
{
new dtaddresses()
};
...
...
try
{
string tb3 = _listAddresses.Count.ToString();
string tb4 = _listAddresses[0].FullAddress;
}
catch (Exception ex)
{
CurrentContext.Message.Display(ex.Message + ex.StackTrace);
}
...
...
...
CurrentContext.Message.Display("Raw Response from server is: {0}", responseFromServer);
//Returns all the content in a string to check. OK! :)
CurrentContext.Message.Display("The success value is: {0} ", tb);
//Returns the Status Value (in this case "success") OK! :)
CurrentContext.Message.Display("The status value is: {0} ", tb2);
//Returns the Success Value (in this case "true") giggity giggity! All Right! :)
CurrentContext.Message.Display("The n. of addresses is: {0} ", tb3);
//Returns how many addresses ( in this case is returning 0) not ok... :(
CurrentContext.Message.Display("The address value is: {0} ", tb4);
// Returns the Fulladdress in index 0 (in this case nothing...) not ok... :(
Can any one help me to understand why I can access the values in the "dtaddresses" class?
This is the far that I went...
The following piece of code I copied from your question is creating a brand new list that has nothing to do with your deserialized data. Thus it's always going to be a single element list, where the first element contains only default values, which is what you are seeing in tb3 and tb4 later on.
List<dtaddresses> _listAddresses = new List<dtaddresses>
{
new dtaddresses()
};
Instead, assign jsonArray.addresses to _listAddresses, such as:
List<dtaddresses> _listAddresses = jsonArray.addresses.ToList()
Or you can forget about _listAddresses completely, and just simply reference jsonArray.addresses directly, such as:
string tb3 = jsonArray.addresses.Count().ToString();
string tb4 = jsonArray.addresses.First().FullAddress;