How to write generic JSON deserializer method? - c#

I'm trying to write a generic method which will deserialize JSON responses. In the end, I am getting an error:
Newtonsoft.Json.JsonSerializationException: 'Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.IEnumerable`1[ClientAddressWFA.Models.Address]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
Here is the code of the method below 👇
public static async Task<IEnumerable<T>> JsonDeserialize<T>(string uri)
{
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(uri);
var content = await response.Content.ReadAsStreamAsync();
StreamReader reader = new StreamReader(content);
JsonReader jReader = new JsonTextReader(reader);
JsonSerializer jSer = new JsonSerializer();
return jSer.Deserialize<IEnumerable<T>>(jReader);
}
This is the schema of the API response:
And this is the model. I'm using IEnumerable collection of type Address model to store desirialized data:
public class Address
{
[JsonProperty("id")]
public int Id;
[JsonProperty("streetAddress")]
public int StreetAddress;
[JsonProperty("streetName")]
public string StreetName;
[JsonProperty("buildingNumber")]
public int BuildingNumber;
[JsonProperty("additionalBuildingNumber")]
public string AdditionalBuildingNumber;
[JsonProperty("fullAddress")]
public string FullAddress;
[JsonProperty("zipCode")]
public int ZipCode;
[JsonProperty("coordinateX")]
public double CoordinateX;
[JsonProperty("coordinateY")]
public double CoordinateY;
[JsonProperty("sozraum")]
public int Sozraum;
[JsonProperty("sozialraum")]
public int Sozialraum;
[JsonProperty("lebraum")]
public int Lebraum;
[JsonProperty("lebensraum")]
public int Lebensraum;
[JsonProperty("stimmbezirk")]
public int Stimmbezirk;
[JsonProperty("hnr_nur_fb62")]
public string HnrNurFb62;
[JsonProperty("gerade")]
public string Gerade;
[JsonProperty("district")]
public string District;
[JsonProperty("districtNumber")]
public string DistrictNumber;
[JsonProperty("wkb_geometry")]
public string WkbGeometry;
[JsonProperty("isMain")]
public bool IsMain;
}

The JSON response you're getting has the addresses array nested within a higher-level object. The simplest solution would be to create that high-level response object:
public class Response<T> {
public List<T> Data {get; set;}
public bool Success {get;set;}
public string Message {get;set;}
}
You can then deserialize the Response object in your method and return just the data property from it:
public static async Task<IEnumerable<T>> JsonDeserialize<T>(string uri) {
//...all the code up until deserialization
return jSer.Deserialize<Response<T>>(jReader).Data;
}

Related

Serialize and deserialize an IEnumerable in Session

I have this method:
public static class SessionExtension
{
public static void SetObjectAsJson(this ISession session, string key, object value)
{
session.SetString(key, JsonConvert.SerializeObject(value));
}
public static T GetObjectFromJson<T>(this ISession session, string key)
{
var value = session.GetString(key);
return value == null ? default(T) : JsonConvert.DeserializeObject<T>(value);
}
}
Which is useful to serialize my list of IEnumerable:
public IEnumerable<FBudget> RecordsList { get; set; }
So after filtering the data I serialize the object:
//BUILD FILTER
static Expression<Func<FBudget, bool>> BuildFilter(BudgetViewModel budget)
{
...
}
/*STORE THE ACTUAL FILTER IN SESSION*/
SessionExtension.SetObjectAsJson(HttpContext.Session, "SessionFilter", budget);
An the deserialize it:
public IActionResult Index()
{
var json = SessionExtension.GetObjectFromJson<IEnumerable<FBudget>>(HttpContext.Session, "SessionFilter");
BudgetVM = new BudgetViewModel()
{
FBudget = new FBudget(),
...
RecordsList = json
};
return View(BudgetVM);
}
But when I try to deserialize it, the compiler give the the following error:
Newtonsoft.Json.JsonSerializationException: 'Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.IEnumerable`1[...]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
Path 'FBudget', line 1, position 11.'
Also I'm trying to do this in order to keep in session the result of the BuildFilter() method, so when the user return to a previous page the filters are saved.
Is the correct approach? What am I doing wrong?
This is the Json which is sendend to the method GetObjectFromJson:
{"BudgetId":0,"Year":0,"FreeOfCharge":null,"Currency":null,"UnitPrice":0.0,"MonthNr":null,"UnitOfMeasure":null,"Quantity":0,"TotalAmount":0.0,"LastUser":null,"ProgramId":0,"LastUpdate":"2021-11-03T15:08:15.65645+01:00","ItemMasterId":0,"ItemMaster":{"ItemMasterId":0,"ItemNumber":0,"ShortItem":null,"ItemDescription":null,"UnitOfMeasure":null,"FBudgets":null,"PharmaFormId":0,"PharmaForm":null,"ProductGroupId":0,"ProductGroup":null,"UnToBulkId":0,"UnToBulk":null,"UnToKgId":0,"UnToKg":null},"CompanyId":2,"Company":null,"LedgerTypeId":0,"LedgerType":null,"CustomerId":0,"Customer":{"CustomerId":0,"CustomerName":null,"CustomerGroupCode":null,"CountryCode":null,"CustomerGroupName":null,"LicensingArea":null,"FBudgets":null}}
Since your mentioned json is an object not an array, for deserialize try the below code:
public IActionResult Index()
{
var json = SessionExtension.GetObjectFromJson<FBudget>(HttpContext.Session, "SessionFilter");
BudgetVM = new BudgetViewModel()
{
FBudget = new FBudget(),
...
RecordsList = new List<FBudget> { json }
};
return View(BudgetVM);
}

Saving json array to c# class object

I have to assign json array of string to a c# class. I have done like below. But I got an exceptio
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'LoggedInUserDetails' because the type requires a JSON object (e.g. {\"name\":\"value\"}) to deserialize correctly.\r\nTo fix this error either change the JSON to a JSON object (e.g. {\"name\":\"value\"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.\r\nPath '', line 1, position 1."}
public class LoggedInUserDetails
{
public string login_user_name
{
get; set;
}
public string login_user_id
{
get; set;
}
}
var GetResponse = await response2.Content.ReadAsStringAsync();
//"[{\"login_user_name\":\"Rahul\",\"login_user_id\":\"43\"}]"
LoggedInUserDetails obj = JsonConvert.DeserializeObject<LoggedInUserDetails>(GetResponse);
Variables.login_user_name = obj.login_user_name;
Variables.login_user_id= obj.login_user_id;
You should deserialize the json string to an IEnumerable collection or Array.
var content = await response2.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<List<LoggedInUserDetails>>(content);
It because of the '[]', which stores it as a array of results. You should be able to do something like the following:
LoggedInUserDetails obj = JsonConvert.DeserializeObject<List<LoggedInUserDetails>>(GetResponse);
where you try to deserialize object as an list. The list will only contain 1 object.
full example:
public class LoggedInUserDetails
{
public string login_user_name
{
get; set;
}
public string login_user_id
{
get; set;
}
}
var GetResponse = await response2.Content.ReadAsStringAsync();
//"[{\"login_user_name\":\"Rahul\",\"login_user_id\":\"43\"}]"
List<LoggedInUserDetails> obj = JsonConvert.DeserializeObject<List<LoggedInUserDetails>>(GetResponse);
Variables.login_user_name = obj[0].login_user_name;
Variables.login_user_id= obj[0].login_user_id;
This way:
LoggedInUserDetails obj = JsonConvert.DeserializeObject<LoggedInUserDetails[]>(GetResponse).FirstOrDefault() ?? throw new Exception("User not found.");
Variables.login_user_name = obj.login_user_name;
Variables.login_user_id = obj.login_user_id;
Your result was an array containing 1 object, so you just had to convert it to an array of your desired object and take the first.
Since you are using array, then it should deserialize array to list
public class Test
{
public string login_user_name {get; set;}
public string login_user_id { get; set;}
}
string js = "[{\"login_user_name\":\"Rahul\",\"login_user_id\":\"43\"}]";
var result = JsonConvert.DeserializeObject<List<Test>>(js);
foreach(var ob in result)
{
Console.WriteLine(ob.login_user_name);
Console.WriteLine(ob.login_user_id);
}
output
Rahul
43

How to create C# classes to deserialize a JSON string which starts and ends with square brackets

I am receiving a JSON string back from an API and want to deserialize it into C# objects but cannot get the classes correct.
I have tried creating the classes using http://json2csharp.com/ but it can't parse the JSON, however https://jsonlint.com/ says that the JSON is valid.
I also tried running JsonClassGeneratorLib but that says
Unable to cast object of type Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject
It seems to be an issue because the JSON is enclosed in [] square brackets. I believe this is valid but makes it into an array. I think I need an array somewhere in my class.
string Json = #"[""error"",{""code"":2,""msg"":""This API Key is invalid""}]";
var obj = JsonConvert.DeserializeObject<RootObject>(Json);
public class CodeMsg
{
[JsonProperty("code")]
public long Code { get; set; }
[JsonProperty("msg")]
public string Msg { get; set; }
}
public class Result
{
[JsonProperty("error")]
public string String { get; set; }
public CodeMsg cm { get; set; }
}
public class RootObject
{
public Result Result { get; set; }
}
I always get the error
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'ConsoleApp1.RootObject' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List<T> that can be deserialized from a JSON array
Try this:
var obj = JsonConvert.DeserializeObject<List<Result>>(Json);
Explanation: If the JSON is an Array, the C# RootObject has to be either deriving from List/IEnumerable itself, or you deserialize it to a List/Array of the Type.
You can dump your RootObject class. If you wanted to use the RootObject type, make it derive from List. But this is not worth the hassle.
Your JSON is a heterogeneous array containing a string and an object. This will not deserialize cleanly into a strongly-typed class structure without a little help. One possible solution is to use a custom JsonConverter like this:
public class ResultConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Result);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray array = JArray.Load(reader);
Result result = new Result
{
String = array[0].Value<string>(),
cm = array[1].ToObject<CodeMsg>(serializer)
};
return result;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then tie the converter to your Result class with a [JsonConverter] attribute like this:
[JsonConverter(typeof(ResultConverter))]
public class Result
{
public string String { get; set; }
public CodeMsg cm { get; set; }
}
Finally, deserialize the JSON into the Result class like this:
var result = JsonConvert.DeserializeObject<Result>(Json);
Working demo: https://dotnetfiddle.net/RLpm5W
Note: You can delete the RootObject class; it is not needed here.
Just make sure, your json string must have same properties as JsonHolder class.
public class JsonHolder
{
public string Id {get;set;}
public string Name {get;set;}
public string Gender {get;set;}
}
var jsonHolderList = new JsonConvert.DeserializeObject<List<JsonHolder>>(jsonString);
var jsonHolder = jsonHolderList.Single()
You can also convert json string into c# object as dynamic object. Just make sure, your json string must have same properties as JsonHolder class.
dynamic obj= new JsonConver.DeserializeObject<List<JsonHolder>>(StrJson);

Deserialize json Object using newtonsoft json

I have a json like
{"NewMessage":[{"Id":-1,"Message":"Test","MessageName":"test1"}],"OldMessage":[]}
and I need to deserialize this json in to my class object.
My Class
public class NewMessage{
public int Id{get;set;}
public string Message{get;set;}
public string MessageName{get;set;}
}
public class OldMessage{
public int Id{get;set;}
public string Message{get;set;}
public string MessageName{get;set;}
}
how can we achive this by using newtonsoft.json. can anyone help. thanks in advance
Your JSON actually contain an object which have properties of your defined classes - given the code posted, you don't have a class to deserialize into.
The first thing you can do is obvious - create a third class - MessagePair - which declares two properties - NewMessage[] and OldMessage[]. Then you can deserialize the JSON string like this:
var theMessages = JsonConvert.DeserializeObject<MessagePair>(jsonText);
If you don't like to create separate class, you can then deserialize into anonymous object like this:
var theMessages = new {
NewMessage[] NewMessages = null,
OldMessage[] OldMessages = null
};
theMessages = JsonConvert.DeserializeAnonymousType(jsonText, theMessages);

Parsing a JSON request in c# with a c# keyword

I'm trying to parse a JSON rpc 2.0 request. The standard is defined here.
I've defined my class as:
[DataContract]
public class JsonRpc2Request
{
public string method;
[DataMember(Name = "params")]
public object parameters;
public object id;
}
I then try and parse a request as follows:
JavaScriptSerializer ser = new JavaScriptSerializer();
var obj = ser.Deserialize<JsonRpc2Request>(Message.Trim());
obj.parameters is always null. I think this is because I can't define an object with the name params as per the JSON RPC spec. (My attempt is using the [DataMember(Name="params")] decoration.
How can I populate my parameters object when the JSON RPC spec calls for the name params which is a keyword in c#?
You can use the DataContractJsonSerializer:
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(JsonRpc2Request));
MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(Message.Trim()));
var obj = ser.ReadObject(stream);
and you'll want to annotate method and id with the DataMember attribute as well.
I would use Json.Net to get the full control over serialization/deserialization process
string json = #"{""method"":""mymethod"",""params"":[1,2],""id"":3}";
var rpcReq = JsonConvert.DeserializeObject<JsonRpc2Request>(json);
public class JsonRpc2Request
{
[JsonProperty("method")]
public string Method;
[JsonProperty("params")]
public object[] Parameters;
[JsonProperty("id")]
public string Id;
}
Since after completing this step, you are going to have to deal with more complex cases like
#"{""method"":""mymethod"",""params"":[{""name"":""joe""}],""id"":3}";

Categories

Resources