For deserialisation I usually use an object with the same property names as found in the JSon and JsonConvert.DeserializeObject<Des>(jsonstring).
But now I came across this:
{
"id": 0815,
"name": "whatever"
"addedInfo": {
"thisisinteresting": 4711,
"id_str": "2336"
}
}
How can I tell JSon.Net to pull the 'thisisinteresting' part of the sub category into a class like:
class Des
{
int id;
string name;
int thisisinteresting;
}
The trivial way would be to actually model your class to the JSON structure:
public class AddedInfo
{
public int thisisinteresting { get; set; }
public string id_str { get; set; }
}
public class RootObject
{
public int id { get; set; }
public string name { get; set; }
public AddedInfo addedInfo { get; set; }
}
Then add a property to the RootObject to emit the property:
public class RootObject
{
public int id { get; set; }
public string name { get; set; }
public AddedInfo addedInfo { get; set; }
[JsonIgnore]
public int thisisinteresting { get { return addedInfo.thisisinteresting; } }
}
There are alternatives like creating a custom serializer or using JObject and deserialize the structure yourself, but I won't go into that. If you need to parse the JSON anyway, the price to deserialize it entirely is small.
Related
I'm trying to deserialize the following JSON:
{
"runtimeOptions": {
"tfm": "net6.0",
"frameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "6.0.0"
},
{
"name": "Microsoft.WindowsDesktop.App",
"version": "6.0.0"
}
],
"configProperties": {
"MaxTargets" : "1024"
}
}
}
My model class looks like:
public class OptionalInclude
{
[JsonProperty("configProperties")]
public string ConfigProperties { get; set; }
[JsonProperty("tfm")]
public string Tfm { get; set; }
[JsonProperty("frameworks")]
public IList<Dictionary<string, string>> Frameworks { get; set; }
}
I'm trying to deserialize in this way, but I need the MaxTargets value from the JSON:
var optionalIncludesConfig = File.ReadAllText(configPath);
result = (JObject)JsonConvert.DeserializeObject(optionalIncludesConfig);
var optionIncludes = new List<OptionalInclude>();
optionIncludes = result.ToObject<List<OptionalInclude>>();
configProperties in your JSON is really a object, and you are forgetting about your top level object runtimeOptions. You should change your model to be:
public class TopLevel //this should be something more descriptive - only used as an example
{
[JsonProperty("runtimeOptions")]
public RuntimeOptions runtimeOptions { get; set; }
}
public class RuntimeOptions
{
[JsonProperty("configProperties")]
public ConfigProperties ConfigProperties { get; set; }
[JsonProperty("tfm")]
public string Tfm { get; set; }
[JsonProperty("frameworks")]
public IList<Dictionary<string, string>> Frameworks { get; set; }
}
public class ConfigProperties
{
[JsonProperty("maxTargets")]
public string MaxTargets { get; set; }
}
Then to deserialize, you should be able to deserialize into an TopLevel object and retrieve configProperties to get your MaxTargets:
var optionalIncludesConfig = File.ReadAllText(configPath);
var result = JsonConvert.DeserializeObject<TopLevel>(optionalIncludesConfig);
string maxTargets = result.runtimeOptions.ConfigProperties.MaxTargets;
It should look something like this as seen in this HttpPost example (where the post body is the JSON found in your question):
Firstly, the ConfigProperties property on OptionalInclude is not a string type in the JSON, it is an object, so you should hard-type it like this:
public class ConfigProperties
{
[JsonProperty("MaxTargets")]
public string MaxTargets { get; set; }
}
Secondly, I think the frameworks in your JSON would make more sense to have a Framework class like this:
public class Framework
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("version")]
public string Version { get; set; }
}
and use an List<Framework> instead. Lastly, you either need a class like this:
public class SomeClassName
{
[JsonProperty("runtimeOptions")]
public OptionalInclude RuntimeOptions { get; set; }
}
and then deserialize into that, or you need to parse the JSON into a JObject, navigate it into the first layer of the object (the "runtimeOptions" object) and then parse that value into OptionalInclude (I'd just hard-type it, personally).
Your resulting OptionalInclude class should end up like this:
public class OptionalInclude
{
[JsonProperty("configProperties")]
public ConfigProperties ConfigProperties { get; set; }
[JsonProperty("tfm")]
public string Tfm { get; set; }
[JsonProperty("frameworks")]
public List<Framework> Frameworks { get; set; }
}
Then you should be able to deserialize it like this:
var jsonString = File.ReadAllText(configPath);
var result = JsonConvert.DeserializeObject<SomeClassName>(jsonString);
Note specifically that you can DeserializeObject and use a specific object type instead of casting to a JObject.
Here is a .NET Fiddle of the full code.
Approach 1
You can access attributes of a JObject with the [ ] operator, for example assuming json is a JObject containing the json you provided:
var maxTargets = json["runtimeOptions"]["configProperties"]["MaxTargets"].ToString();
I find this approach fast, but it's easy to mess up with null tokens and such. Also, this isn't a very polished soluton.
Approach 2
I personally like to have classes for nested jsons, so for example:
public class ConfigProperties {
[JsonProperty("MaxTargets")]
public string MaxTargets { get; set; }
}
and then add it to your outer class:
public class OptionalInclude {
// other stuff
public ConfigProperties Properties { get; set; }
}
then you will be able do deserialize it. However, I see you are using a List of OptionalInclude. Assuming that you are cycling through such list:
foreach (var item in optionalIncludes) {
var maxTargets = item.Properties.MaxTargets;
// your logic
}
configProperties is not an enumerable (list)
You can make:
public class Configuration
{
[JsonProperty("runtimeOptions")]
public RuntimeOptions RuntimeOptions { get; set; }
}
public class RuntimeOptions
{
[JsonProperty("tfm")]
public string Tfm { get; set; }
[JsonProperty("frameworks")]
public List<Framework> Frameworks { get; set; }
[JsonProperty("configProperties")]
public ConfigProperties ConfigProperties { get; set; }
}
public class Framework
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("version")]
public string Version { get; set; }
}
public class ConfigProperties
{
[JsonProperty("MaxTargets")]
public string MaxTargets { get; set; }
}
Then you code:
var configuration = File.ReadAllText(configPath);
var result = JsonSerializer.Deserialize<Configuration>(configuration);
string optionInclude = result.RuntimeOptions.ConfigProperties.MaxTargets;
I'm querying an external service and wanted to deserialize the response into a customer object but the issue is response for each customer may be different. some customer may have Sales entity in the response and few may have Marketing.
The json property for sales entity is SalesId and for marketing is MarketingId. Can you advise whether the model I use to store result is correct or any improvement ? If so, how would I deserialize the response without knowing the correct json property ?
For Customer 66666
{
"customerId": "66666",
"customerName": "test1234",
"dependentEntity": [
{
"SalesId": "3433434",
"SalesPersonName": "343434",
"SaleSource": "StorePurchase"
}
]
}
For Customer 5555
{
"customerId": "55555",
"customerName": "test2",
"dependentEntity": [
{
"MarketingId": "3433434",
"MarketingAppName": "343434",
"MarketingSource": "Online"
}
]
}
Here is the Model I'm thinking but not sure the correct one
public class Customer
{
public string customerId { get; set; }
public string customerName { get; set; }
public IList<T> dependentList { get; set; }
}
public class Dependent
{
[JsonProperty("Id")]
public string Id { get; set; }
public string Name { get; set; }
public string Source { get; set; }
}
You could probably try something like the following one:
public class DependentEntity
{
[JsonProperty("SalesId")]
public string SalesId { get; set; }
[JsonProperty("SalesPersonName")]
public string SalesPersonName { get; set; }
[JsonProperty("SaleSource")]
public string SaleSource { get; set; }
[JsonProperty("MarketingId")]
public string MarketingId { get; set; }
[JsonProperty("MarketingAppName")]
public string MarketingAppName { get; set; }
[JsonProperty("MarketingSource")]
public string MarketingSource { get; set; }
}
public class Customer
{
[JsonProperty("customerId")]
public string CustomerId { get; set; }
[JsonProperty("customerName")]
public string CustomerName { get; set; }
[JsonProperty("dependentEntity")]
public IList<DependentEntity> DependentEntity { get; set; }
}
We have a type for DependentEntity that has both the attributes of Marketing and Sales object. After parsing your input, you could create a logic (checking the attributes) based on which you could check if a DependentEntity is a Marketing or a Sales object.
The above classes was generated using, jsonutils.
If we can assume that the dependentEntity contains only a single type of objects then you can use json.net's schema to perform branching based on the matching schema.
So, lets suppose you have these dependent entity definitions:
public class DependentMarket
{
public string MarketingId { get; set; }
public string MarketingAppName { get; set; }
public string MarketingSource { get; set; }
}
public class DependentSales
{
public string SalesId { get; set; }
public string SalesPersonName { get; set; }
[JsonProperty("SaleSource")]
public string SalesSource { get; set; }
}
...
Then you can use these classes to generate json schemas dynamically:
private static JSchema marketSchema;
private static JSchema salesSchema;
//...
var generator = new JSchemaGenerator();
marketSchema = generator.Generate(typeof(DependentMarket));
salesSchema = generator.Generate(typeof(DependentSales));
And finally you can do the branching like this:
var json = "...";
var semiParsedJson = JObject.Parse(json);
JArray dependentEntities = (JArray)semiParsedJson["dependentEntity"];
JObject probeEntity = (JObject)dependentEntities.First();
if (probeEntity.IsValid(marketSchema))
{
var marketEntities = dependentEntities.ToObject<List<DependentMarket>>();
...
}
else if (probeEntity.IsValid(salesSchema))
{
var salesEntities = dependentEntities.ToObject<List<DependentSales>>();
...
}
else if ...
else
{
throw new NotSupportedException("The provided json format is not supported");
}
JavaScriptSerializer jsSerializer = new JavaScriptSerializer();
// Deserialize the response to get an array of CUSTOM Cases
var reportsList = jsSerializer.Deserialize<SfdcObjects.SfdcCollection<SfdcObjects.Assets>>(HttpUtility.UrlDecode(response));
throws an exception:
Error: System.InvalidOperationException: Type 'SalesforceDataQueryComponent.Utils.SfdcObjects+SfdcCollection`1[
[SalesforceDataQueryComponent.Utils.SfdcObjects+Assets, SalesforceDataQueryComponent, Version=1.2.0.0, Culture=neutral]]'
is not supported for deserialization of an array.
I can not figure it out the issue:
Objects:
namespace SalesforceDataQueryComponent.Utils
{
class SfdcObjects
{
// Used for Authentication
public class TokenResponse
{
public string id { get; set; }
public string issued_at { get; set; }
public string refresh_token { get; set; }
public string instance_url { get; set; }
public string signature { get; set; }
public string access_token { get; set; }
}
// All classes shown next are used to parse the HttpGet Response
public class SfdcCollection<T>
{
public bool Done { get; set; }
public int Size { get; set; }
public string NextRecordsUrl { get; set; }
public List<T> Records { get; set; }
}
public class SfdcAttributes
{
public string Type { get; set; }
public string Url { get; set; }
}
public class Accounts : Account
{
public SfdcAttributes Attributes { get; set; }
}
public class Assets : Asset
{
public SfdcAttributes Attributes { get; set; }
}
public class CustomAssets : Assets
{
public string StringInstallDate { get; set; }
}
public class Users : User
{
public SfdcAttributes Attributes { get; set; }
}
public class CustomCase : Case
{
public string StringCreatedDate { get; set; }
}
public class CustomCases : CustomCase
{
public SfdcAttributes Attributes { get; set; }
}
}
}
You do not include your response JSON in your question, however from the error message, your problem must be that the root JSON container in your response is an array. A JSON array, according to the JSON standard, looks like this:
[value1, value2, ..., valueN]
JSON serializers map types that implement ICollection or IEnumerable from and to JSON arrays.
Your root object SfdcCollection<T>, however, is NOT a collection or enumerable, despite its name. Instead it's a non-enumerable generic POCO:
public class SfdcCollection<T> // No IEnumerable<T>
{
public bool Done { get; set; }
public int Size { get; set; }
public string NextRecordsUrl { get; set; }
public List<T> Records { get; set; }
}
Thus a serializer will map this to a JSON object (which is a set of key/value pairs and looks like {"name1" : value1, "name2" : value2, ..., "nameN" : valueN }) instead of an array.
You need to update your data model to the JSON you are actually receiving. Try uploading your JSON to http://json2csharp.com/, it will automatically generate classes for you.
If you must use the classes in your question, you could ask another question about how to map the JSON you are actually receiving onto your required classes, using your desired serializer (e.g. Json.NET, DataContractJsonSerializer, JavaScriptSerializer, or etc.)
I have a simple JSON like this:
{
"id": 123,
"name": "BaseName",
"variation": { "name": "VariationName" }
}
Is there a simple way to map it with JSON.NET deserialization to:
class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string VariationName { get; set; }
}
I can probably do it with a custom converter, but I hoped there would be a simpler way by annotating the class with attributes which would give instructions to deserialize the variation object just using the one property.
You could set up a class for variation and make VariationName a get-only property
class Product
{
public int Id { get; set; }
public string Name { get; set; }
public Variation variation { get; set; }
public string VariationName { get { return variation.VariationName; } }
}
class variation
{
public string name { get; set; }
}
In short, I have a client Windows Forms app that receives a Json string from an API in the following form:
string textResult = "{"Data":[{"ID":"G0000013","M_CurBalanceOutstanding":52408.5}],"DataDetail":[{"ErrorDate":"\/Date(1410179960809+0200)\/","ErrorID":1,"ErrorInfo":"Success"}]}"
or formatted via http://www.jsoneditoronline.org/
{
"Data": [
{
"ID": "G0000013",
"M_CurBalanceOutstanding": 52408.5
}
],
"DataDetail": [
{
"ErrorDate": "/Date(1410164281557+0200)/",
"ErrorID": 1,
"ErrorInfo": "Success"
}
]
}
I am trying to de-serialize it like this:
var deserializer = new JavaScriptSerializer();
List<MatterDetailBalOutstanding> results = deserializer.Deserialize<List<MatterDetailBalOutstanding>>(textResult);
where textresult is my JSon string.
I have the following classes:
[DataContract]
class MatterDetailBalOutstanding
{
[DataMember]
public string ID { get; set; }
[DataMember]
public decimal M_CurBalanceOutstanding { get; set; }
[DataMember]
public List<MatterReturnStatusDetails> ErrorData;
public MatterDetailBalOutstanding(string _ID, decimal _M_CurBalanceOutstanding, List<MatterReturnStatusDetails> _ErrorData)
{
ID = _ID;
M_CurBalanceOutstanding = _M_CurBalanceOutstanding;
ErrorData = _ErrorData;
}
}
and:
[DataContract]
class MatterReturnStatusDetails
{
[DataMember]
public int ID { get; set; }
[DataMember]
public string Info { get; set; }
[DataMember]
public DateTime Date { get; set; }
public MatterReturnStatusDetails(int _ID, string _Info, DateTime _Date)
{
ID = _ID;
Info = _Info;
Date = _Date;
}
}
I just cannot get it to work? To my understanding it is possible to de-serialize a string containing two JSon arrays. I have read a ton of threads, and a lot of them suggest using another serializer. I have to go with JavaScriptSerializer though. Please could someone help with this? What am I doing wrong? Where am I missing something?
Update 1:
When I try:
MatterDetailBalOutstanding results = deserializer.Deserialize<MatterDetailBalOutstanding>(textResult);
I get the below error:
No parameterless constructor defined for type of 'ConsumeTestWCFApp.ConsumeTestWCFApp+MatterDetailBalOutstanding'.
You can use json2csharp to assist you in generating classes suitable for mapping your JSON. Here is the result :
public class Datum
{
public string ID { get; set; }
public double M_CurBalanceOutstanding { get; set; }
}
public class DataDetail
{
public DateTime ErrorDate { get; set; }
public int ErrorID { get; set; }
public string ErrorInfo { get; set; }
}
public class RootObject
{
public List<Datum> Data { get; set; }
public List<DataDetail> DataDetail { get; set; }
}
Then you can annotate and modify the generated classes further as necessary and use it in deserialization :
var result = deserializer.Deserialize<RootObject>(textResult);
This problem:
No parameterless constructor defined for type of
'ConsumeTestWCFApp.ConsumeTestWCFApp+MatterDetailBalOutstanding'.
occurs because your serialized classes do not have a default constructor. By creating a specific constructor like this:
class MatterDetailBalOutstanding
{
public MatterDetailBalOutstanding(string _ID, decimal _M_CurBalanceOutstanding, List<MatterReturnStatusDetails> _ErrorData)
{
...
}
}
you do not get a default constructor and have to add one yourself:
class MatterDetailBalOutstanding
{
public MatterDetailBalOutstanding(string _ID, decimal _M_CurBalanceOutstanding, List<MatterReturnStatusDetails> _ErrorData)
{
...
}
public MatterDetailBalOutstanding()
{
...
}
}
This may not be you biggest problem now, but I didn't see anyone answer that part of the question.