Dynamic JObject - parse invalid JSON - c#

Consider I have following json:
{ "version": "1.0" }
I can parse it to dynamic JObject and use:
dynamic result = JObject.Parse(myJson);
string verison = result.Version; //works <3
But server returns the following json
{ { "version": "1.0" } }
This json is consider as valid by newtonsoft, but cannot access version anymore:
dynamic result = JObject.Parse(myJson);
string verison = result.Version; //error
How to access Version when onlt dynamic result is avalable?

{ { "version": "1.0" } } This json is consider as valid by newtonsoft
That is incorrect, you will not be able to parse this and will receive a exception of type Newtonsoft.Json.JsonReaderException (with: Invalid property identifier character: {. Path '', line 1, position 2.)
Invalid JSON:
{ { "version": "1.0" } }
Valid JSON:
{ "version": "1.0" }
(In case you have server control, I suggest you make the necessary steps on the server to return valid JSON)
However, worst case scenario, you could make this invalid JSON valid by removing the first char { and last char } before parsing it. For example like so:
var myJson = json.Substring(1, json.Length - 2);
dynamic result = JObject.Parse(myJson);
string version = result.version;
Where json here was the original response containing the invalid JSON.
Also note that for the JSON you provided you must use lowercase version as result.version. The dynamic property name must match exactly the one in the JSON

I think you trouble in capital "V" in "Version". Should be "result.version"

Related

Passing JSON as a string in the body of the POST request

I need a small help, because I don't know how to solve the below problem.
The requirement is simple, I have to sent the JSON to the server as a string parameter. The server basing on the key finds the mapping, and generically parses the JSON to some objects. That means, that the payload can have a different values and structures, each key has its own mapping - different data structure, number of parameters and so on. So the payload shouldn't be parsed outside the endpoint logic.
I know, that the Swagger sees the payload as a JSON, not as a string, and it tries to parse the data. How can I send the JSON as a string parameter to the endpooint without parsing the parameter? I have to parse it inside of the application, because of the mentioned mappings.
Example JSON:
{
"key": "test",
"payload": "[{"IDNew":1,"NameNew":"t1","DescriptionNew":"t1d", "IntegerValueNew":1, "DecimalValueNew":123.3}]"
}
When I'm trying to send the data in Swagger, I'm getting the below results:
curl -X POST "http://localhost:5110/api/InboundData" -H "accept: */*" -H "Content-Type: application/json-patch+json" -d "{ \"key\": \"test\", \"payload\": \"[{\"IDNew\":1,\"NameNew\":\"t1\",\"DescriptionNew\":\"t1d\", \"IntegerValueNew\":1, \"DecimalValueNew\":123.3}]\"}"
{
"errors": {
"payload": [
"After parsing a value an unexpected character was encountered: I. Path 'payload', line 3, position 17."
]
},
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "|d952c89f-4e25126d8cdf3697."
}
Data model:
[Required]
[JsonProperty(Required = Required.DisallowNull)]
[MaxLength(100)]
public string Key { get; set; }
[Required]
[JsonProperty(Required = Required.DisallowNull)]
public string Payload { get; set; }
The error clearly suggests that your JSON is not correct. If we analyze the payload property:
{
"key": "test",
"payload": "[{"IDNew":1,"NameNew":"t1","DescriptionNew":"t1d", "IntegerValueNew":1, "DecimalValueNew":123.3}]"
}
It seems you are creating a string object which further contains a JSON as a string. Generally, when you pass an array, you would pass it like this.
{
"key": "test",
"payload": [
{
"IDNew": 1,
"NameNew": "t1",
"DescriptionNew": "t1d",
"IntegerValueNew": 1,
"DecimalValueNew": 123.3
}
]
}
But, since value of the payload property is not properly escaped, which is why it is not properly able to parse it as it has unexpected characters for a string value.
If you strictly want to pass a JSON Array as a string object, you need to properly escape it in order to get it working. For example below is a JSON that contains JSON as a string with properly escaped properties:
{
"key": "test",
"payload": "[{\"IDNew\":1,\"NameNew\":\"t1\",\"DescriptionNew\":\"t1d\", \"IntegerValueNew\":1, \"DecimalValueNew\":123.3}]"
}
This is how you would escape your JSON if you strictly want to pass a JSON object that further contains JSON as string.
Or, perhaps, use single quote (') instead for the nested JSON. For example below is a JSON that contains JSON as a string with a single quotes for the properties:
{
"key": "test",
"payload": "[{'IDNew':1,'NameNew':'t1','DescriptionNew':'t1d', 'IntegerValueNew':1, 'DecimalValueNew':123.3}]"
}
UPDATE
I just wanted to add a suggestion that would be less confusing and would generate an accurate output for the scenario.
It would be nice if you generate the models for your intended JSON string and serialize the model to get a JSON string then do the assignment to payload property.
var payload = new List<payloadSample1>();
payload.Add(new payloadSample1{ IDNew = 1, NameNew = "t1", DescriptionNew = "t1d" });
var payloadStr = JsonConvert.SerializeObject(payload);
// payloadStr would contain your JSON as a string.
In C#, you can also generate dynamic type objects. Use those if your JSON is constantly varying and you find it hectic to create many models for many api requests.
var payload = new List<dynamic>();
payload.Add(new { IDNew = 1, NameNew = "t1", DescriptionNew = "t1d" });
var payloadStr = JsonConvert.SerializeObject(payload);
// And even then, if you have a further JSON object to send:
var payloadParent = new { key = "test", payload = payloadStr };
// send payloadParent as your json.
This is not the cleanest approach because of many reasons one out of those would be, when there is a change in your model, you will have to manually analyze all your dynamic objects and change all the references where you are using it. But, certainly, it will reduce the confusion behind escaping and maintaining the strings.
If you are using JavaScript make the api call, then generate a proper JSON object and then stringify it to get a string.

When deserializing Json string that contains "\" gives Bad Json escape sequence

I have a input field in UI which contains a field Value with "\\test\a\b\c". The Json string in API after serialization has the Value field as below.
{
"ItemKey": "8b603493-3d2d-4903-a054-2abb895ab870",
"ParentKey": "00000000-0000-0000-0000-000000000000",
"Schema": "",
"SchemaTypeName": "",
"Value": "\\\\test\\a\\b\\c",
"ValueDefinition": null
}
When deserializing this string I get the error,
var result = JsonConvert.DeserializeObject<Class>(rootElement);
{"Bad JSON escape sequence: \\M. Path 'sample[0].sample[1].Value', line 1, position 3384."}
I tried double escaping "\" and this error is resolved.
var tempStr = valueReplacement.Replace(#"\\", #"\").Replace(#"//", #"/");
valueReplacement = tempStr.Replace(#"\", #"\\").Replace(#"/", #"//");
{"Value": "\\test\\a\\b\\c"}
But I need the final json that is generated after deserializing to have the value still the same because I need to integrate this with UI.
"Value": "\\test\a\b\c"
Is there any way I can change the value again after deserialization?

Having error upon trying to parse a JSON array into JSON object

I am trying to parse a JSON array by using this code:
public async Task QuoteAsync()
{
await this.Context.Channel.TriggerTypingAsync();
var client = new HttpClient();
var result = await client.GetStringAsync("https://zenquotes.io/api/random");
JArray qarray = JArray.Parse(result);
JObject quote = JObject.Parse(qarray[0]["q"].ToString());
JObject author = JObject.Parse(qarray[0]["a"].ToString());
await this.ReplyAsync($"{quote} - {author}");
}
The response I receive upon sending a request to zenquotes api is
[
{
"q": "Be where your enemy is not.",
"a": "Sun Tzu",
"h": "<blockquote>“Be where your enemy is not.” — <footer>Sun Tzu</footer></blockquote>"
}
]
I can't seem to figure out why the error is occurring since I don't really see any issues.
This is the error I am getting:
Unexpected character encountered while parsing value: A. Path '', line 0, position 0.
The error is occurring on the 7th line:
JObject quote = JObject.Parse(qarray[0]["q"].ToString());
and since the 8th line is the same I expect the same error.
According to JObject.Parse,
Load a JObject from a string that contains JSON.
For your "q" and "a", it is just a simple string but not a JSON string. Hence you should not use JObject.Parse.
With .ToString(), it is good enough to retrieve the value of q and a.
string quote = qarray[0]["q"].ToString();
string author = qarray[0]["a"].ToString();
Sample program

Can't parse json from Resources

In resources I have json file with next content:
{
"EU": [
"Germany",
"Ukraine",
"United Kingdom",
"Hungary"
]
}
I want to deserialize it into Dictionary<string,List<string>>
I've tried next :
var json = Encoding.UTF8.GetString(Resources.regionGroups);//Resources.regionGroups return byte[]
return JsonConvert.DeserializeObject<Dictionary<string, List<string>>>(json);
But every time I get exception as variable json is in incorect json format.
What can cause this? I've tried the same deserialization but with jsonString as hard-coded and it works.
Detailed exception message :
Unexpected character encountered while parsing value: . Path '', line
0, position 0.
UPDATE :
After removing all spaces
var json = Regex.Replace(Encoding.UTF8.GetString(Resources.regionGroups), "(\"(?:[^\"\\\\]|\\\\.)*\")|\\s+", "$1");
from string I have next one
"{\"EU\":[\"Germany\",\"Ukraine\",\"United Kingdom\",\"Hungary\"]}"
which also reproduce exception.
Well #AmitKumarGhosh was right about encoding, as I think.
So I've tried to change type of my json file in the Resources. I've changed it from binary to text file and this helps.
So parsing now is very simple :
JsonConvert.DeserializeObject<Dictionary<string, List<string>>>(Resources.regionGroups);

Why is this Json.Net conversion from XML and JSON not working?

You can enter either of these URLs into a browser and verify that they return valid xml:
http://maps.googleapis.com/maps/api/geocode/xml?`address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&sensor=false"
...or json:
http://maps.googleapis.com/maps/api/geocode/json?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&sensor=false%22
...but when attempting to get the xml programmatically using this code (after installing Json.NET via NuGet into my project and dropping a dataGridView on my Windows form):
dataGridView1.DataSource = GetLocationData("http://maps.googleapis.com/maps/api/geocode/xml?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&sensor=false");
private JArray GetLocationData(string uri)
{
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
var webResponse = (HttpWebResponse)webRequest.GetResponse();
var reader = new StreamReader(webResponse.GetResponseStream());
string s = reader.ReadToEnd();
return JsonConvert.DeserializeObject<JArray>(s);
}
...I get:
Newtonsoft.Json.JsonReaderException was unhandled
_message=Unexpected character encountered while parsing value: <. Path '', line 0, position 0.
I thought, okay, this actually is json code, not xml, so I replaced "xml" with "json" in the URL, expecting more joy in Mudville.
However, when attempting to get the same data as json, I also get an exception, namely, "System.InvalidCastException was unhandled _message=Unable to cast object of type 'Newtonsoft.Json.Linq.JObject' to type 'Newtonsoft.Json.Linq.JArray'."
Why, and how can I fix it? Is it possible to also grab the data as xml?
Using the json URL is correct. Your problem is that the JSON response is not giving you an array which is your error.
Notice that the response you get back is an object denoted by the {
{
"results" : [
{
"address_components" : [
{
"long_name" : "1600",
"short_name" : "1600",
"types" : [ "street_number" ]
},
....
If it was an array, you would get back [ instead. You should use JObject or a custom model instead of JArray. The results element is what contains the actual array that you will need to loop through.
Pseudo for the result you are actually getting in the response:
object
{
Result[] results;
string status;
}
You can also grab the XML version if you want, but then you need to actually have an object model defined to match and use the XmlSerializer to deserialize it or load it into an XmlDocument (lots of ways to work with XML). However, you cannot pass XML into the JSON serializer as it is expecting a JSON string.
EDIT:
There are methods on JsonConvert to convert from XML. See this answer here: How to convert JSON to XML or XML to JSON?

Categories

Resources