How to ignore empty array elements with NewtonSoft library - c#

I have the following code :
public City[] Cities { get; set; }
In City class, I have two properties
public string Name { get; set; }
public string Code { get; set; }
When a request comes that has this Cities field empty I would like to hide this with Newtonsoft.
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public City[] Cities { get; set; }
But this code I have tried does not work because Cities is not null, but empty and the request always has the two properties in this array.
How should I use Newtonsoft in this case? Is there any additional check for the objects here needed?

You should use Conditional Property Serialization in JSON.NET. I think you are going to ignore this property if it's empty or null so, inside the class contains Cities properties add this method:
// ignore a property on a condtion
public bool ShouldSerializeCities() => Cities != null && Cities.Count > 0;
Update 1:
As #DavidG mentioned the workaround above won't ignore string fields (Name and Code) if they are null or empty. For making that happen you need to make use of DefaultValue:
Define JsonConvert settings like this:
var settings = new JsonSerializerSettings {
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore
};
Use DefaultValue attribute over your desiarred field/properties:
public class City
{
[DefaultValue("")]
public string Name
{
get;
set;
}
[DefaultValue("")]
public string Code
{
get;
set;
}
}
Serialize your object with the settings you created above:
JsonConvert.SerializeObject(obj, settings) ;
For example, if your object looks like this:
var obj = new Foo{
Cities = new [] {
new City() {Name = "A", Code = ""}
, new City() {Name = "B", Code = "C"}
, new City(){Name = "", Code = ""}
}
};
The result will be:
{
"Cities": [
{
"Name": "A"
},
{
"Name": "B",
"Code": "C"
},
{}
]
}
I created a project on .NET Fiddle to see how it works.
If you don't like creating new settings, you still can use ShuldSerializeMemberName inside your City class:
public class City
{
public string Name{get;set;}
public bool ShouldSerializeName() => !string.IsNullOrEmpty(Name);
}

Related

Want to create one list with one Id and multiple ProviderId values like

I want to create one list with one Id and multiple ProviderId values like
Id ProviderId
1 "val1"
1 "val2"
I have a model class as per below :
public class Model
{
[JsonProperty]
public string Id { get; set; }
[JsonProperty]
public List<PrefDictionary> ProviderId { get; set; }
}
Now I have created one list as per below :
List<PrefDictionary> PrefData = data.Select(p => new PrefDictionary()
{
_1 = p.PrefDictionary._1,
_2 = p.PrefDictionary._2
}).ToList();
Here in data I am getting whole json format data and one node is like this below for PrefDictionary.
data = {
Id : 1,
PrefDictionary{
1: "Val1",
2: "Val2"
}}
Now then I have created one more list as per below :
List<Model> PP = data.Select(p => new Model()
{
Id = p.Id,
ProviderId = PrefData.Select(x => new PrefDictionary()
{
_1 = x._1,
_2 = x._2
}).ToList(),
}).ToList();
}
But the problem is I am getting empty in second column list in first I am getting Id but not getting any value in second column.
I'm not 100% I understand JSON format you are expecting. However, if you want a list of providers - each with a list of preferences - then I think you'll need another class to get this to serialize properly:
public class Model
{
[JsonProperty]
public List<Providers> providers { get; set; }
}
public class Providers
{
[JsonProperty]
public string Id { get; set; }
[JsonProperty]
public List<PrefDictionary> ProviderId { get; set; }
}
This should give you the JSON output:
{
"Providers": [
{
"ID": 1,
"PrefDictionary": [
{
"Val1": "",
"Val2": ""
}
]
},
{
"ID": 2,
"PrefDictionary": [
{
"Val1": "",
"Val2": ""
}
]
}
]
}
I suspect the problem could be the way how you deserialize your data object.
Based on your code, I've created the result that contains the PrefDirectonary.
.
However, one thing I did for the data variable was to make an object like this,
PrefDictionary pd = new PrefDictionary
{
_1 = "Value1",
_2 = "Value2"
};
List<TestObject> data = new List<TestObject>() {
new TestObject
{
Id = "1",
PrefDictionary = pd
}
};
It will be helpful to show us how you deserialize the JSON data object.

Remove property name from the response

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.

Deserialize JSON array with unknown keys inside JSON object to a generic property - C#

Finding the right title for this problem was kinda hard so I'll try to explain the problem a bit better below.
I am making a call to an API which returns the following JSON object:
{{
"id": "jsonrpc",
"jsonrpc": "2.0",
"result": {
"result": [
{
"AccountId": 285929,
"Flags": [
"Managed_Obsolete"
],
"PartnerId": 73560,
"Settings": [
{
"AN": "company_1"
},
{
"CD": "1435323320"
},
{
"ED": "2147483647"
},
{
"OS": "Windows Server 2012 R2 Standard Edition (9600), 64-bit"
},
{
"OT": "2"
},
{
"T3": "1085792125772"
},
{
"US": "958222150780"
},
{
"YS": "100"
}
]
},
{
"AccountId": 610474,
"Flags": null,
"PartnerId": 249262,
"Settings": [
{
"AN": "company_2"
},
{
"CD": "1522143635"
},
{
"ED": "2147483647"
},
{
"OS": "Windows 7 Professional Service Pack 1 (7601), 64-bit"
},
{
"OT": "2"
},
{
"T3": "598346102236"
},
{
"US": "758149148249"
},
{
"YS": "100"
}
]
},
],
"totalStatistics": null
},
}}
In above result I listed only the first 2 accounts (total of 80+ accounts normally).
Deserializing the object works fine, I am putting the JSON object fields inside my C# model (list).
The problem however is that I can't get the (inner) Settings array properly in my model. The settings array keys are unknown, I define these keys when I call the API:
JObject requestObject = new JObject();
requestObject.Add(new JProperty("id", "jsonrpc"));
requestObject.Add(new JProperty("jsonrpc", "2.0"));
requestObject.Add(new JProperty("method", "myMethod"));
requestObject.Add(new JProperty("visa", someID));
requestObject.Add(new JProperty("params",
new JObject(
new JProperty("query", new JObject(
new JProperty("PartnerId", partnerId),
new JProperty("StartRecordNumber", 0),
new JProperty("RecordsCount", 9999999),
new JProperty("Columns", new JArray("AR", "AN", "US", "T3", "OT", "OS", "YS"))
)),
new JProperty("timeslice", unixDate),
new JProperty("totalStatistics", "*")
))
);
In above call I define the keys for the Settings array, this could however also be just one key or more. For this reason I want to make my Settings property in my C# model generic (I don't want to list all the possible key names because this are over 100 keys).
What I had so far:
List<EnumerateAccountHistoryStatisticsResult> resultList = new List<EnumerateAccountHistoryStatisticsResult>();
var result = JsonConvert.DeserializeObject<JObject>(streamreader.ReadToEnd());
dynamic innerResult = result["result"]["result"];
foreach (var obj in innerResult)
{
resultList.Add(
new EnumerateAccountHistoryStatisticsResult
{
AccountId = obj.AccountId,
Flags = obj.Flags.ToObject<IEnumerable<string>>(),
PartnerId = obj.PartnerId,
Settings = obj.Settings.ToObject<List<ColumnSettingsResult>>(),
});
}
The EnumerateAccountHistoryStatisticsResult Model:
public class EnumerateAccountHistoryStatisticsResult
{
public int AccountId { get; set; }
public IEnumerable<string> Flags { get; set; }
public int PartnerId { get; set; }
public List<ColumnSettingsResult> Settings { get; set; }
}
The ColumnSettingsResult model:
public class ColumnSettingsResult
{
public string AR { get; set; }
public string AN { get; set; }
public string US { get; set; }
public string T3 { get; set; }
public string OT { get; set; }
public string OS { get; set; }
public string YS { get; set; }
// and list all other columns...
}
With above models I would need to list all the possible columns which are over 100 properties, besides that the result of the Settings list is not logical because I get all the property values but for each different key I get null values:
The ColumnSettingsResult model should more be something like:
public class ColumnSettingsResult
{
public string ColumnName { get; set; }
public string ColumnValue { get; set; }
}
I cant get the key and value inside these two properties though without defining the key name inside the model..
I already tried several things without result (links below as reference).
Anyone that can get me in the right direction?
C# deserialize Json unknown keys
Convert JObject into Dictionary<string, object>. Is it possible?
Convert Newtonsoft.Json.Linq.JArray to a list of specific object type
Try making Settings of type Dictionary<string,string> (or List<KeyValuePair<string,string>> if Dictionary doesn't give you what you want.
public class MyJsonObject
{
public string id { get; set; }
public string jsonrpc { get; set; }
public Result result { get; set; }
public class Result2
{
public int AccountId { get; set; }
public List<string> Flags { get; set; }
public int PartnerId { get; set; }
public Dictionary<string,string> Settings { get; set; } //or List<KeyValuePair<string,string>>
}
public class Result
{
public List<Result2> result { get; set; }
public object totalStatistics { get; set; }
}
}
Then JsonConvert.DerserializeObject<MyJsonObject>(jsonString);

Convert Json property based on property value

I would like to deserialize a json to object. The json is like below. But one property value maybe string or array, does anyone know how to handle this?
{
"name": "123", //Name
"properties": [
{
"propertyId": "Subject", // property id
"value": [
{
"entityId": "math", //entity id
"entityTypeId": "MATH" //entity type id
}
]
},
{
"propertyId": "Description",
"value": "Hello World."
}
]
}
The class is like below.
//The object
public class Content
{
public Content()
{
//Properties is List.
Properties = new List<Property>();
}
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "properties")]
public List<Property> Properties { get; set; }
}
public class Property
{
public Property()
{
Value = new List<Value>();
}
[JsonProperty(PropertyName = "propertyId")]
public string PropertyDefId { get; set; }
//Actually this property value can also be string, that's the problem.
[JsonProperty(PropertyName = "value")]
public List<Value> Value { get; set; }
}
//Value object
public class Value
{
//Have to write comments.
[JsonProperty(PropertyName = "entityId")]
public string EntityId { get; set; }
//Have to write comments.
[JsonProperty(PropertyName = "entityTypeId")]
public string EntityTypeId { get; set; }
}
I've done this in Java with Gson liblary and it was like
JsonObject field = parser.parse(json).getElementAsJSONObject();
if (field.isPrimitive()) {
String text = field.asString();
} else if (field.isArray()) {
JSONArray array = field.asArray();
}
I wrote this code from my memory so not 100% reliable. I don't know any solution for C# though.
$.parseJSON will convert your string to the correct object even if the property type is different for two different properties.
http://jsfiddle.net/mdanielc/e0acsyp1/2/
var jsonString = '{"name": "123","properties": [{"propertyId": "Subject","value": [{"entityId":"math","entityTypeId": "MATH" }]},{"propertyId": "Description","value": "Hello World."}]}';
var jsonobj = $.parseJSON(jsonString);
alert(jsonobj.properties[0].value[0].entityId);
alert(jsonobj.properties[1].value);
});

Add array to a list of objects

this is the class to create my json object
public class RootObject
{
public string name { get; set; }
public string id { get; set; }
public string school { get; set; }
public List<object> details{ get; set; }
}
this is the way I'm creating a new object
var obj = new RootObject();
obj.name= "test";
obj.id = "null";
obj.school = "something else";
Then I am serializing the object using JavaScriptSerializer (this is working fine)
I need to add an array to this list of object "details" to get something like this:
{
"name ":"test",
"id":null,
"school ":"something else",
"details":["details1","detail2"]
}
I tried to add as string or element by element but without success. How can I solve this?
I strongly suggest you just use Json.NET:
obj.details = new List<object>
{
"details1", "details2"
};
Then, JsonConvert.SerializeObject(obj, Formatting.Indented) gives:
{
"name": "test",
"id": "null",
"school": "something else",
"details": [
"details1",
"details2"
]
}
You need to initialize the List:
obj.details = new List<object>();
Then to add data:
obj.details.Add("Details1");
obj.details.Add("Details2");

Categories