Deserialize dynamic json into generic .NET object - c#

What is the best way to deserialize the following JSON response into a generic object? For example I would like to access the response message and a the list of errors (and accessing the field name and error message of it).
{
"message": "The given data was invalid.",
"errors": {
"name": [
"Name is required."
],
"gender": [
"Gender is required."
],
"date_of_birth": [
"Date of birth is required."
]
}
}
Edit:
I would like to access the JSON object in a way something like this
string message = genericObject.message
foreach (error errorElement in genericObject.errors)
{
string errorField = errorElement.fieldName;
string errorDescription = errorElement.errorMessage;
}
Edit 2:
I don't know the possible error fields beforehand.

Since you mention that you do not know which "error fields" will be present, a dictionary is the best way to go.
Here's a simple example:
void Main()
{
string json = File.ReadAllText(#"d:\temp\test.json");
var response = JsonConvert.DeserializeObject<Response>(json);
response.Dump();
}
public class Response
{
public string Message { get; set; }
public Dictionary<string, List<string>> Errors { get; }
= new Dictionary<string, List<string>>();
}
When executing this in LINQPad, I get this output:
You can even add your own code from your question:
string json = File.ReadAllText(#"d:\temp\test.json");
var genericObject = JsonConvert.DeserializeObject<Response>(json);
string message = genericObject.Message;
foreach (var errorElement in genericObject.Errors) // see note about var below
{
string errorField = errorElement.Key;
string errorDescription = errorElement.Value.FirstOrDefault(); // see below
}
Note 1: The result of iterating a dictionary is a KeyValuePair<TKey, TValue>, in this case it would be a KeyValuePair<string, List<string>>.
Note 2: You've shown JSON having an array for each field, so errorElement.errorMessage isn't going to work properly since you may have multiple error messages.
You can nest some loops, however, to process them all:
string message = genericObject.Message;
foreach (var errorElement in genericObject.Errors) // see note about var below
{
string errorField = errorElement.Key;
foreach (string errorDescription in errorElement.Value)
{
// process errorField + errorDescription here
}
}

There are many ways to do this.
The System.Web.Helpers assembly contains the Json class which you can do:
dynamic decodedObject = Json.Decode(json);
Another way would be to use the Newtonsoft.Json nuget package:
var deserializedObject = JsonConvert.DeserializeObject<dynamic>(json);

If you are willing to use Newtonsoft.Json you can use:
var json = JsonConvert.DeserializeObject<dynamic>(originalJson);

you would have to create the following classes:
RootObject.cs containing the following properties:
public class RootObject
{
[JsonProperty("message")]
public string Message { get; set; }
[JsonProperty("errors")]
public Errors Errors { get; set; }
}
Errors.cs, containing the following properties:
public class Errors
{
[JsonProperty("name")]
public string[] Name { get; set; }
[JsonProperty("gender")]
public string[] Gender { get; set; }
[JsonProperty("date_of_birth")]
public string[] DateOfBirth { get; set; }
}
And then you read the whole thing like this:
var inputObj = JsonConvert.DeserializeObject<RootObject>(json);
Where inputObj will be of type RootObject and json is the JSON you are receiving.
If you have implemented this correctly, use it like this:
var message = inputObj.Message;
var nameErrors = inputObj.Errors;
var firstNameError = inputObj.Errors.Name[0];
Here is a visual:
Showing the whole object, filled with the properties:
The "main" error:
If you have any questions feel free to ask.

Related

How to Deserialize Json Array

I have a Json Response to deserialize, after doing the deserialize I get error.
This is the Json array I want to deserialize:
{
"status": 200,
"success": true,
"message": "Success",
"data": [
{
"transaction_reference": "REF202301031128311645_1",
"virtual_account_number": "1234567890",
"principal_amount": "200.00",
"settled_amount": "199.80",
"fee_charged": "0.20",
"transaction_date": "2023-01-03T00:00:00.000Z",
"transaction_indicator": "C",
"remarks": "Transfer FROM Transfer | [1234567] TO Merchant Name",
"currency": "NGN",
"alerted_merchant": false,
"merchant_settlement_date": "2023-01-03T10:29:25.847Z",
"frozen_transaction": null,
"customer": {
"customer_identifier": "1234567"
}
}
]
}
From the Array, I want to get the following as variable:
status,
transaction_reference,
Virtual_account_number,
principal_amount,
customer_identifier,
below is what I tried:
string data = response.Content;
string dataT = data.Replace("", string.Empty);
dynamic stuff = JObject.Parse(dataT);
dynamic status = stuff.status;
var JsonResponse = stuff.data;
var ResponseX = JsonNode.Parse(JsonResponse); // Deserializing json Array
dynamic transaction_reference = ResponseX[0]["transaction_reference"];
dynamic virtual_account_number = ResponseX[1]["virtual_account_number"];
dynamic principal_amount = ResponseX[2]["principal_amount"];
dynamic customer = ResponseX[13]["customer_identifier"];
dynamic customer_identifier = customer.customer_identifier;
The error I got is as below
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException
HResult=0x80131500 Message=The best overloaded method match for
'System.Text.Json.Nodes.JsonNode.Parse(ref
System.Text.Json.Utf8JsonReader,
System.Text.Json.Nodes.JsonNodeOptions?)' has some invalid arguments
Source= StackTrace:
The error occurred at the line
var ResponseX = JsonNode.Parse(JsonResponse); // Deserializing json Array
What I really want achieve is to separate the following and get each as variable:
status,
transaction_reference,
Virtual_account_number,
principal_amount,
customer_identifier,
Please can someone point to me where my error is?
you have too much code that you don't need, if you don't want to deserialize to c# class, you can do it this way too
var ResponseX = JsonNode.Parse(response.Content); // Parse the response string
int status= (int)ResponseX["status"];
var data=ResponseX["data"][0];
string transaction_reference = (string) data["transaction_reference"];
string virtual_account_number = (string) data["virtual_account_number"];
string principal_amount = (string) data["principal_amount"];
string customer = (string) data["customer_identifier"];
int customer_identifier = Convert.ToInt32 ((string) data["customer"]["customer_identifier"]);
another way is to deserialize data to c# class
Datum d = System.Text.Json.JsonSerializer.Deserialize<Datum>(data.AsObject());
string transaction_reference = d.transaction_reference;
string virtual_account_number = d.virtual_account_number;
int customer_identifier = Convert.ToInt32( d.customer.customer_identifier);
public class Datum
{
public string transaction_reference { get; set; }
public string virtual_account_number { get; set; }
public string principal_amount { get; set; }
public string settled_amount { get; set; }
public string fee_charged { get; set; }
public DateTime transaction_date { get; set; }
public string transaction_indicator { get; set; }
public string remarks { get; set; }
public string currency { get; set; }
public bool alerted_merchant { get; set; }
public DateTime merchant_settlement_date { get; set; }
public object frozen_transaction { get; set; }
public Customer customer { get; set; }
}
public class Customer
{
public string customer_identifier { get; set; }
}
It would be nice to know what the specific error is but from inspecting the JSON it looks like you are stepping into the wrong parent node.
Try
dynamic transaction_reference = ResponseX["data"][0]["transaction_reference"];
dynamic virtual_account_number = ResponseX["data"][1]["virtual_account_number"];
dynamic principal_amount = ResponseX["data"][2]["principal_amount"];
dynamic customer = ResponseX["data"][13]["customer_identifier"];
dynamic customer_identifier = customer.customer_identifier;
Also, make sure the array has at least 14 entries under the parent data.
You are passing an invalid into JsonNode.Parse().
The error is thrown at runtime because you use dynamic as type and the compiler does not know the type at compile time.
In line dynamic stuff = JObject.Parse(dataT); you are using Newtonsoft to parse the data.
In Line var JsonResponse = stuff.data; you are writing a variable of type Newtonsoft.Json.Linq.JArray.
Then you try to call JsonNode.Parse(JsonResponse); which is not possible with the given argument, because the type of JsonResponse is not valid.
You will get this error on compile time, when you use the "real" types instead of dynamic.
To fix your issue, you should use the same function for parsing.

Serializing data in to fields in JSON file [duplicate]

This question already has answers here:
How to write a JSON file in C#?
(4 answers)
Closed 2 years ago.
I have a JSON File as below ,
{
"student": {
"fullName": "No Name",
"id": 0001
},
"message": "Lets see if this is displayed!"
}
I want to write some logic in my WPF application where when someone clicks the submit button after filling in the corresoonding text fields those values will be serialized into this JSON.
In the sense for example if someone enters Dwayne as the name , 2222 as the ID and "Hello" as the message I want the JSON file to look as
{
"student": {
"fullName": "Dwayne",
"id": 2222
},
"message": "Hello"
}
Here is what I have implmented thus far in my code behind
I have the StudentData.cs class
public class StudentData
{
public StudentData()
{
}
public string Name { get; set; }
public string ID { get; set; }
public string Message { get; set; }
}
and in the xaml.cs where the save is executed I get the necessary data and save it as an object
string fullName = textName.Text;
string ID = textId.Text;
string message = textMessage.Text;
StudentData stuData = new StudentData();
stuData .Name = fullName;
stuData .ID = ID;
stuData .Message = message;
Can anyone help on what I can do next ?
You should use a JSON serialisation package, I prefer Newtonsoft.
https://www.newtonsoft.com/json
You would then serialise your data into JSON and write it like this:
List<StudentData> myStudentData = new List<StudentData>();
//Add data
string json = JsonConvert.SerializeObject(myStudentData);
File.WriteAllText("c:\\path\\to\\yourfile.json", json );
This will overwrite the file you specify with the new data.
BUT:
Your data structure is not correct to achieve the JSON you wish to output. So you either need to change your expectation of the output of the data, or would need to change your classes.
With the JSON example you have then message is only shown once for the whole output. If you want this then you need to use the following classes.
public class Student
{
public string fullName { get; set; }
public int id { get; set; }
}
public class RootObject
{
public List<Student> student { get; set; }
public string message { get; set; }
}
Then you will of course have to change your creation and output of objects like this;
RootObject root = new RootObject();
root.message = "Hello";
List<StudentData> myStudentData = new List<StudentData>();
//add data to student
root.student = myStudentData;
string json = JsonConvert.SerializeObject(root);
File.WriteAllText("c:\\path\\to\\yourfile.json", json );
If you wish for the message to be per student, then you keep your current class structure and you will get the following JSON output.
{
"student": {
"fullName": "Dwayne",
"id": 2222,
"message": "Hello"
}
}
You can use Newtonsoft.json library to convert your object to json.
var student = JsonConvert.SerializeObject(StudentData);
newtonsoft.json

Json deserialize returning null

Here is the JSON response
{
"result": [
{
"sys_id": "85071a1347c12200e0ef563dbb9a71c1",
"number": "INC0020001",
"description": ""
}
]
}
Here is my JSON class
public class Result
{
public string sys_id { get; set; }
public string number { get; set; }
public string description { get; set; }
}
public class jsonResult
{
public IList<Result> result { get; set; }
}
Here is what I am doing to deserialize
strReponse = rClient.makeReqest();
Result deserializedProduct = JsonConvert.DeserializeObject<Result>(strReponse);
System.Windows.Forms.MessageBox.Show(deserializedProduct.number);
It looks like it never assigns anything into my JSON class.
This is my first time dealing with JSON and Web calls. What am i missing? The API call does return the correct JSON, and I used json2csharp to make my json class.
Thank you!
You need deserialize full object represented in json string. Which is jsonResult in your case.
After, you will get access to values, you need, through properties of jsonResult
strReponse = rClient.makeReqest();
var deserializedResult = JsonConvert.DeserializeObject<jsonResult>(strReponse);
var number = deserializedResult.result.First().number;
MessageBox.Show(number);
Because jsonResult.result is of type IList will be safer to loop through all possible results
strReponse = rClient.makeReqest();
var deserializedResult = JsonConvert.DeserializeObject<jsonResult>(strReponse);
foreach (var result in deserializedResult.result)
{
MessageBox.Show(result.number);
}
You should deserialize to jsonResult NOT Result.
So try this :
jsonResult deserializedProduct = JsonConvert.DeserializeObject<jsonResult>(strReponse);
Also you may get values of the list like this:
var firstResult = deserializedProduct.result.FirstOrDefault();
var someSpecialResults = deserializedProduct.result.Where(r=>r.number.Contains("123"));
Also :
if (firstResult != null)
System.Windows.Forms.MessageBox.Show(firstResult .number);
Also you may iterate them like this :
deserializedProduct.result.ForEach(r=> System.Windows.Forms.MessageBox.Show(r.number);)
I hope to be helpful for you:)

Parsing Json facebook c#

I am trying for many hours to parse a JsonArray, I have got by graph.facebook, so that i can extra values. The values I want to extract are message and ID.
Getting the JasonArry is no Problem and works fine:
[
{
"code":200,
"headers":[{"name":"Access-Control-Allow-Origin","value":"*"}],
"body":"{
\"id\":\"255572697884115_1\",
\"from\":{
\"name\":\"xyzk\",
\"id\":\"59788447049\"},
\"message\":\"This is the first message\",
\"created_time\":\"2011-11-04T21:32:50+0000\"}"},
{
"code":200,
"headers":[{"name":"Access-Control-Allow-Origin","value":"*"}],
"body":"{
\"id\":\"255572697884115_2\",
\"from\":{
\"name\":\"xyzk\",
\"id\":\"59788447049\"},
\"message\":\"This is the second message\",
\"created_time\":\"2012-01-03T21:05:59+0000\"}"}
]
Now I have tried several methods to get access to message, but every method ends in catch... and throws an exception.
For example:
var serializer = new JavaScriptSerializer();
var result = serializer.Deserialize<dynamic>(json);
foreach (var item in result)
{
Console.WriteLine(item.body.message);
}
throws the exception: System.Collections.Generic.Dictionary doesnt contain definitions for body. Nevertheless you see in the screenshot below, that body contains definitions.
Becaus I am not allowed to post pictures you can find it on directupload: http://s7.directupload.net/images/120907/zh5xyy2k.png
I don't havent more ideas so i please you to help me. I need this for a project, private, not commercial.
Maybe you could give me an phrase of code, so i can continue my development.
Thank you so far
Dominic
If you use Json.Net, All you have to do is
replacing
var serializer = new JavaScriptSerializer();
var result = serializer.Deserialize<dynamic>(json);
with
dynamic result = JsonConvert.DeserializeObject(json);
that's all.
You are not deserializing to a strongly typed object so it's normal that the applications throws an exception. In other words, the deserializer won't create an Anynymous class for you.
Your string is actually deserialized to 2 objects, each containing Dictionary<string,object> elements. So what you need to do is this:
var serializer = new JavaScriptSerializer();
var result = serializer.Deserialize<dynamic>(s);
foreach(var item in result)
{
Console.WriteLine(item["body"]["message"]);
}
Here's a complete sample code:
void Main()
{
string json = #"[
{
""code"":200,
""headers"":[{""name"":""Access-Control-Allow-Origin"",""value"":""*""}],
""body"":{
""id"":""255572697884115_1"",
""from"":{
""name"":""xyzk"",
""id"":""59788447049""},
""message"":""This is the first message"",
""created_time"":""2011-11-04T21:32:50+0000""}},
{
""code"":200,
""headers"":[{""name"":""Access-Control-Allow-Origin"",""value"":""*""}],
""body"":{
""id"":""255572697884115_2"",
""from"":{
""name"":""xyzk"",
""id"":""59788447049""},
""message"":""This is the second message"",
""created_time"":""2012-01-03T21:05:59+0000""}}
]";
var serializer = new JavaScriptSerializer();
var result = serializer.Deserialize<dynamic>(json);
foreach(var item in result)
{
Console.WriteLine(item["body"]["message"]);
}
}
Prints:
This is the first message
This is the second message
I am using this simple technique
var responseTextFacebook =
#"{
"id":"100000891948867",
"name":"Nishant Sharma",
"first_name":"Nishant",
"last_name":"Sharma",
"link":"https:\/\/www.facebook.com\/profile.php?id=100000891948867",
"gender":"male",
"email":"nihantanu2010\u0040gmail.com",
"timezone":5.5,
"locale":"en_US",
"verified":true,
"updated_time":"2013-06-10T07:56:39+0000"
}"
I have declared a class
public class RootObject
{
public string id { get; set; }
public string name { get; set; }
public string first_name { get; set; }
public string last_name { get; set; }
public string link { get; set; }
public string gender { get; set; }
public string email { get; set; }
public double timezone { get; set; }
public string locale { get; set; }
public bool verified { get; set; }
public string updated_time { get; set; }
}
Now I am deserializing
JavaScriptSerializer objJavaScriptSerializer = new JavaScriptSerializer();
RootObject parsedData = objJavaScriptSerializer.Deserialize<RootObject>(responseTextFacebook );

ServiceStack.Text json deserialization creates wrong object instead of throwing on invalid json input string

When I try to deserialise this invalid json string ( }] missing in the end) :
[{"ExtId":"2","Name":"VIP sj�lland","Mobiles":["4533333333","4544444444"]
By doing this:
var result = JsonSerializer.DeserializeFromString<T>(str);
The ServiceStack json deserializer accepts the string, but it creates a wrong object, because I end up with a C# object having these values:
ExtId : "2" // ok fine.
Name: "VIP sj�lland" // ok fine
Mobiles: ["4533333333","4544444444", "544444444"]// Aarg! An array with 3 objects ?!?
// There were only two in the JSON string.
In this case it would be much better to have an exception thrown instead of continuing with bad data. Therefore I tried using:
JsConfig.ThrowOnDeserializationError = true;
just before calling DeserializeFromString but no exception was thrown. In January I asked this question Configure ServiceStack.Text to throw on invalid JSON and the answer was that ServiceStack is favoring resilence and that I could make a pull request in GitHub.
Is this still the case? And have anyone done it already, saving me the trouble? Otherwise, I am on a very tight schedule, so if anyone has some code or suggestions for how to create an option-flag for making ServiceStack throw on deserialization errors, please reply here, so that I can get this done faster.
This is resolved in ServiceStack.Text v4+ which by default doesn't populate incomplete collections, e.g:
public class Poco
{
public string ExtId { get; set; }
public string Name { get; set; }
public string[] Mobiles { get; set; }
}
var json = "[{\"ExtId\":\"2\",\"Name\":\"VIP sj�lland\",\"Mobiles\":[\"4533333333\",\"4544444444\"]";
var dto = json.FromJson<Poco[]>();
Assert.That(dto[0].ExtId, Is.EqualTo("2"));
Assert.That(dto[0].Name, Is.EqualTo("VIP sj�lland"));
Assert.That(dto[0].Mobiles, Is.Null);
Or if preferred can throw on Error:
JsConfig.ThrowOnDeserializationError = true;
Assert.Throws<SerializationException>(() =>
json.FromJson<Poco[]>());
C# is a little bit picky when it comes to JSON. Following would be valid! Note i do not have anonymous object array as the default element.
{
"ExtItem": [
{
"ExtId": "2",
"Name": "VIPsj�lland",
"Mobiles": [
"4533333333",
"4544444444"
]
}
]
}
If i generate a POCO from this I get
public class Rootobject
{
public Extitem[] ExtItem { get; set; }
}
public class Extitem
{
public string ExtId { get; set; }
public string Name { get; set; }
public string[] Mobiles { get; set; }
}
I personally use extension method to string
public static class Extensions
{
public static bool DeserializeJson<T>(this String str, out T item)
{
item = default(T);
try
{
item = new JavaScriptSerializer().Deserialize<T>(str);
return true;
}
catch (Exception ex)
{
return false;
}
}
}
This would enable me to write:
Rootobject ext;
const string validJson = #"
{
""ExtItem"": [
{
""ExtId"":""2"",
""Name"":""VIPsj�lland"",
""Mobiles"":[
""4533333333"",
""4544444444""
]
}
]
}";
if (validJson.DeserializeJson(out ext))
{ //valid input
// following would print 2 elements : 4533333333, 4544444444
Console.WriteLine(string.Join(", ", ext.ExtItem.First().Mobiles));
} //invalid input
I tried this an have the exception thrown when I missed the } at the end.
In C#, the format for JSON is {"name", "value"} not [{"name", "value"}].
class M
{
public string ExtId { get; set; }
public string Name { get; set; }
public List<string> Mobiles { get; set; }
}
string str = "{\"ExtId\":\"2\",\"Name\":\"VIP\",\"Mobiles\":[\"4533333333\",\"4544444444\"]";
M m = JsonConvert.DeserializeObject<M>(str);
When run this, you will get error as a } is missing.
Using:
string str = "{\"ExtId\":\"2\",\"Name\":\"VIP\",\"Mobiles\":[\"4533333333\",\"4544444444\"]}";
The object is deserialized fine.
You can see the JSON of this object from:
string s = JsonConvert.SerializeObject(m);
I just use the Newtonsoft.Json T JsonConvert.DeserializeObject<T>(string value) and it throws an exception;
If I use the object JsonConvert.DeserializeObject(string value) this one it creates the correct object by simply placing the missing }]
I found that this is a very reliable and fast library.

Categories

Resources