Deserializing nested json objects. When inner object might differ - c#

The service I'm dealing returning different objects nested into a generic object. Here are sample mockup results from service
{"date":1591430887.591481,"results":[{"identity":"result_type_a","result":{"attr_a":1591427634}}]}
{"date":1591430887.591481,"results":[{"identity":"result_type_b","result":{"attr_b":1591427634,"attr_bb":3591457634}}]}
{"date":1591430887.591481,"results":[{"identity":"result_type_c","result":{"attr_c":1591427634,"attr_cc":3591457634,"attr_cc":59634}},{"identity":"result_type_d","result":{"attr_d":"rfrvr","attr_dd":"ytur"}}]}
I tried creating a generic object with declaring result attribute as string. And planning that I could deserialize to the returning json to that generic object, check identity attribute and deserilize the string in result attribute to specific object type.
Here is the object structure
public class GeneralResponse
{
[JsonProperty("date")]
public double Date { get; set; }
[JsonProperty("results")]
public List<GenericResults> Results { get; set; }
}
public class GenericResults
{
[JsonProperty("identity")]
public string Identity { get; set; }
[JsonProperty("result")]
public string Result { get; set; }
}
To serialize/deserialize I'm using Newtonsoft library and the code is below
public static GeneralResponse SerializeResponse(string response)
{
return JsonConvert.DeserializeObject<GeneralResponse>(response);
}
Unfortunately I got following exception while deserializing generic object.
"Unexpected character encountered while parsing value: {. Path 'results[0].result', line 1, position 71."
If I declare Result property of GenericResult as object as below
public class GenericResults
{
[JsonProperty("identity")]
public string Identity { get; set; }
[JsonProperty("result")]
public object Result { get; set; }
}
I can pass first serialization and make the second serialization without getting any exception.
string inner_object = response.Result.ToString();
switch (type)
{
case ResponseTypes.type_A: return JsonConvert.DeserializeObject<ObjectTypeA>(inner_object);
case ResponseTypes.type_B: return JsonConvert.DeserializeObject<ObjectTypeB>(inner_object);
case ResponseTypes.type_C: return JsonConvert.DeserializeObject<ObjectTypeC>(inner_object);
default: throw new Exception("Unknown Response Type");
}
But returned object does not contain the data.
I would appreciate any help about modeling this algorithm. Thanks in advance.

When you use string with JSON.NET serialization it is not actually using the JSON string but tries to parse a JSON string value.
You could use JObject instead, which is JSON.NETs wrapper for JSON objects. The JObject can then be used to deserialize into a type or by used to access JSON properties directly. Like this:
switch (type)
{
case ResponseTypes.type_A: return response.Result.ToObject<ObjectTypeA>(inner_object);
case ResponseTypes.type_B: return response.Result.ToObject<ObjectTypeB>(inner_object);
case ResponseTypes.type_C: return response.Result.ToObject<ObjectTypeC>(inner_object);
default: throw new Exception("Unknown Response Type");
}

You could use a Dictionary to store the result, which looks to be polymorphic. For example,
public partial class GeneralResponse
{
[JsonProperty("date")]
public double Date { get; set; }
[JsonProperty("results")]
public ResultElement[] Results { get; set; }
}
public partial class ResultElement
{
[JsonProperty("identity")]
public string Identity { get; set; }
[JsonProperty("result")]
public Dictionary<string,object> Result { get; set; }
}
Working Demo
Output Sample

Related

Deserialize JSON property into different class properties

I am receiving API responses from 3rd party that have ambiguous types. For some methods it is:
{"error":{"message":"Resource is already part of this app","status_code":400}}
And on other calls it is:
{"error": "Resource is already part of this app" }
Is it possible to deserialize such responses into something like:
public class Response
{
[JsonProperty("error")]
public string Error { get; set; }
[JsonIgnore] //[JsonProperty("error")]
public ObjectError ObjectError { get; set; }
}
public class ObjectError
{
[JsonProperty("message")]
public string Message { get; set; }
[JsonProperty("status_code")]
public string StatusCode { get; set; }
}
UPDATE
So I have ended up using object as catch all for deserialization.
[JsonProperty("error")]
public object Error { get; set; }
public string ErrorAsString => Error is string ? Error.ToString() : null;
public ObjectError ErrorAsObject => Error is string ? null : Error != null ? JsonConvert.DeserializeObject<ObjectError>(Error.ToString()) : null;
It's not ideal, I know.
You can do this easily using NetwosoftJson. Here you can check on how to deserialize into an object using it.
You could always parse the JSON object and check to see if it has specific fields. For example:
JObject json = JObject.Parse(jsonString);
if(json.HasKey("message"))
{
var result = JsonConvert.DeserializeObject<ObjectError>(jsonString);
// Do some stuff here
}
else
{
var result = JsonConvert.DeserializeObject<Response>(jsonString);
// Do some stuff here
}

Deserializing a json string into structure results in exception

I'm having issues mapping a json string returned from sql.
The structure of the object looks like this
public class OrderSummaryDto
{
public List<PrescriptionInfo> PrescriptionsInfo { get; set; }
public class PrescriptionInfo
{
public string Name { get; set; }
public AdministrationInfo Administration { get; set; }
public class AdministrationInfo
{
public string Instructions { get; set; }
public string PackageType { get; set; }
}
}
}
The json string returned from DB
[{"Name":"testName","Units":3,"Medium":"flower power","Administration":"{\"Instructions\":\"drIVnkLEm0b24OK9ceMeeF2fq\",\"PackageType\":\"case\"}"},{"Name":"testName","Units":3,"Medium":"flower power","Administration":"{\"Instructions\":\"drIVnkLEm0b24OK9ceMeeF2fq\",\"PackageType\":\"case\"}"}]
The Sql that generates the json string
(SELECT _co.[Name]
,_co.[Pharmacy_Instructions] AS [Administration]
FROM [dbo].[Compounds] _co
WHERE _co.[Id] = 1
FOR JSON PATH) AS [PrescriptionsInfo]
Pharmacy_Instructions is already a JSON formated string
Message "Could not cast or convert from System.String to
Models.Order.Summary.OrderSummaryDto+PrescriptionInfo+AdministrationInfo." string
Your JSON string is currently a list of PrescriptionInfo Objects, not a OrderSummary object that contains a list of PrescriptionInfo objects. In addition, some fields (Administration) are being treated as strings, instead of objects. The JSON needs to look like this to work properly:
{"PrescriptionsInfo": [{"Name":"testName","Units":3,"Medium":"flower power","Administration":{"Instructions":"drIVnkLEm0b24OK9ceMeeF2fq","PackageType":"case"}},{"Name":"testName","Units":3,"Medium":"flower power","Administration":{"Instructions":"drIVnkLEm0b24OK9ceMeeF2fq","PackageType":"case"}}]}
Or formatted:
{
"PrescriptionsInfo":[
{
"Name":"testName",
"Units":3,
"Medium":"flower power",
"Administration":{
"Instructions":"drIVnkLEm0b24OK9ceMeeF2fq",
"PackageType":"case"
}
},
{
"Name":"testName",
"Units":3,
"Medium":"flower power",
"Administration":{
"Instructions":"drIVnkLEm0b24OK9ceMeeF2fq",
"PackageType":"case"
}
}
]
}
In order to do this you will have to use aliases. Check out this MS guide
https://learn.microsoft.com/en-us/sql/relational-databases/json/format-nested-json-output-with-path-mode-sql-server?view=sql-server-2017
I am weak with JSON support in SQL, might ask with those tags, but it seems like it would be something similar to this.
(SELECT _co.[Name],
JSON_VALUE(_co.[Pharmacy_Instructions], '$.Instructions') AS "Administration.Instructions",
JSON_VALUE(_co.[Pharmacy_Instructions], '$.PackageType') AS "Administration.PackageType"
FROM [dbo].[Compounds] _co
WHERE _co.[Id] = 1
FOR JSON PATH) AS [PrescriptionsInfo]

Deserialize JSON object into type System.Guid

What I have is a REST response in JSON format that looks like this :{ "guid": "c75d06a8-a705-48ec-b6b3-9076becf20f4" }
When trying to deserialize this reponse String into an Object of type System.Guid like this:Newtonsoft.Json.JsonConvert.DeserializeObject(response.content, type);, The following exception is thrown :
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Nullable`1[System.Guid]' because the type requires a JSON primitive value (e.g. string, number, boolean, null) to deserialize correctly.
To fix this error either change the JSON to a JSON primitive value (e.g. string, number, boolean, null) 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.
Path 'guid', line 1, position 8.
Any help is appreciated!
If you don't want to create a class just to contain the Guid value, you can just parse the value directly from the parsed Json:
string json = "{ \"guid\": \"c75d06a8-a705-48ec-b6b3-9076becf20f4\" }";
var container = JToken.Parse(json);
Guid guid;
if (Guid.TryParse(container["guid"]?.ToString(), out guid))
{
Console.WriteLine(guid);
}
else{
Console.WriteLine("No guid present or it has an invalid format");
}
// c75d06a8-a705-48ec-b6b3-9076becf20f4
Another option is to use a dynamic variable, though personally I don't think this is a good use-case for dynamic:
string json = "{ \"guid\": \"c75d06a8-a705-48ec-b6b3-9076becf20f4\" }";
dynamic container = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(json);
Guid guid;
if (Guid.TryParse(container.guid?.ToString(), out guid)) {
Console.WriteLine(guid);
}
else {
Console.WriteLine("No guid present or it has an invalid format");
}
// c75d06a8-a705-48ec-b6b3-9076becf20f4
Its been a bit since I've done this type of thing, however I think the answer is in your error message there. Let it come back as string. In your class override the guid string property to set another property of type GUID and perform your conversion and error handling there. Another way is to use a constructor to help out here. Hope this gives you a direction.
Fully example with GUID and Json
string content = "{\"data\":{\"Type\":\"Foo\",\"Description\":\"bar \",\"Software\":\"baz\",\"Version\":\"qux\",\"Url\":\"http://example.com\",\"DatabasePortNumber\":1,\"Id\":\"2cdc66f1-0000-0000-39ac-233c00000000\"}}";
var result_SystemText = System.Text.Json.JsonSerializer.Deserialize<SystemApplicationResponse>(content); //Will result in null-object
var result_Newtonsoft = Newtonsoft.Json.JsonConvert.DeserializeObject<SystemApplicationResponse>(content); //Will result in correctly serialized object
public class SystemApplicationResponse
{
public SystemApplicationDto Data { get; set; }
}
public class SystemApplicationDto
{
public SystemApplicationDtoType Type { get; set; }
public string Description { get; set; }
public string Software { get; set; }
public string Version { get; set; }
public string Url { get; set; }
public int DatabasePortNumber { get; set; }
public System.Guid Id { get; set; }
}
public enum SystemApplicationDtoType
{
Foo = 0
}

How to deserialize JSON to complex object in C#

I'm trying to deserialize json result to wanted object. The result I'm getting is:
{
"base": "EUR",
"date": "2017-06-30",
"rates": {
"AUD": 1.4851,
"BGN": 1.9558,
"BRL": 3.76,
"CAD": 1.4785
}
}
I want this result to deserialize to my object:
public class ExchangeRates
{
public string Base { get; set; }
public DateTime Date { get; set; }
public IList<Rates> Rates { get; set; }
}
public class Rates
{
public string Name { get; set; }
public decimal Value { get; set; }
}
my deserialization looks like this:
using (var httpClient = new HttpClient())
{
var response = httpClient.GetAsync("http://api.fixer.io/latest").Result;
var result = response.Content.ReadAsStringAsync().Result;
var values = JsonConvert.DeserializeObject<ExchangeRates>(result);
}
When I run the program I get following exception:
Newtonsoft.Json.JsonSerializationException: 'Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[ConsoleApp4.Rates]' 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.
Path 'rates.AUD', line 1, position 49.'
How can I deserialize JSON to my wanted object??
UPDATE 1
Or maybe I can just deserialize 'rates' list?
Take a look at your JSON, specifically rates:
"rates": {
"AUD": 1.4851,
"BGN": 1.9558,
"BRL": 3.76,
"CAD": 1.4785
}
This is very clearly a JSON object, as it has key-value pairs. However, looking at your code, you have defined the corresponding property (Rates) as an IList:
public IList<Rates> Rates { get; set; }
I understand your reasoning behind defining the Rates class. You think that by defining that class, NewtonSoft will deserialize rates the way you want it to. However, this is impossible because rates is not an array, and therefore deserializing it into any kind of IList is impossible.
The easiest and most clear cut solution is to use a dictionary:
public Dictionary<string, decimal> Rates { get; set; }
However, if you don't want to use a dictionary, you need to modify your JSON like so and your solution will work:
"rates":[
{
"Name":"AUD",
"Value":1.4851
},
{
"Name":"BGN",
"Value":1.9558
},
{
"Name":"BRL",
"Value":3.76
},
{
"Name":"CAD",
"Value":1.4785
}
]
By converting rates to an array, and making its contents objects instead of key-value pairs, NewtonSoft can deserialize rates as a list, and its contents as instances of the Rates class.
I agree with the other guys comments: you should use a Dictionary. To achieve the conversion to your final object structure you can use for example an intermediary class with an explicit cast operator.
using System;
using Newtonsoft.Json;
using System.Collections.Generic;
public class Program
{
public void Main()
{
var result = #"{
""base"": ""EUR"",
""date"": ""2017-06-30"",
""rates"": {
""AUD"": 1.4851,
""BGN"": 1.9558,
""BRL"": 3.76,
""CAD"": 1.4785
}}";
var values = (ExchangeRates) JsonConvert.DeserializeObject<TempExchangeRates>(result);
Console.WriteLine(values.Base);
Console.WriteLine(values.Date);
foreach(var rate in values.Rates)
Console.WriteLine(rate.Name + ": " + rate.
}
}
public class TempExchangeRates
{
public string Base { get; set; }
public DateTime Date { get; set; }
public Dictionary<string,decimal> Rates { get; set; }
public static explicit operator ExchangeRates(TempExchangeRates tmpRates)
{
var xRate = new ExchangeRates();
xRate.Base = tmpRates.Base;
xRate.Date = tmpRates.Date;
xRate.Rates = new List<Rates>();
foreach(var de in tmpRates.Rates)
xRate.Rates.Add(new Rates{Name = de.Key, Value = de.Value});
return xRate;
}
}
public class ExchangeRates
{
public string Base { get; set; }
public DateTime Date { get; set; }
public IList<Rates> Rates { get; set; }
}
public class Rates
{
public string Name { get; set; }
public decimal Value { get; set; }
}

deserialize json into .net object using json.net

I am having a problem deserializing some JSON string back into .net objects. I have a container class which contains some information from external and there is a field call ClassType which defined what type of information is that and the actual content is in another property, which currently can be anything, so we define that as an Object type.
Following are the .net class definition which helps to understand the issue.
class ClassOne
{
public string Name { get; set; }
public int Age { get; set; }
}
class ClassTwo
{
public string AddressLine { get; set; }
public string AddressLine2 { get; set; }
}
class ClassThree
{
public string Country { get; set; }
public string Passport { get; set; }
}
class ContainerClass
{
public string ClassType { get; set; }
public object ClassContent { get; set; }
}
When getting the information from external in a JSON format it will be something like:
{"ClassType":"Class1","ClassContent":{"Name":"James","Age":2}}
I am using Newtonsoft JSON.net library to deserialize the JSON string. It seems like that the default deserialize function will just deserialize that into an Newtonsoft.Json.Linq.JContainer. I just wondering how can I write some Converter to deserialize the ClassContent based on the ClassType definition. Any code sample will be highly appreciated.
I would go dynamic way, like:
string json = #"{""ClassType"":""Class1"",""ClassContent"":{""Name"":""James"",""Age"":2}}";
dynamic jObj = JObject.Parse(json);
if (jObj.ClassType == "Class1")
{
Console.WriteLine("{0} {1}", jObj.ClassContent.Name, jObj.ClassContent.Age);
}
Since returning an object (ClassContent) doesn't mean much, and you have to cast it to a concrete class somehow (using some if's or switch).
Sample:
var container = JsonConvert.DeserializeObject<ContainerClass>(json);
JContainer content = (JContainer)container.ClassContent;
switch(container.ClassType)
{
case "Class1": return container.ToObject(typeof(ClassOne));
..
}
use dynamic and call .ToObject(Type type)
dynamic root = JObject.Parse(json)
return root["ClassContent"].ToObject(Type.GetType(root["ClassType"]))
Try the following
var jsonObject = JObject.Parse(jsonString);
var result = jsonObject.ToObject(Type.GetType("namespace.className"));

Categories