I have my ResponseDto which includes a simple string property named Answer.
public string Answer { get; set; }
Now, the requirement came such that I could either be getting an answer as a string, or as an array of int.
I decided to create two classes for this:
public class AnswerType {
public string Answer { get; set; }
}
public class OptionAnswerType {
public int[] AnswerOptionIds { get; set; }
}
I could serialize / deserialize it accordingly.
But to still keep a single response property type, I thought about creating an empty base class:
public class BaseAnswerType { }
public class AnswerType : BaseAnswerType {
public string Answer { get; set; }
}
public class OptionAnswerType : BaseAnswerType {
public Guid[] AnswerOptionIds { get; set; }
}
and change my property in ResponseDto to:
public BaseAnswerType Answer { get; set }
through which via run time, I would be returning either of the two classes.
Is this a bad approach? Any alternate would be greatly appreciated.
I have a JSON like
{
"myObject":{
"Difficult": true,
"Impossible": false
}
}
and a model like
public class Blas
{
public string something { get; set; }
public int somethinElse { get; set; }
public Dictionary<string, bool> myObject{ get; set; } //this is how I'm currently modeling the JSON
}
and when I use it I'm doing myObject["Difficult"];
but I need to do something like if(myObject.Difficult)..
Note to duplicate suggestion: The suggestion is
irrelevant because as the title says, my question is regarding to
model a JSON in C#, not converting. I can convert, but I need to
improve my current modeling in c#.
add class "MyObject" like this
Import
using Newtonsoft.Json
Code
public class MyObject
{
[JsonProperty("Difficult")]
public bool Difficult { get; set; }
[JsonProperty("Impossible")]
public bool Impossible { get; set; }
}
public class MyData {
[JsonProperty("myObject")]
public MyObject { get; set; }
}
then, define class like this
MyData obj = JsonConvert.DeserializeObject<MyData>(jsonString);
jsonString would be your json string.
I’ve got a base class (which is used a base for a User class):
public abstract class UserB2C
{
…
public List<SignInName> SignInNames { get; set; }
…
}
public class SignInName
{
string Type { get; set; }
string Value { get; set; }
}
And some JSON which includes:
\"signInNames\":[{\"type\":\"emailAddress\",\"value\":\"user#yahoo.co.uk\"}],
which is passed to:
JsonConvert.DeserializeObject<User>(json);
But the object created (deserialised) has:
"signInNames": [
{}
],
Any ideas why the field doesn’t get populated?
There is no error generated.
All other (simple) values get populated ok.
I've tried changing the case of Type and Value to match the JSON string,
and also tried explicitly creating the List<SignInName> when the object is created, but to no avail.
Properties of SignInName class should be declared as public in order to be deserialized with values.
public class SignInName
{
public string Type { get; set; }
public string Value { get; set; }
}
UPDATE
Here is a Minimal, Complete, and Verifiable example:
using Newtonsoft.Json;
using System.Collections.Generic;
class Program
{
static void Main(string[] args)
{
var json =
"{\"signInNames\":[{\"type\":\"emailAddress\",\"value\":\"user#example.com\"}]}";
var user = JsonConvert.DeserializeObject<User>(json);
System.Console.WriteLine(JsonConvert.SerializeObject(user));
}
}
public abstract class UserB2C
{
public List<SignInName> SignInNames { get; set; }
}
public class User : UserB2C { }
public class SignInName
{
string Type { get; set; }
string Value { get; set; }
}
Output is {"SignInNames":[{}]}
If we make SignInName class properies public the output will be:
{"SignInNames":[{"Type":"emailAddress","Value":"user#example.com"}]}
I have a three classes
public class A<T>
{
public bool Success {get; set;}
public string Reason {get; set;}
public T Data {get; set;}
}
public class B
{
public string Token {get; set;}
}
public class C
{
public int SomeInt {get; set;}
public string SomeString {get; set;}
public double SomeDouble {get; set;}
}
I call my web service and pass in A or A to deserialise into like this
async Task<T> MyTask<T>(...) (where T is A<B> or A<C>)
The service works fine and returns some JSON which if I try and to deserialise into a var, I'm getting nothing deserialised
To deserialise, I'm using
var foo = JsonConvert.DeserializeObject<T>(response.Content);
Is there a limitation to how I can deserialise the object so I can only use a base class rather than this form of class?
Yes, you can not just pass A because A is not base class, it is generic class and does not exists on its own.
C# generates generic classes during compile time, which means if you use A<B> in your code, C# will generate something like:
public class generated_A_B
{
public bool Success { get; set; }
public string Reason { get; set; }
public B Data { get; set; }
}
Nothing will be generated for A<C>, if not explicitly used. Reason is obvious, if you generate classes for every single combination of the A, you will bloat your code.
In your current situation it is better to just call them explicitly
void Main()
{
CallAB();
CallAC();
}
A<B> CallAB()
{
return ServiceCall<A<B>>("/api/ab");
}
A<C> CallAC()
{
return ServiceCall<A<C>>("/api/ac");
}
If you really want to get "generic" A, you should make A an actual base class and have your API to return the type of Data. In my example I just use Name of the type, but you probably should use FullName that includes namespace, to avoid name conflicts.
void Main()
{
var json = #"
{
""success"": true,
""reason"": ""All good"",
""type"": ""B"",
""data"": {
""token"": ""1234-5678""
}
}";
var foo = JsonConvert.DeserializeObject<A>(json);
var type = Assembly.GetExecutingAssembly().GetTypes().Where(i => i.IsClass && i.Name == foo.Type).FirstOrDefault();
if (type == null)
{
throw new InvalidOperationException(string.Format("Type {0} not found", foo.Type));
}
var data = foo.Data.ToObject(type);
}
public class A
{
public bool Success { get; set; }
public string Reason { get; set; }
public string Type { get; set; }
public JToken Data { get; set; }
}
public class B
{
public string Token { get; set; }
}
I would recommend either to go for the easy solution: create an intermediate merged your classes B and C, to something like
public class BC
{
public string Token { get; set; }
public int SomeInt { get; set; }
public string SomeString { get; set; }
public double SomeDouble { get; set; }
public bool IsB => !String.IsNullOrEmpty(Token);
public B ToB() => new B() { Token = Token };
public C ToC() => new C() { SomeInt = SomeInt, SomeString = SomeString, SomeDouble = SomeDouble };
}
Then you can call your service, and convert BC either to B or C:
async Task<A<BC>> MyTask<A<BC>>(...)
var abc = await MyTask(...);
if(abc.Data.IsB)
{
var resB = abc.data.ToB();
}
else
{
var resC = abc.data.ToC();
}
Or go to the more complicated solution, with some JsonConverter as explained in this answer:
how to implement custom jsonconverter in json net to deserialize a list of base
I've been trying to parse this JSON string. I'm using JSON.NET and a snippet of the JSON, my classes and basic function calls follow:
{"status":"ok","apirate":"0","people":{
"Mike":{"id":"Mike","rating":"0.80","questions":"100"},
"Donald":{"id":"Donald","rating":"0.7","questions":"9"},
"Tony":{"id":"Tony","rating":"0.22","questions":"2"},
"Penelope":{"id":"Penelope","rating":"0.006","questions":"6"},
"Sarah":{"id":"Sarah","rating":"0.79","questions":"20"},
"Thomas":{"id":"Thomas","rating":"0.12","questions":"25"},
"Gail":{"id":"Gail","rating":"0.44","questions":"35"}}}
The classes I'm using as storage objects:
public class Folks
{
public Folks()
{
}
public String status;
public String message; //optional
public int apirate;
public PeopleDetails[] people;
}
public class PeopleDetails
{
public PeopleDetails ()
{
}
public String id;
public double rating;
public int questions;
}
And finally, what I'm doing in the code:
Folks test = new Folks();
test = JsonConvert.DeserializeObject<Folks>(myRequest.GetResponse());
Status and API rate are coming through fine, message doesn't exist because there's no error and my PeopleDetails array is making an exception. (EDIT: throwing a JsonSerializationException because the type requires a JSON array to deserialize correctly.) I've tried putting another class/object between the two I've pasted here and I've tried different collections, and so on.
So... since this is my first time working with this (smart, pick the complex stuff the first time) can anybody point me towards a solution?
Thanks in advance.
Well, first, your given JSON is incorrect, there is a { missing in the penelope record.
so the correct JSON would be
{"status":"ok","apirate":"0","people":{
"Mike":{"id":"Mike","rating":"0.80","questions":"100"},
"Donald":{"id":"Donald","rating":"0.7","questions":"9"},
"Tony":{"id":"Tony","rating":"0.22","questions":"2"},
"Penelope":{"id":"Penelope","rating":"0.006","questions":"6"},
"Sarah":{"id":"Sarah","rating":"0.79","questions":"20"},
"Thomas":{"id":"Thomas","rating":"0.12","questions":"25"},
"Gail":{"id":"Gail","rating":"0.44","questions":"35"}}}
Then, if you have a look at the structur, you may not that people is not a list but a dictionary, with the name as the key.
So, here is a working test
[TestMethod]
public void Test()
{
var json = "{\"status\":\"ok\",\"apirate\":\"0\",\"people\":{\n\"Mike\":{\"id\":\"Mike\",\"rating\":\"0.80\",\"questions\":\"100\"},\n\"Donald\":{\"id\":\"Donald\",\"rating\":\"0.7\",\"questions\":\"9\"},\n\"Tony\":{\"id\":\"Tony\",\"rating\":\"0.22\",\"questions\":\"2\"},\n\"Penelope\":{\"id\":\"Penelope\",\"rating\":\"0.006\",\"questions\":\"6\"},\n\"Sarah\":{\"id\":\"Sarah\",\"rating\":\"0.79\",\"questions\":\"20\"},\n\"Thomas\":{\"id\":\"Thomas\",\"rating\":\"0.12\",\"questions\":\"25\"},\n\"Gail\":{\"id\":\"Gail\",\"rating\":\"0.44\",\"questions\":\"35\"}}}";
var folks = JsonConvert.DeserializeObject<Folks>(json);
Assert.AreEqual("ok", folks.Status);
}
public class Folks
{
public Folks()
{
this.People = new Dictionary<string, PeopleDetails>();
}
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
[JsonProperty("apirate")]
public int Apirate { get; set; }
[JsonProperty("people")]
public Dictionary<string, PeopleDetails> People { get; set; }
}
public class PeopleDetails
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("rating")]
public decimal Rating { get; set; }
[JsonProperty("questions")]
public int Questions { get; set; }
}