public class CarsModel
{
public int OnStock { get; set; }
public int Rented { get; set; }
public List<CarInfo> Cars { get; set; }
}
public class CarInfo
{
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public CarDetails Details { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public ServiceInfo ServiceInfo { get; set; }
[JsonProperty( NullValueHandling = NullValueHandling.Ignore)]
public MarketingInfo MarketingInfoDetails { get; set; }
}
Depending on the scenario this cars property will have different representation (either MarketingInfo or CarDetails or ServiceInfo, but not both).
case 1: MarketingInfo
"onStock": 11,
"rented": 2,
"cars": [{
"carId": 1,
"MarketingInfo": {
"contactPerson": "John Smith"
}, ...
}]
case 2: ServiceInfo and CarDetails
"onStock": 11,
"rented": 2,
"cars": [{
"ServiceInfo": {
"lastService": "2021-01-01"
},
"CarDetails": {
"carId": 1,
"lastOwner": "Mister Clark"
}, ...
}]
This is how I populate my response
var carsModel = new CarsModel{
OnStock = 11,
Rented = 2
};
foreach (var car in cars)
{
carsModel.Cars.Add(new CarsModel
{
MarketingInfoDetails = new MarketingInfo
{
ContactPerson = "John Smith",
....
}
});
}
In the above model representation my response is NOT as I want them to be (because it contains marketingInfoDetails word)
Response:
{
"data" :[{
"onStock": 11,
"rented": 2,
"cars": [
{
"marketingInfoDetails": {
"contactPerson": "John Smith",
...
}
}]
}]
}
where I want to be represented without name "marketingInfoDetails"
{
"data" : [{
"onStock" : 11,
"rented" : 2,
"cars" : [{ "contactPerson" : "John Smith"}]
}]
}
Please share if you think this response for the described scenario can be modeled better.
Your C# model and result JSON are matching. What you're asking for changes from the start of your question to the end of your question. What you appear to want from the C# model and JSON is to remove the object which your desired example includes alongside a carId property that is not present in your C# models.
You need to remove the intermediate CarInfo class and instead have your list of cars be a common interface which is inherited by CarDetails, ServiceDetails, and MarketingInfo, then you will not see the "MarketingInfoDetails" property in your JSON.
If you were to instead add the carId property to CarInfo and again serialize the C# model to JSON, you will see that your current model matches exactly what you first stated you want the JSON to look like.
If you are using Json.net, you could make use of Conditional Serialization functionality using ShouldSerialize.
To conditionally serialize a property, add a method that returns boolean with the same name as the property and then prefix the method name with ShouldSerialize. The result of the method determines whether the property is serialized. If the method returns true then the property will be serialized, if it returns false then the property will be skipped.
You would need to modify your DTOs to include an additional property and method (for each property you want to make conditional) as the following.
[JsonIgnore]
public bool SerializeDetails{get;set;} = true;
public bool ShouldSerializeDetails() =>SerializeDetails;
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public CarDetails Details { get; set; }
[JsonIgnore]
public bool SerializeMarketingInfoDetails{get;set;} = true;
public bool ShouldSerializeMarketingInfoDetails() =>SerializeMarketingInfoDetails;
[JsonProperty( NullValueHandling = NullValueHandling.Ignore)]
public MarketingInfo MarketingInfoDetails { get; set; }
For each instance of CarDetails, you could now set the SerializeMarketingInfoDetails or SerializeDetails properties to conditional serialize them.
Demo Code
It'a recommended that you use EmiDefaultValue="true" for datamembers.
In the case the proprety is null, it won't be shown in the jsonsince its value is null.
Related
I want to get data from json file correctly. The json data file I modeled for this is as follows:
{
"Free title 1":[
{
"Subject": "a1",
"Relation": "a2"
},
{
"Subject": "b1",
"Relation": "b2"
}
],
"Another free title":[
{
"Subject": "z1",
"Relation": "z2"
},
{
"Subject": "y1",
"Relation": "y2"
}
],
"Unordered title":[
{
"Subject": "x1",
"Relation": "x2"
},
{
"Subject": "w1",
"Relation": "w2"
}
]
}
This is how I create an object class:
public class _Infos_
{
public List<_Info_> Infos { get; set; }
}
public class _Info_
{
public string Subject { get; set; }
public string Relation { get; set; }
}
And finally I'm trying to get the data in a method like this:
var js = JsonConvert.DeserializeObject<_Infos_>(File.ReadAllText("__FILE_PATH__"));
foreach (var j in js.Infos)
{
MessageBox.Show(j.Subject);
}
I get the error that js is empty. Here I want to get Free title 1, Another free title and Unordered title in a list. Of course, these titles will be constantly changing. Afterwards, I want to get the Subject and Relation data under these titles. But I have no idea how to get it.
This data structure is a dictionary of collections of _Info_s. You need to deserialize it to Dictionary<string, List<_Info_>>.
Here are System.Text.Json and Json.net examples:
var d = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, List<_Info_>>>(json);
var d2 = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, List<_Info_>>>(json);
Your class definition is a little wrong.
You can use online tools "json to c#" to generate the correct classes.
like this one: https://json2csharp.com
Your "root" of your json for example does not contain an array in your json. The property "Free title 1":[..] is an array, so your root needs a property with the name FreeTitle1 and it has to be an array/list.
public class Root
{
[JsonProperty("Free title 1")]
public List<TitleInfo> FreeTitle1 { get; set; }
[JsonProperty("Another free title")]
public List<TitleInfo> AnotherFreeTitle { get; set; }
[JsonProperty("Unordered title")]
public List<TitleInfo> UnorderedTitle { get; set; }
}
public class TitleInfo
{
public string Subject { get; set; }
public string Relation { get; set; }
}
If your object members have dynamic names, you can also manually deserialize the object, e.g. using the general type JObject. E.g.
JObject obj = JObject.Parse(File.ReadAllText("__FILE_PATH__"));
JObject implements IEnumerable<KeyValuePair<string, JToken>> over which you can iterate.
Each member will then have JToken Value, which is a JArray in this case, which you can cast to a List of your type.
foreach (var groups in obj)
{
var infos = groups.Value.ToObject<List<_Info_>>();
// .. loop over infos
}
I have a JSON object that looks like this:
{
"Name": "John Smith"
"Age": 18
"Children" : [
{
"Name": "Little Johnny"
"Age": 4
"Children": []
}
]
}
My Model object looks like this
public class Person {
public string Name { get; set; }
public int Age { get; set; }
public IList<Person> Children { get; set; }
public Guid? TrackingKey { get; set; }
}
As you see the "TrackingKey" property is not a part of the JSON.
I want to have the TrackingKey property to be set with a value that I provide upon JSON deserialization.
The tracking key will be the same for the parent and all the children as well.
However it cannot be a static value that i could pass in with the DefaultValue attribute when I declare my model.
What would be the best way to assign The Tracking Key to a collection of parents and ALL their children?
You can use GUIDs to generate the value you need.
string dados = Guid.NewGuid().ToString("N");
I have the following problem which is driving me crazy to find a proper solution.
I have to consume two RESTful APIs which return the same structure except for the items structure.
Let me give you two examples:
{
"hasmoredata":true,
"current_page": 1,
"page_size": 20,
"total_pages": 5,
"items": [
{
"user_id": "1",
"username": "carl",
"first_name": "carl",
}
]
}
{
"hasmoredata":true,
"current_page": 1,
"page_size": 10,
"total_pages": 2,
"items": [
{
"course_id": "10",
"course_name": "Math",
"duration": "3h",
}
]
}
I'd like to have two classes which extend an abstract one that collect the common properties. Something like this (in C#):
public abstract class CursorResult
{
[JsonProperty("current_page")]
public int CurrentPage { get; set; }
[JsonProperty("page_size")]
public int PageSize { get; set; }
[JsonProperty("total_pages")]
public int TotalPages { get; set; }
[JsonProperty("hasmoredata")]
public bool HasMoreData{ get; set; }
}
public class UsersList : CursorResult
{
[JsonProperty("items")]
List<User> Users { get; set; }
}
public class CoursesList : CursorResult
{
[JsonProperty("items")]
List<Courses> Courses { get; set; }
}
Now the problem is the fact that i have to write a function that collect the entire result (all the pages of items) and merge those results in one:
private CursorResult GetEntireResult(string apiURL)
{
Cursor c = new Cursor(1, pageSize);
CursorResult result = TryDeserializeCursorResult(CallRestFulAPI(apiURL + c.GetCursorParametersString, Method.GET));
c.Hashcode = result.CursorHashCode;
while (result.HasMoreData)
{
c.CurrentPage += 1;
result.AddItems(TryDeserializeCursorResult(CallRestFulAPI(apiURL + c.ParametersString, Method.GET)));
}
return result;
}
but i don't have any idea on how write the AddItem function in order to add Users or Courses depending on the API result.
Thanks in advance for any help!
Lapo
A couple of things:
With your current code, assuming you're using a default serializer, when you deserialize you're not going to capture 'items' because you're deserializing to CursorResult which doesn't have an 'items' property defined. The serializer doesn't automatically know about derived types. Also I would recommend renaming the UserList and CoursesList classes to UserCursorResult/CourseCursorResult. The classes aren't lists, they contain lists.
Here is code that will discriminate between the json serialized (sub)types :
string yourJsonString = #"{ 'hasmoredata':true,'current_page':
1,'page_size': 20,'total_pages': 5,'items': [{'user_id':
'1','username': 'carl','first_name': 'carl'}]}";
JSchemaGenerator generator = new JSchemaGenerator();
JSchema userSchema = generator.Generate(typeof(UsersList));
JSchema courseSchema = generator.Generate(typeof(CoursesList));
JToken jObject = JObject.Parse(yourJsonString);
if (jObject.IsValid(courseSchema))
{
CoursesList courseList = JsonConvert.DeserializeObject<CoursesList>(yourJsonString);
//do stuff with CourseList
}
else if (jObject.IsValid(userSchema))
{
UsersList userList = JsonConvert.DeserializeObject<UsersList>(yourJsonString);
//do stuff with UsersList
}
I have universal app where I communicate with REST API. Any response from REST I deserialize with DataContractJsonSerializer.
I have a problem with response that contains dictionary. This dictionary I don't need deserialize - my class doesn't contains this dictionary. In Win RT it works, but in Win Phone it doesn't works. I get this error:
The dictionary cannot be deserialized because the member 'key of dictionary' was found more than once in the input.
I don't understand why it works in Win RT and not in Win Phone.
//edit - add sample JSON
{
"ResultType": 0,
"Message": "",
"Exception": null,
"Result": {
"property": 1,
"property2": 2,
"property3": "2015-01-31T13:56:43.5337609+01:00",
"GeneratedQuestions": {
"className": [
{
"innerProperty": 1,
"innerProperty2": 2,
"innerProperty3": "sample",
"innerProperty4": [
{
"prop1": 1,
"prop2": 2,
"prop3": 3,
"prop4": "sample text",
}
]
},
{
"innerProperty": 1,
"innerProperty2": 2,
"innerProperty3": "sample2",
"innerProperty4": []
},
{
"innerProperty": 1,
"innerProperty2": 2,
"innerProperty3": "sample3",
"innerProperty4": []
}
]
}
}
}
code for deserialize json
protected T DeserializeJson<T>(string json)
where T : class
{
T type;
DataContractJsonSerializer jsonSerialized = new DataContractJsonSerializer(typeof(T), new DataContractJsonSerializerSettings { DateTimeFormat = new System.Runtime.Serialization.DateTimeFormat("dd.mm.yyyy HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture) });
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
try
{
ms.Position = 0;
type = jsonSerialized.ReadObject(ms) as T;
}
catch
{
type = default(T);
}
}
return type;
}
Simplified version of Model:
[DataContract]
public class SampleClass
{
[DataMember(Name = "ResultType")
public string ResultType { get; set; }
[DataMember(Name = "Message")
public string Message{ get; set; }
[DataMember(Name = "Exception")
public Exception Exception{ get; set; }
[DataMember(Name = "Result")
public ResultModel Result{ get; set; }
}
public class ResultModel
{
[DataMember(Name = "property")
public int Property { get; set; }
[DataMember(Name = "property2")
public int Property2 { get; set; }
[DataMember(Name = "property3")
public string Property3 { get; set; }
}
I think the error lies in the missing [DataContract]-attribute on ResultModel which tells the serializer to serialize all public members:
Apply the IgnoreDataMemberAttribute attribute to opt-out of the default DataContractSerializer behavior. By default, the DataContractSerializer serializes all publicly visible types. All public read/write properties and fields of the type are serialized. You can change the default behavior by applying the DataContractAttribute and DataMemberAttribute attributes to the types and members
So either put that [DataContract]-attribute on ResultModel or mark GeneratedQuestions as [IgnoreDataMember].
Well the latter is no option for you as you don't have that property in your model. Just to mention that second option.
I am getting this JSON response from a system I am connecting to and trying to figure out the best way to deserialize it into a C# object. I am currently using RestSharp which seems pretty straight forward to use but the format of the JSON is baffling me a bit. Here is the format that its coming in as:
[
{"name": "Tickets:",
"schema": [
{"dataType": "string", "colName": "First", "idx": 0},
{"dataType": "string", "colName": "Second", "idx": 1},
{"dataType": "string", "colName": "Name", "idx": 2}
],
"data": [
["bill", "test", "joe"],
["bill2", "test2", "joe2"],
["bill3", "test3", "joe3"]
]
}
]
Here is my current code:
var url = "http://myUrl:10111";
var client = new RestClient { BaseUrl = url };
var request = new RestRequest { Method = Method.GET, Resource = "/search?fmt=Json", RequestFormat = DataFormat.Json };
request.AddHeader("accept", "application/json");
var response = client.Execute(request);
var wptResponse = new JsonDeserializer().Deserialize<TicketResults>(response);
return wptResponse;
but as stated above I am trying to figure out the correct way to model the TicketResults object to support deserializing this message above.
Ideally I would like something like this:
public class TicketResults
{
public List<Ticket> Tickets {get;set;}
}
public class Ticket
{
public string First {get;set;}
public string Second {get;set;}
public string Name {get;set;}
}
and in this example above would get three entries in the Tickets collection.
Also, is the above JSON format normal as i have never seen this broken out into separate schema and data section (I can see where it might save some space but in this case the messages are not that big)
In Visual Studio 2012 and up and you can go to Edit > Paste Special > Paste JSON as classes. It produces the following code given your example pasted from clipboard.
public class Rootobject
{
public Class1[] Property1 { get; set; }
}
public class Class1
{
public string name { get; set; }
public Schema[] schema { get; set; }
public string[][] data { get; set; }
}
public class Schema
{
public string dataType { get; set; }
public string colName { get; set; }
public int idx { get; set; }
}
string json = File.ReadAllText("json.txt");
Rootobject root = new Rootobject();
root.Property1 = JsonConvert.DeserializeObject<Class1[]>(json);
I agree the json format is quite ... goofy. Here's how to model your dto:
public class JsonDto
{
public string name { get; set; }
public Schema[] schema {get; set;}
public string[][] data { get; set; }
}
public class Schema
{
public string dataType { get; set; }
public string colName { get; set; }
public int idx { get; set; }
}
I was able to get your string (unaltered) to deserialize with JSON.Net like this:
var jsonDto = JsonConvert.DeserializeObject<JsonDto[]>(json);
Let me know if you're still having trouble.
Do you have any control over the structure of the JSON being returned? It's kind of wacky. For some reason the field names and the data is separated out. If the format was a little more sensible like:
[
{
"First": "bill",
"Second": "test",
"Name": "joe"
},
{
"First": "bill2",
"Second": "test2",
"Name": "joe2"
},
]
Then you would have a shot at serializing it to your Ticket class. However, without reworking the JSON structure, which I don't recommend you do, the C# class that you are serializing to will have to match the JSON structure.
I suppose you could come up with an intermediary class to hold the JSON data as it comes to you. Then you could loop over those objets and create instances of the Ticket class out of them. At least that way you end up with a data structure you can work with.