Json.Net - Deserialize object with "dynamic" properties - c#

I got the following Json data
{
"HasErrors": false,
"Includes": {
"Products": {
"091006": {
"hej" : "tja"
},
"091026": {
"hej" : "tjafsafsa"
}
}
}
}
By dynamic JSON i mean that the propertys on the Products class changes so I cant hardcode them in the c# class in the same way that I do with "HasErrors" for example.
Example:
{
"HasErrors": false,
"Includes": {
"Products": {
"091006": {
"hej" : "tja"
},
"091026": {
"hej" : "tjafsafsa"
}
}
}
}
Another example:
{
"HasErrors": false,
"Includes": {
"Products": {
"091126": { //CHANGED
"hej" : "tja"
},
"091043226": { //CHANGED
"hej" : "tjafsafsa"
}
}
}
}
I've built up the following classes in .NET
Response.cs
public class Response<T> where T : new()
{
[JsonProperty("hasErrors")]
public bool HasErrors { get; set; }
[JsonProperty("includes")]
public Includes<T> Includes { get; set; }
}
Includes.cs
public class Includes<T> where T : new()
{
[JsonProperty("products")]
public ProductRoot Products { get; set; }
}
ProductRoot.cs
public class ProductRoot
{
[JsonProperty("products")]
public Dictionary<string, Product> Products { get; set; }
}
Product.cs
public class Product
{
[JsonProperty("hej")]
public string Hej { get; set; }
}
I then try to Deserialize it like this:
var hej = JsonConvert.DeserializeObject<Response<Product>>(json_from_above_as_string);
And then I get this error:
Could not cast or convert from System.String to www.Models.Externals.Product.
[JsonSerializationException: Error converting value "091006" to type 'www.Models.Externals.Product'. Path 'Includes.ProductsOrder[0]', line 1, position 15173.]
You guys have any idea of what Im doing wrong?

I've instantiated an object of type <Response<Product>> successfully and serialized it to get a feel about what's happening under the hood and, as an example, the JSON I get out of it is as follows:
{
"hasErrors":false,
"includes":{
"products":{
"products":{
"091006":{
"hej":"tja"
}
}
}
}
}
This shows that your JSON simply doesn't marry up with your object model. You can do a couple of things. Either change your JSON format to something like the above, or change your object model to the following:
public class Response<T> where T : new()
{
[JsonProperty("hasErrors")]
public bool HasErrors { get; set; }
[JsonProperty("includes")]
public Includes<T> Includes { get; set; }
}
public class Includes<T> where T : new()
{
[JsonProperty("products")]
public Dictionary<string, T> Products { get; set; }
}
public class Product
{
[JsonProperty("hej")]
public string Hej { get; set; }
}

I solved it by removing the ProductRoot class.
It now looks like this:
public class Response<T> where T : new()
{
[JsonProperty("hasErrors")]
public bool HasErrors { get; set; }
[JsonProperty("includes")]
public Includes<T> Includes { get; set; }
}
public class Includes<T> where T : new()
{
[JsonProperty("products")]
public Dictionary<string, Product>Products { get; set; }
}

Related

Calling a Web API to get particular information using DeserializineAsync (JSON/ C#)

I'm trying to call a web API (JSON/Csharp Console application) I did the same steps in the dotnet : https://learn.microsoft.com/en-us/dotnet/csharp/tutorials/console-webapiclient
However, when I'm trying to get only the property "NAME1", an error came up : The JSON value could not be converted to System.Collections.Generic.List. I could get all the json information but when Deserializing the result for getting only particular data such as NAME1 it doesn't work.
public partial class Result
{
public List<GAZETTEER_ENTRY> GAZETTEER_ENTRY { get; set; }
}
public partial class GAZETTEER_ENTRY
{
public string ID { get; set; }
public string NAME1 { get; set; }
}
class Program
{
private static readonly HttpClient client = new HttpClient();
static async Task Main(string[] args)
{
await RunAsync();
}
private static async Task RunAsync()
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("key", "xxxxxx");
var streamTask = client.GetStreamAsync("https://api.os.uk/search/names/v1/find?query=coventry&fq=LOCAL_TYPE:Hospital");
var data = await JsonSerializer.DeserializeAsync<List<GAZETTEER_ENTRY>>(await streamTask);
foreach (var item in data)
{
Console.WriteLine(item.NAME1);
}
This is the JSON data:
"header" : {
"uri" : "https://api.os.uk/search/names/v1/find?query=coventry",
"query" : "coventry",
"format" : "JSON",
"maxresults" : 100,
"offset" : 0,
"totalresults" : 4134
},
"results" : [ {
"GAZETTEER_ENTRY" : {
"ID" : "osgb4000000074568994",
"NAMES_URI" : "http://data.ordnancesurvey.co.uk/id/4000000074568994",
"NAME1" : "Coventry",
"TYPE" : "populatedPlace",
"LOCAL_TYPE" : "City",
"COUNTRY" : "England",
}
}, {
"GAZETTEER_ENTRY" : {
"ID" : "osgb4000000019401618",
"NAMES_URI" : "http://data.ordnancesurvey.co.uk/id/4000000019401618",
"NAME1" : "Coventry Street",
"TYPE" : "transportNetwork",
"LOCAL_TYPE" : "Named Road",
"COUNTRY" : "England",
}
}, {
"GAZETTEER_ENTRY" : {
"ID" : "osgb4000000073321650",
"NAMES_URI" : "http://data.ordnancesurvey.co.uk/id/4000000073321650",
"NAME1" : "Coventry",
"TYPE" : "transportNetwork",
"LOCAL_TYPE" : "Railway Station",
"COUNTRY" : "England",
}
Try deserializing the json using this RootObject model:
public class RootObject
{
public ResultObject[] results { get; set; }
}
public class ResultObject
{
public GazetteerEntryObject GAZETTEER_ENTRY { get; set; }
}
public class GazetteerEntryObject
{
public string NAME1 { get; set; }
}
Do it this way
var data = await JsonSerializer.DeserializeAsync<RootObject>(await streamTask);
To retrieve the names, do it like this:
var names = data.results.Select(x => x.GAZETTEER_ENTRY.NAME1);
Your response classes should be like
public class SearchResult
{
public Header header { get; set; }
public List<Result> results { get; set; }
}
public class Header
{
public string uri { get; set; }
public string query { get; set; }
public string format { get; set; }
public int maxresults { get; set; }
public int offset { get; set; }
public int totalresults { get; set; }
}
public class Result
{
public GAZETTEER_ENTRY GAZETTEER_ENTRY { get; set; }
}
public class GAZETTEER_ENTRY
{
public string ID { get; set; }
public System.Uri NAMES_URI { get; set; }
public string NAME1 { get; set; }
public string TYPE { get; set; }
public string LOCAL_TYPE { get; set; }
public string COUNTRY { get; set; }
}
and the deserialize should be
var data = await JsonSerializer.DeserializeAsync<SearchResult>(await streamTask);
and access NAME1 of nth result
data.results[0].GAZETTEER_ENTRY.NAME1
if you find difficulty in comparing classes and json response, You can simply copy the json response and goto VS -> edit-> paste special -> paste json as classes. VS will auto generate classes for you.

How to design different structures with common parts using inheritance

I've searched a lot about this problem without finding any appreciable result (maybe because i don't have a clear idea of what to ask), then here we are...
I have to design two structures derived from two different JSON which they have common parts:
{
"data":
{
"success": true,
"updated_ids": [0],
"not_updated_ids": [0]
}
}
{
"data":
{
"success": true,
"branch_ids":["0"]
}
}
My idea is to create something like this:
class Response
{
Data data { get; set; }
}
class Data
{
bool success { get; set; }
}
class UserResponse : Data
{
List<int> updatedIds { get; set; }
List<int> notUpdatedIds { get; set; }
}
class BranchResponse : Data
{
List<string> branchIds { get; set; }
}
Now my question is: How can i instantiate my two different classes?
If I do new Response() i don't get the UserReponse or BranchResponse part and if i do new UserResponse() i don't get the Response part.
Basically i would like to instantiate a variable for each structure, populate it with all the values and then Serialize to create the Json.
Thanks in advance!
Alright so what you need is an interface and a factory to accomplish what you are trying to create here.
public interface IData
{
bool Success { get; set; }
}
public class Response
{
public IData Data { get; set; }
}
public class UserData : IData
{
public bool Success { get; set; }
public List<int> UpdatedIds { get; set; }
public List<int> NotUpdatedIds { get; set; }
}
public class BranchData : IData
{
public bool Success { get; set; }
public List<string> BranchIds { get; set; }
}
public class HowToUseIt
{
public Response CreateResponse()
{
Response myReponse = new Response
{
Data = new UserData
{
Success = true,
UpdatedIds = new List<int>(),
NotUpdatedIds = new List<int>()
}
};
return myReponse;
}
public void WhatKindOfDataDoIHave(Response response)
{
if (typeof(UserData) == response.Data.GetType())
{
//You have user data
}
else if (typeof(BranchData) == response.Data.GetType())
{
//You have branch data
}
else
{
//You have a problem!
}
}
}
Using a part of the suggestion from Andrew i've finally found a solution whihc works for me. Posting it to anyone who maybe is going crazy with it:
class Response
{
Data data { get; set; }
}
public abstract class Data
{
bool Success { get; set; }
public abstract Response CreateDeserializationModelSchema();
}
public class UserData : Data
{
public bool Success { get; set; }
public List<int> UpdatedIds { get; set; }
public List<int> NotUpdatedIds { get; set; }
public override Response CreateDeserializationModelSchema()
{
return new Response
{
Data = new UserData()
};
}
public static UsersData GetContent(Response response)
{
if (response.Data.GetType() == typeof(UsersData))
return (UsersData)response.Data;
else
throw new FormatException("Response not in the correct format to parse into UpdateUsersStatusResponse");
}
}
class UsersDataConverter : CustomCreationConverter<Response>
{
public override Response Create(Type objectType)
{
return new UsersData().CreateDeserializationModelSchema();
}
}
public class HowToUseIt
{
public void Use()
{
Response resp = JsonConvert.DeserializeObject<Response>(jsonResponse.ToString(), new UserDataConverter());
UserData u = UserData.GetContent(resp)
}
}

Creating an array in C# that contains an object

I have a RequestModel that contains some properties and another class Consignment.
Calling JsonConvert.SerializeObject(this); returns
{ "consignment": { "collectionDetails": null } }
Instead I would like to return an array of the object
"consignment": [ { "collectionDetails": null } ]
I've tried to create Consignment[] but get a conversion issue.
I've also tried to create a List<Consignment>
class RequestModel
{
public string job_id { get; set; }
public Consignment consignment = new Consignment();
public class Consignment
{
public string consignmentNumber { get; set;
}
public string ToJSON()
{
return JsonConvert.SerializeObject(this);
}
Here you go:
public class RequestModel
{
public string job_id { get; set; }
public Consignment consignment { get; set; }
public RequestModel()
{
consignment = new Consignment();
}
public string ToJSON()
{
return JsonConvert.SerializeObject(this);
}
}
Then your Consignment DTO would look like this assuming you have a CollectionItems class (didn't see any code in question around this):
public class Consignment
{
public List<CollectionItems> collectionDetails { get; set; }
public Consignment()
{
collectionDetails = new List<Collection>();
}
}

Json Deserializing to null using Newtosoft Json

I have the following json:
{
"cuisines": [
{
"cuisine": {
"cuisine_id": 152,
"cuisine_name": "African"
}
},
{
"cuisine": {
"cuisine_id": 1,
"cuisine_name": "American"
}
},
{
"cuisine": {
"cuisine_id": 4,
"cuisine_name": "Arabian"
}
},
{
"cuisine": {
"cuisine_id": 151,
"cuisine_name": "Argentine"
}
}
]
}
Im using RestSharp to get the data and sending it to JSON.Net:
JsonConvert.DeserializeObject<Cuisines>(content)
And I'm using the following classes:
public class Cuisine
{
[JsonProperty("cuisine_id")]
public string cuisine_id { get; set; }
[JsonProperty("cuisine_name")]
public string cuisine_name { get; set; }
}
public class Cuisines
{
[JsonProperty("cuisines")]
public List<Cuisine> AllCuisines { get; set; }
}
What is wierd is, the return data is finding 81 cuisine objects on my request, but all the Cuisine info is null.
You model needs one more class. So it should be
public class Cuisine
{
[JsonProperty("cuisine_id")]
public string cuisine_id { get; set; }
[JsonProperty("cuisine_name")]
public string cuisine_name { get; set; }
}
public class CuisineWrapper
{
public Cuisine cuisine { get; set; }
}
public class Cuisines
{
[JsonProperty("cuisines")]
public List<CuisineWrapper> AllCuisines { get; set; }
}
Your classes definitions doesn't match provided JSON. Top level array contains objects with a single property (object) cuisine, like so:
"cuisine": {
"cuisine_id": 152,
"cuisine_name": "African"
}
where as your C# List<Cuisine> contains objects directly exposing cuisine_id and cuisine_name. If you can't change JSON, decorate class Cuisine with JsonObjectAttribute
You actually have 3 objects - A root object that contains a property named cuisines that is a collection of Cuisine.
When you paste your JSON as classes in visual studio you get the following structure (which you would probably want to rename some things and list-ify the array)
public class Rootobject
{
public Cuisine[] cuisines { get; set; }
}
public class Cuisine
{
public Cuisine1 cuisine { get; set; }
}
public class Cuisine1
{
public int cuisine_id { get; set; }
public string cuisine_name { get; set; }
}
Your JSON is nested more than your class structure. If you can change your JSON to the form:
"cuisines": [
{
"cuisine_id": 152,
"cuisine_name": "African"
},
{
"cuisine_id": 1,
"cuisine_name": "American"
},
.. etc
Then it will match your class structure.
Alternatively, change your class structure to match the JSON:
public class Cuisine
{
[JsonProperty("cuisine")]
public CuisineData data { get; set; }
}
public class CuisineData
{
[JsonProperty("cuisine_id")]
public string cuisine_id { get; set; }
[JsonProperty("cuisine_name")]
public string cuisine_name { get; set; }
}
public class Cuisines
{
[JsonProperty("cuisines")]
public List<Cuisine> AllCuisines { get; set; }
}

How to map interface with multi-inheritance?

I have following original class structure:
public interface IMapFromElement
{
string Prop { get; }
}
public interface IMapFromElementDerived : IMapFromElement
{
string Prop2 { get; }
}
public interface IMapFromElement2 : IMapFromElement
{
}
public interface IMapFromElementDerived2 : IMapFromElementDerived, IMapFromElement2
{
}
public abstract class MapFromElement : IMapFromElement2
{
public string Prop { get; set; }
}
public class MapFromElementDerived : MapFromElement, IMapFromElementDerived2
{
public string Prop2 { get; set; }
}
I'm trying to map them to:
public class MapTo
{
public IMapToElementWritable Element { get; set; }
}
public interface IMapToElementWritable : IMapFromElement
{
new string Prop { get; set; }
}
public interface IMapToElementWritableDerived : IMapFromElementDerived, IMapToElementWritable
{
new string Prop2 { get; set; }
}
public abstract class MapToElement : IMapToElementWritable
{
public string Prop { get; set; }
}
public class MapToElementDerived : MapToElement, IMapToElementWritableDerived
{
public string Prop2 { get; set; }
}
I try to map them with:
var from = new MapFrom
{
Element = new MapFromElementDerived {Prop = "qwerty", Prop2 = "asdf"}
};
Mapper.Initialize(
cfg =>
{
cfg.CreateMap<IMapFrom, MapTo>();
cfg.CreateMap<IMapFromElement, IMapToElementWritable>();
cfg.CreateMap<IMapFromElementDerived, IMapToElementWritableDerived>()
.IncludeBase<IMapFromElement, IMapToElementWritable>()
.ConstructUsing((ResolutionContext item) => new MapToElementDerived());
cfg.Seal();
});
Mapper.AssertConfigurationIsValid();
var result = Mapper.Map<MapTo>(from);
I expected, that I will have as output MapTo with MapToElementDerived as it's Element property value. But really I was unable to achieve it - Automapper creates proxy for IMapToElementWritable instead. Looks like IncludeBase doesn't work (I tried it with Include also, but it haven't helped). Maybe I just write incorrect config.
Looks like there is an issue in Automapper. I've tried to resolve it in https://github.com/AutoMapper/AutoMapper/pull/1037

Categories

Resources