Get the values of dynamic keys from a JSON string - c#

I have this json string and i want to get the 4th line (iValue, sValue) of every record.
My problem here is the keys vary for every record (based on the data type of the value).
Is there any way to do this on C#?
Here is an example:
{ "data": [
{
"pKey": "0",
"Entity": "tableName",
"Attribute": "CID",
"iValue": "13"
},
{
"pKey": "0",
"Entity": "tableName",
"Attribute": "username",
"sValue": "test_user1"
}] }

Here is kind of a big implementation, you will have to implement this for each iValue, fValue, etc however, it speeds up the implementation and usage. First of, here is the usage:
string rawJson = "{\"data\":[{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"CID\",\"iValue\":\"13\"},{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"username\",\"sValue\":\"test_user1\"}]}";
var values = JsonConvert.DeserializeObject<TakeData>(rawJson).Data.Select(v => v.PureData);
Now values contains the list. Here is the usage for accessing each:
foreach (var val in values)
{
if (val is IntData i)
{
int myInt = i.iValue;
// use the rest of the properties
}
else if (val is StrData s)
{
string myStr = s.sValue;
// use the rest of the properties
}
}
And here is the implementation:
class TakeData
{
public List<TakeItAll> Data { get; set; }
}
class TakeItAll
{
public int pKey { get; set; }
public string Entity { get; set; }
public string Attribute { get; set; }
private int _iValue;
public int iValue
{
get => _iValue;
set
{
_iValue = value;
PureData = new IntData { pKey = pKey, Entity = Entity, Attribute = Attribute, iValue = iValue };
}
}
private string _sValue;
public string sValue
{
get => _sValue;
set
{
_sValue = value;
PureData = new StrData { pKey = pKey, Entity = Entity, Attribute = Attribute, sValue = sValue };
}
}
public IPureData PureData { get; private set; }
}
interface IPureData
{
int pKey { get; set; }
string Entity { get; set; }
string Attribute { get; set; }
}
class IntData : IPureData
{
public int pKey { get; set; }
public string Entity { get; set; }
public string Attribute { get; set; }
public int iValue { get; set; }
}
class StrData : IPureData
{
public int pKey { get; set; }
public string Entity { get; set; }
public string Attribute { get; set; }
public string sValue { get; set; }
}
Of course you can use some alternatives as well. Such as using an enum in TakeItAll to keep track of the data type (or a type variable) instead of so many classes. This way However the size of the values object would be larger.
class TakeItAll
{
public int pKey { get; set; }
public string Entity { get; set; }
public string Attribute { get; set; }
private int _iValue;
public int iValue
{
get => _iValue;
set
{
_iValue = value;
ValType = typeof(string);
}
}
private string _sValue;
public string sValue
{
get => _sValue;
set
{
_sValue = value;
ValType = typeof(int);
}
}
public Type ValType { get; private set; }
}

I would deserialize this into an object supporting both types of properties and then by code try parsing either the integer or the string if the integer fails.
If the Attribute value gives you a clue as to which one to look for, you could also use that to prevent having to try parsing the integer every time.
I would not rely on the property being the "fourth" property every time, as I'm assuming this would be external data, where you may not be able to control whether these properties come out in the exact same order every time (now and in the future).

If you don't know the data type then you could use an object to handle it.
It's a good idea to deserialize JSON string to concrete class to avoid string manipulation mistake.
public class Datum
{
public object pKey { get; set; }
public string Entity { get; set; }
public string Attribute { get; set; }
public string iValue { get; set; }
public string sValue { get; set; }
}
public class DataCollection
{
public List<Datum> data { get; set; }
}
public void Test()
{
var str = "{\"data\":[{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"CID\",\"iValue\":\"13\"},{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"username\",\"sValue\":\"test_user1\"}]}";
var list = JsonConvert.DeserializeObject<DataCollection>(str);
var keys = list.data.Select(x => x.pKey).ToList();
}

Another option is to deserialize to dynamic and inspect that:
var json = "{\"data\":[{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"CID\",\"iValue\":\"13\"},{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"username\",\"sValue\":\"test_user1\"}]}";
var result = JsonConvert.DeserializeAnonymousType<dynamic>(json, null);
if (result.data != null)
{
for (var i = 0; i < result.data.Count; i++)
{
if (result.data[i]["iValue"] != null)
// Parse iValue
if (result.data[i]["sValue"] != null)
// Parse sValue
}
}

You could load the Json in a ExpandoObject
var expConverter = new ExpandoObjectConverter();
dynamic objList = JsonConvert.DeserializeObject<List<ExpandoObject>>(json, expConverter);
JSON array to ExpandoObject via JSON.NET
Then once you have loaded it in as a List<ExpandoObject> you may itterate over it as a dictionary.
foreach(var obj in objList)
{
//convert the object to a Dictionary and select the 4th element.
var yourresult = (obj as IDictionary<string, object>).ElementAt(3);
}

My two cents:
Get each object of the array as a KeyValuePair
var json = "your json here";
var root = JsonConvert.DeserializeObject<Root>(json);
foreach (var element in root.Data)
{
//===================================================> Here using object because your value type change. You can change it to string if your value is always wrapped in a string (like "13")
var keyValuePair = element.ToObject<Dictionary<string, object>>();
//here, for each object of the 'data' array, you can check if the desidered property exists
if (keyValuePair.ContainsKey("iValue"))
{
var propertyValue = keyValuePair["iValue"];
}
else if (keyValuePair.ContainsKey("sValue"))
{
var propertyValue = keyValuePair["sValue"];
}
// Or you can check the property name in the desidered position
if (keyValuePair.Keys.ElementAt(3) == "iValue")
{
var propertyValue = keyValuePair["iValue"];
}
else if (keyValuePair.Keys.ElementAt(3) == "sValue")
{
var propertyValue = keyValuePair["sValue"];
}
}
Where Root is
class Root
{
[JsonProperty("data")]
public List<JObject> Data { get; set; }
}
With this solution you can always know which property (iValue or sValue) is specified. On the contrary, if you use a model class which has both property names, you wouldn't know which property is specified when the value is null (unless you use additional properties/classes and a custom JsonConverter).
Edit
As Panagiotis Kanavos reminded me, the JObject class implements IDictionary<string, JToken>. So, inside your foreach you could use:
if (element["iValue"] != null)
{
var propertyValue = element["iValue"].Value<string>();
}
if (element["sValue"] != null)
{
var propertyValue = element["sValue"].Value<string>();
}
// Or you can check the property name in the desidered position
var propName = element.Properties().ElementAt(3).Name;
if (propName == "iValue")
{
var propertyValue = keyValuePair["iValue"].Value<string>();
}
else if (propName == "sValue")
{
var propertyValue = keyValuePair["sValue"].Value<string>();
}
Of course you can optimize this code and check for nulls.

Related

moving property to anther property in class throws exception "System.InvalidCastException: 'Object must implement IConvertible.'"?

what I am trying to find away to move DynamicProperties property to JSONdata property because of that I have this reflection function to do the job when it came to DynamicProperties it throws exception "System.InvalidCastException: 'Object must implement IConvertible.'" can someone help me?
public IHttpActionResult Get(ODataQueryOptions<Client> options)
{
if(queryNew.ElementType == typeof(Client)){}
else //if (queryNew.ElementType.Name == "SelectSome`1")
{
var results = new List<Client>();
try
{
foreach (var item in queryNew)
{
var dict = ((ISelectExpandWrapper)item).ToDictionary();
var model = DictionaryToObject<Client>(dict);
results.Add(model);
}
return Ok(results);
}
catch (Exception)
{
throw;
}
}
private static T DictionaryToObject<T>(IDictionary<string, object> dict) where T : new()
{
T t = new T();
PropertyInfo[] properties = t.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
if (!dict.Any(x => x.Key.Equals(property.Name, StringComparison.InvariantCultureIgnoreCase)))
continue;
KeyValuePair<string, object> item = dict.First(x => x.Key.Equals(property.Name, StringComparison.InvariantCultureIgnoreCase));
Type tPropertyType = t.GetType().GetProperty(property.Name).PropertyType;
Type newT = Nullable.GetUnderlyingType(tPropertyType) ?? tPropertyType;
if(dict.Any(x => x.Key.Equals("DynamicProperties", StringComparison.InvariantCultureIgnoreCase)))
{
object temp = JsonConvert.SerializeObject(item.Value, Formatting.Indented); //Convert.ChangeType(item.Value.ToString(), newT);
t.GetType().GetProperty("JsonData").SetValue(t, temp, null);
}
object newA = Convert.ChangeType(item.Value, newT);
t.GetType().GetProperty(property.Name).SetValue(t, newA, null);
}
return t;
}
client class
public class Client
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string ParticipantId { get; set; }
public DateTime? BirthDate { get; set; }
public int Gender { get; set; }
public byte? Age { get; set; }
public int Status { get; set; }
public string Notes { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime? UpdatedDate { get; set; }
public DateTime? DeletedDate { get; set; }
public bool IsDeleted { get; set; }
public int? DefaultLanguageId { get; set; }
public Guid UserId { get; set; }
public string JsonData { get; set; }
public virtual ICollection<ClientTag> ClientTags { get; private set; }
protected IDictionary<string, object> _dynamicProperties;
public IDictionary<string, object> DynamicProperties
{
get
{
if (_dynamicProperties == null)
{
if (this.JsonData == null)
{
_dynamicProperties = new Dictionary<string, object>();
}
else
{
_dynamicProperties = Mapper.Map<IDictionary<string, object>>(JObject.Parse(this.JsonData));
}
//_dynamicProperties.Source.ListChanged += (sender, e) => { this.AssessmentData = _dynamicProperties.Source.ToString(); };
}
return _dynamicProperties;
}
}
public void UpdateJsonDataFromDynamicProperties()
{
this.JsonData = Mapper.Map<JObject>(_dynamicProperties).ToString();
}
}
When you get an IConvertable error, that means that you have tried to assign one type to another.
for example, if you try to assign textbox to string, you will get an error because you cannot assign object to string, you will have to use Iconvertible.
e.g.:
String.ToString();
Which implicitly implements Iconvertible.
I cannot tell exactly where your code fails, you didn't mention it either, I can just tell you that somewhere in the client methods, you are trying to assign two different types and you just cannot do that.
Use debug on both the server and client class and check where you are trying to assign two different types, then implement the desired Iconvertible, meaning make the needed conversion.
The only problem in your code here, is that you are trying to assign to different types.
My guess, is that this line caused trouble:
if (this.JsonData == null)
{
_dynamicProperties = new Dictionary<string, object>();
}
else
{
_dynamicProperties = Mapper.Map<IDictionary<string, object>>(JObject.Parse(this.JsonData));
}
When you are trying to assign _dynamicProperties into a dictionary object when in fact you declared _dynamicProperties as IDictionary object.
There is a subtle different between Idictionary and dictionary object, they are not of the same type. this is the change need to be done:
if (this.JsonData == null)
{
_dynamicProperties = new IDictionary<string, object>();
}
else
{
_dynamicProperties = Mapper.Map<IDictionary<string, object>>(JObject.Parse(this.JsonData));
}

Get Property Value of Nested Classes is always null

I have following two classes
public class Family
{
public string ChildName { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public Family Child { get; set; }
}
I have an instance of Employee class as follows.
Employee employee = new Employee();
employee.Name = "Ram";
employee.Id = 77;
employee.Child = new Family() { ChildName = "Lava" };
I have a method which gets the property value based on the property name as follows:
public static object GetPropertyValue(object src, string propName)
{
string[] nameParts = propName.Split('.');
if (nameParts.Length == 1)
{
return src.GetType().GetRuntimeProperty(propName).GetValue(src, null);
}
foreach (String part in nameParts)
{
if (src == null) { return null; }
Type type = src.GetType();
PropertyInfo info = type.GetRuntimeProperty(part);
if (info == null)
{ return null; }
src = info.GetValue(src, null);
}
return src;
}
In the above method,when I try to get property value of nested class like
GetPropertyValue(employee, "employee.Child.ChildName")
or
GetPropertyValue(GetPropertyValue(employee, "Family"), "ChildName"
doesn't return any value because type.GetRuntimeProperty(part) is always null.
Is there any way to fix this problem?
You problem lies in this line:
foreach (String part in nameParts)
Because you are iterating over each part of nameParts, you are also iterating over "employee", which of course is not a valid property.
Try either this:
foreach (String part in nameParts.Skip(1))
Or calling the method like this:
GetPropertyValue(employee, "Child.ChildName")
(Notice no "employee.", because you already pass in an employee)
The problem in this case is that when you split the string employee.Child.ChildName, the "employee" is the first part. However, employee is not a property of the source i.e. Employee Class.
Try this:
public class Program
{
public static void Main()
{
Employee employee = new Employee();
employee.Name = "Ram";
employee.Id = 77;
employee.Child = new Family() { ChildName = "Lava" };
GetPropertyValue(employee, "employee.Child.ChildName");
}
public class Family
{
public string ChildName { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public Family Child { get; set; }
}
public static object GetPropertyValue(object src, string propName)
{
string[] nameParts = propName.Split('.');
if (nameParts.Length == 1)
{
return src.GetType().GetRuntimeProperty(propName).GetValue(src, null);
}
nameParts = nameParts.Skip(1).ToArray();
foreach (String part in nameParts)
{
if (src == null) { return null; }
Type type = src.GetType();
PropertyInfo info = type.GetRuntimeProperty(part);
if (info == null)
{ return null; }
src = info.GetValue(src, null);
}
return src;
}
Here, i have skipped the first part of the string i.e. "employee". However, you can solve the problem by passing Child.ChildName
This question is around 2 years old, but I found a another working solution for you question, which is easy to understand. If you initialize the object in calling calss constructor you can use dot(.) notation to assign or read property. Example -
public class Family{
public string ChildName { get; set; }
}
public class Employee{
public int Id { get; set; }
public string Name { get; set; }
public Family Child { get; set; }
public Employee(){
Child = new Family();
}
}
Employee emp = new Employee();
emp.Family.ChildName = "Nested calss attribute value";

Parse the JSON syntax if it is not parsed well

How do I parse the JSON data if it is not well parsed from newtonsoft json. Please refer my below code:
var web_uri = new Uri("www.example.com");
var resp = await client2.GetAsync(web_uri);
var resp_to_str = await resp.Content.ReadAsStringAsync();
var json_obj = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(resp_to_str);
Finally i parsed the JSON. now, it produces as expected.
{
"Sex": "Male",
"category": "A",
"ID": 14,
"created": "2016-03-03",
"Tag": "2340",
"members": [{
"type": "A",
"name": "fam_mem",
"state": "ca",
"Family": {
"myGuardName": "tony",
"details": [{
"address": "ca",
"type": "A"
}]
}
}]
}
**RootObject omyclass = Newtonsoft.Json.JsonConvert.DeserializeObject<RootObject>(json_obj);**
Now i am getting error at the above line:
An exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in System.Linq.Expressions.dll but was not handled in user code
Additional information: The best overloaded method match for 'Newtonsoft.Json.JsonConvert.DeserializeObject(string)' has some invalid arguments
public class Detail
{
public string address { get; set; }
public string type { get; set; }
}
public class Family
{
public string myGuardName { get; set; }
public List<Detail> details { get; set; }
}
public class Member
{
public string type { get; set; }
public string name { get; set; }
public string state { get; set; }
public Family Family { get; set; }
}
public class RootObject
{
public string Sex { get; set; }
public string category { get; set; }
public int ID { get; set; }
public string created { get; set; }
public string Tag { get; set; }
public List<Member> members { get; set; }
}
TextBlock.Text = omyclass
I have updated the question
Hope it helps:
var web_uri = new Uri("www.example.com");
var resp = await client2.GetAsync(web_uri);
var resp_to_str = await resp.Content.ReadAsStringAsync();
RootObject omyclass = JsonConvert.DeserializeObject<RootObject>(resp_to_str); //pass the response string here.
Updated from OP's comment:
textBlock2.Text = omyclass + "----!"; will not work because omyclass is RootObject, not a string
You have to get the info you need and append it to textBlock2:
textBlock2.text = omyclass.Sex + "----!";
UPDATE 2 (OP get string as key value pair):
Usage: textBlock2.text = omyclass + "----!";
Override RootObject.ToString() and use Reflection to get properties & property values
public class RootObject
{
public string Sex { get; set; }
public string category { get; set; }
public int ID { get; set; }
public string created { get; set; }
public string Tag { get; set; }
public List<Member> members { get; set; }
public override string ToString()
{
var values = new List<string>();
foreach (var property in GetType().GetProperties())
{
values.Add(property.Name + ": " + property.GetValue(this));
}
return string.Join(", ", values);
}
}
The problem is that JsonConvert.DeserializeObject<T>(string) expects a string as an input argument -- but you are not passing in a string. You are passing in json_obj which is a dynamic returned by a previous call to deserialize:
var json_obj = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(resp_to_str);
var omyclass = Newtonsoft.Json.JsonConvert.DeserializeObject<RootObject>(json_obj);
What JsonConvert returns from the first dynamic call is in fact a JToken containing a tree of LINQ-to-JSON tokens -- not a string. This causes the RuntimeBinderException upon making the second call.
There is no need to double-deserialize the JSON string in this manner. Just pass the resp_to_str to JsonConvert.DeserializeObject<RootObject>():
var omyclass = Newtonsoft.Json.JsonConvert.DeserializeObject<RootObject>(resp_to_str);
Prototype fiddle.
Update
If you want to see all the fields and properties of the deserialized class in a text box, you could re-serialize it to JSON:
var reserializedJson = JsonConvert.SerializeObject(omyclass, Formatting.Indented);
textBlock2.Text = reserializedJson;
If you do
textBlock2.Text = omyclass + "----!"
You are just showing the ToString() value for your class. And since you have not overridden this method it will just show the class name.
If you don't want to re-serialize you could use the following extension method:
public static class ObjectExtensions
{
public static StringBuilder ToStringWithReflection<T>(this T obj, StringBuilder sb)
{
sb = sb ?? new StringBuilder();
if (obj == null)
return sb;
if (obj is IEnumerable)
{
sb.Append("[");
var first = true;
foreach (var item in ((IEnumerable)obj))
{
if (!first)
sb.Append(",");
sb.Append(item == null ? "" : item.ToString());
first = false;
}
sb.Append("]");
}
else
{
var type = obj.GetType();
var fields = type.GetFields();
var properties = type.GetProperties().Where(p => p.GetIndexParameters().Length == 0 && p.GetGetMethod(true) != null && p.CanRead);
var query = fields
.Select(f => new KeyValuePair<string, object>(f.Name, f.GetValue(obj)))
.Concat(properties
.Select(p => new KeyValuePair<string, object>(p.Name, p.GetValue(obj, null))));
sb.Append("{").Append(obj.GetType().Name).Append(": ");
var first = true;
foreach (var pair in query)
{
if (!first)
sb.Append(", ");
sb.Append(pair.Key).Append(": ");
if (pair.Value is IEnumerable && !(pair.Value is string))
pair.Value.ToStringWithReflection(sb);
else
sb.Append(pair.Value == null ? "null" : pair.Value.ToString());
first = false;
}
sb.Append("}");
}
return sb;
}
public static string ToStringWithReflection<T>(this T obj)
{
return obj.ToStringWithReflection(new StringBuilder()).ToString();
}
}
Then do
textBlock2.Text = omyclass.ToStringWithReflection() + "----!"
Update 2
Or, if you want to include properties from your object hierarchy recursively, you can override the ToString() method of each, like so:
public class Detail
{
public string address { get; set; }
public string type { get; set; }
public override string ToString() { return this.ToStringWithReflection(); }
}
public class Family
{
public string myGuardName { get; set; }
public List<Detail> details { get; set; }
public override string ToString() { return this.ToStringWithReflection(); }
}
public class Member
{
public string type { get; set; }
public string name { get; set; }
public string state { get; set; }
public Family Family { get; set; }
public override string ToString() { return this.ToStringWithReflection(); }
}
public class RootObject
{
public string Sex { get; set; }
public string category { get; set; }
public int ID { get; set; }
public string created { get; set; }
public string Tag { get; set; }
public List<Member> members { get; set; }
public override string ToString() { return this.ToStringWithReflection(); }
}
Then the ToString() output will be:
{RootObject: Sex: Male, category: A, ID: 14, created: 2016-03-03, Tag: 2340, members: [{Member: type: A, name: fam_mem, state: ca, Family: {Family: myGuardName: tony, details: [{Detail: address: ca, type: A}]}}]}
You Can Use a Custom Json formatter and parse it the way you want.It can be implemented at framework level so that all data is parsed according to your requirements.Inherit the custom formatter from MediaTypeFormatter.
Implement the virtual and abstract functions of MediaTypeFormatter.Pasre the data according to your requirements here.
I have implemented the formatter this way and it even parses complex data to match the data that i want.
When serializing complex Json if not deserialized correctly the value becomes null.For deserializing complex Json use JsonMediaTypeFormatter.
My Example
In your config File add
config.Formatters.Clear();
config.Formatters.Insert(0, new JsonNetFormatterDecide()); // My custom formatter
config.Formatters.Insert(1, new JsonMediaTypeFormatter());//Default Formatter
config.MapHttpAttributeRoutes();`
So i used my custom formatter for all get operations.Format the data as i want and sent to client.The problem i faced was while accepting complex Json data(such as Jsonarrays within Jsonobjects). So in my config file i added the JsonMediaTypeFormatter() in index 1 of Config.formatters.
In my custom formatter
public override bool CanReadType(Type type)
{
return false;
}
This makes JsonMediaTypeFormatter which is a very popular formatter to Deserialize complex data to do the deserialization.
The bottomline is that you can use JsonMediaTypeFormatter toDeserialize complex Json Data
It has some predefined functions to deserialize.
By looking at your Json it seems as if Json arrays within Json objects is the reason why your Json is not deserialing correctly
If you plan to write your custom Json formatter ,You can implement at framework level like
public class JsonNetFormatterDecide : MediaTypeFormatter
{
//......
public override bool CanReadType(Type type)
{
return false; //this causes the // project to use the second formatter in the config file ie,JsonMediaTypeFormatter or the //default Json Formatter
}
}

Reflections -Set objects property when the property is in a List<>

I know I can use reflection to set a objects property like this below.
public void SaveContent(string propertyName, string contentToUpdate, string corePageId)
{
var page = Session.Load<CorePage>(corePageId);
Type type = page.GetType();
PropertyInfo prop = type.GetProperty(propertyName);
prop.SetValue(page, contentToUpdate, null);
}
I'm having these classes below:
public class CorePage
{
public string BigHeader { get; set; }
public List<BigLinks> BigLinks { get; set; }
}
public class BigLinks
{
public string TextContent { get; set; }
}
My SaveContent()-method works obviously when the property to set is, for example public string BigHeader { get; set; }
But how can I do this if the the property I want to set is in the property:
public List<BigLinks> BigLinks { get; set; }
If public List<BigLinks> BigLinks { get; set; } is a list of 5 BigLinks objects, how can a set the value of, for example the third objects public string TextContent { get; set; }?
You have to get the property value using reflection and change the desired value like this:
var c = new CorePage() { BigLinks = new List<BigLinks> { new BigLinks { TextContent = "Y"}}};
var r = typeof(CorePage).GetProperty("BigLinks").GetGetMethod().Invoke(c, null) as List<BigLinks>;
r[0].TextContent = "X";
If you don't know the type of list item:
var itemInList = (typeof(CorePage).GetProperty("BigLinks").GetGetMethod().Invoke(c, null) as IList)[0];
itemInList.GetType().GetProperty("TextContent").SetValue(itemInList, "XXX", null);
Another option is casting to dynamic:
var itemInList = (typeof(CorePage).GetProperty("BigLinks").GetGetMethod().Invoke(c, null) as dynamic)[0].TextContent = "XXXTTT";

Remove the null property from object

,I have one class in which I have three properties now what I want to do, if in the object if any one of null or empty then I want to remove it from the object below is my code.
public class TestClass
{
public string Name { get; set; }
public int ID { get; set; }
public DateTime? DateTime { get; set; }
public string Address { get; set; }
}
TestClass t=new TestClass();
t.Address="address";
t.ID=132;
t.Name=string.Empty;
t.DateTime=null;
Now here I want the object of TestClass but in that Name and DateTime property should not be their in the object,
is it possible?
Please help me
There's no such concept as removing a property from an individual object. The type decided which properties are present - not individual objects.
In particular, it will always be valid to have a method like this:
public void ShowDateTime(TestClass t)
{
Console.WriteLine(t.DateTme);
}
That code has no way of knowing whether you've wanted to "remove" the DateTime property from the object that t refers to. If the value is null, it will just get that value - that's fine. But you can't remove the property itself.
If you're listing the properties of an object somewhere, you should do the filtering there, instead.
EDIT: Okay, no you've given us some context:
ok I am using Schemaless database so null and empty value also store space in database that's the reason
So in the code you're using which populates that database, just don't set any fields which corresponds to properties with a null value. That's purely a database population concern - not a matter for the object itself.
(I'd also argue that you should consider how much space you'll really save by doing this. Do you really care that much?)
I was bored and got this in LINQPad
void Main()
{
TestClass t=new TestClass();
t.Address="address";
t.ID=132;
t.Name=string.Empty;
t.DateTime=null;
t.Dump();
var ret = t.FixMeUp();
((object)ret).Dump();
}
public static class ReClasser
{
public static dynamic FixMeUp<T>(this T fixMe)
{
var t = fixMe.GetType();
var returnClass = new ExpandoObject() as IDictionary<string, object>;
foreach(var pr in t.GetProperties())
{
var val = pr.GetValue(fixMe);
if(val is string && string.IsNullOrWhiteSpace(val.ToString()))
{
}
else if(val == null)
{
}
else
{
returnClass.Add(pr.Name, val);
}
}
return returnClass;
}
}
public class TestClass
{
public string Name { get; set; }
public int ID { get; set; }
public DateTime? DateTime { get; set; }
public string Address { get; set; }
}
Hereby a 'slightly' more clear and shorter version of the accepted answer.
/// <returns>A dynamic object with only the filled properties of an object</returns>
public static object ConvertToObjectWithoutPropertiesWithNullValues<T>(this T objectToTransform)
{
var type = objectToTransform.GetType();
var returnClass = new ExpandoObject() as IDictionary<string, object>;
foreach (var propertyInfo in type.GetProperties())
{
var value = propertyInfo.GetValue(objectToTransform);
var valueIsNotAString = !(value is string && !string.IsNullOrWhiteSpace(value.ToString()));
if (valueIsNotAString && value != null)
{
returnClass.Add(propertyInfo.Name, value);
}
}
return returnClass;
}
You could take advantage of the dynamic type:
class Program
{
static void Main(string[] args)
{
List<dynamic> list = new List<dynamic>();
dynamic
t1 = new ExpandoObject(),
t2 = new ExpandoObject();
t1.Address = "address1";
t1.ID = 132;
t2.Address = "address2";
t2.ID = 133;
t2.Name = "someName";
t2.DateTime = DateTime.Now;
list.AddRange(new[] { t1, t2 });
// later in your code
list.Select((obj, index) =>
new { index, obj }).ToList().ForEach(item =>
{
Console.WriteLine("Object #{0}", item.index);
((IDictionary<string, object>)item.obj).ToList()
.ForEach(i =>
{
Console.WriteLine("Property: {0} Value: {1}",
i.Key, i.Value);
});
Console.WriteLine();
});
// or maybe generate JSON
var s = JsonSerializer.Create();
var sb=new StringBuilder();
var w=new StringWriter(sb);
var items = list.Select(item =>
{
sb.Clear();
s.Serialize(w, item);
return sb.ToString();
});
items.ToList().ForEach(json =>
{
Console.WriteLine(json);
});
}
}
May be interfaces will be handy:
public interface IAdressAndId
{
int ID { get; set; }
string Address { get; set; }
}
public interface INameAndDate
{
string Name { get; set; }
DateTime? DateTime { get; set; }
}
public class TestClass : IAdressAndId, INameAndDate
{
public string Name { get; set; }
public int ID { get; set; }
public DateTime? DateTime { get; set; }
public string Address { get; set; }
}
Creating object:
IAdressAndId t = new TestClass()
{
Address = "address",
ID = 132,
Name = string.Empty,
DateTime = null
};
Also u can put your interfaces in separate namespace and make your class declaration as internal. After that create some public factories which will create the instances of your classes.

Categories

Resources