Convert class to dynamic and add properties - c#

I have a class MyClass. I would like to convert this to a dynamic object so I can add a property.
This is what I had hoped for:
dynamic dto = Factory.Create(id);
dto.newProperty = "123";
I get the error:
WEB.Models.MyClass does not contain a definition for 'newProperty'
Is that not possible?

The following has worked for me in the past:
It allows you to convert any object to an Expando object.
public static dynamic ToDynamic<T>(this T obj)
{
IDictionary<string, object> expando = new ExpandoObject();
foreach (var propertyInfo in typeof(T).GetProperties())
{
var currentValue = propertyInfo.GetValue(obj);
expando.Add(propertyInfo.Name, currentValue);
}
return expando as ExpandoObject;
}
Based on: http://geekswithblogs.net/Nettuce/archive/2012/06/02/convert-dynamic-to-type-and-convert-type-to-dynamic.aspx

As my object has JSON specific naming, I came up with this as an alternative:
public static dynamic ToDynamic(this object obj)
{
var json = JsonConvert.SerializeObject(obj);
return JsonConvert.DeserializeObject(json, typeof(ExpandoObject));
}
For me the results worked great:
Model:
public partial class Settings
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("runTime")]
public TimeSpan RunTime { get; set; }
[JsonProperty("retryInterval")]
public TimeSpan RetryInterval { get; set; }
[JsonProperty("retryCutoffTime")]
public TimeSpan RetryCutoffTime { get; set; }
[JsonProperty("cjisUrl")]
public string CjisUrl { get; set; }
[JsonProperty("cjisUserName")]
public string CjisUserName { get; set; }
[JsonIgnore]
public string CjisPassword { get; set; }
[JsonProperty("importDirectory")]
public string ImportDirectory { get; set; }
[JsonProperty("exportDirectory")]
public string ExportDirectory { get; set; }
[JsonProperty("exportFilename")]
public string ExportFilename { get; set; }
[JsonProperty("jMShareDirectory")]
public string JMShareDirectory { get; set; }
[JsonIgnore]
public string Database { get; set; }
}
I used it in this manner:
private static dynamic DynamicSettings(Settings settings)
{
var settingsDyn = settings.ToDynamic();
if (settingsDyn == null)
return settings;
settingsDyn.guid = Guid.NewGuid();
return settingsDyn;
}
And received this as a result:
{
"id": 1,
"runTime": "07:00:00",
"retryInterval": "00:05:00",
"retryCutoffTime": "09:00:00",
"cjisUrl": "xxxxxx",
"cjisUserName": "xxxxx",
"importDirectory": "import",
"exportDirectory": "output",
"exportFilename": "xxxx.xml",
"jMShareDirectory": "xxxxxxxx",
"guid": "210d936e-4b93-43dc-9866-4bbad4abd7e7"
}
I don't know about speed, I mean it is serializing and deserializing, but for my use it has been great. A lot of flexability like hiding properties with JsonIgnore.
Note: xxxxx above is redaction. :)

You cannot add members to class instances on the fly.
But you can use ExpandoObject. Use factory to create new one and initialize it with properties which you have in MyClass:
public static ExpandoObject Create(int id)
{
dynamic obj = new ExpandoObject();
obj.Id = id;
obj.CreatedAt = DateTime.Now;
// etc
return obj;
}
Then you can add new members:
dynamic dto = Factory.Create(id);
dto.newProperty = "123";

You can't add properties to types at runtime. However, there is an exception which is: ExpandoObject. So if you need to add properties at runtime you should use ExpandoObject, other types don't support this.

Just to add up my experience, if you are using JSON.NET, then below might be one of the solution:
var obj....//let obj any object
ExpandoObject expandoObject= JsonConvert.DeserializeObject<ExpandoObject>(JsonConvert.SerializeObject(obj));
Not tested performances etc.. but works.

Related

Create object of type specified in a string

I have a bunch of classes generated by EF that are simple tables and have similar structures:
public class Contact
{
public int ID { get; set; }
public string Description { get; set; }
}
public class Member
{
public int ID { get; set; }
public string Description { get; set; }
}
I've also got a method for returning an object of a specified type:
public T GetInstance<T>(string type)
{
return (T)Activator.CreateInstance(Type.GetType(type));
}
What I want to do is something like this:
public ActionResult GetAll(string ClassType) // ClassType will be the name of one of the classes above
{
Object LookupType = GetInstance<Object>(ClassType);
List<LookupType> AllList = new List<LookupType>();
AllList = repo.GetAll<LookupType>().ToList<LookupType>(); // some generic method that will return a list;
}
This makes the compiler mad because I'm using a variable (LookupType) rather than a true type to build the list. However, neither of these work either:
List<LookupType.GetType()> Items = new List<LookupType.GetType()>();
List<typeof(LookupType)> Items = new List<typeof(LookupType)>();
Both cause an error - "Using generic type List requires 1 type argument".
Is there a proper way to do this? Is there a way to convert ClassType directly to a type without first making it an object (from which I hope to derive the type)?
Try using the CreateInstance method
SomeObject someObject = new SomeObject();
Type type = someObject.GetType();
Type listType = typeof(List<>).MakeGenericType(new [] { type });
IList list = (IList)Activator.CreateInstance(listType);
You cannot do it with C#!!
Compiler must to know the parameter type.
In that maybe you would like to accomplish, interfaces will help you
public class Contact: IIdDescription
{
public int ID { get; set; }
public string Description { get; set; }
}
public class Member: IIdDescription
{
public int ID { get; set; }
public string Description { get; set; }
}
public interface IIdDescription
{
int ID { get; set; }
string Description { get; set; }
}
public ActionResult GetAll(string type)
{
var AllList = new List<IIdDescription>();
if(type == Member.GetType().Name)
AllList = repo.Set<Member>().Cast<IIdDescription >().ToList();
if(type == Contact.GetType().Name)
AllList = repo.Set<Contact>().Cast<IIdDescription >().ToList();
...
}
and into your view use interface as model, something like
#model IEnumerable<IIdDescription>
If you don't know the type ahead of time maybe using a list of dynamic objects can help.
var item = GetInstance<Contact>("Namespace.Contact");
var items = new List<dynamic>();
items.Add(item);
You can then access the types like so...
Contact contact = items[0];
Just be aware that using dynamic can be expensive.

Serializing a custom object using SOLID principles

I want to serialize my model objects (from WPF MVVM) which contains pure data. This sounds easy but I don't want to use the Serialization attributes and stuff provided in .NET framework. I just want to serialize it using my own way.
So here's a simplified version of one of my classes.
public class EntryKeyValuePair
{
public EntryKeyValuePair(string key, string value, bool isMultiline = false, bool isMandatory = true, bool isProtected = false)
{
Key = key;
Value = value;
IsMultiline = isMultiline;
IsMandatory = isMandatory;
IsProtected = isProtected;
}
public string Key { get; set; }
public string Value { get; set; }
public bool IsMultiline { get; set; }
public bool IsMandatory { get; set; }
public bool IsProtected { get; set; }
public static EntryKeyValuePair FromXML(XElement element, ICipher cipher)
{
string key = cipher.Decrypt(element.Element(nameof(Key)).Value);
string value = cipher.Decrypt(element.Element(nameof(Value)).Value);
bool isMultiline = bool.Parse(element.Element(nameof(IsMultiline)).Value);
bool isMandatory = bool.Parse(element.Element(nameof(IsMandatory)).Value);
bool isProtected = bool.Parse(element.Element(nameof(IsProtected)).Value);
return new EntryKeyValuePair(key, value, isMultiline, isMandatory, isProtected);
}
public XElement ToXML(ICipher cipher)
{
return new XElement(nameof(EntryKeyValuePair),
new XElement(nameof(Key),cipher.Encrypt(Key)),
new XElement(nameof(Value), cipher.Encrypt(Value)),
new XElement(nameof(IsMultiline), IsMultiline), new XElement(nameof(IsMandatory), IsMandatory),
new XElement(nameof(IsProtected), IsProtected));
}
}
This works quite well. But this violates single responsibility principle and maybe other principles as well. This is also difficult to maintain and extend.
So I wanted to find another way. And here it is:
First I defined an IStringFormatter interface which can format the data to any string data formats like XML and JSON. (Not sure tho)
interface IStringFormatter
{
string Name { get; set; }
Dictionary<string, string> FieldDictionary { get; }
string Format();
}
Here's how the XMLStringFormatter looks like:
class XmlStringFormatter : IStringFormatter
{
public XmlStringFormatter()
{
FieldDictionary = new Dictionary<string, string>();
}
public string Name { get; set; }
public Dictionary<string, string> FieldDictionary { get; }
public string Format()
{
var xElement = new XElement(Name, FieldDictionary.Keys.Select(key => new XElement(key, FieldDictionary[key])));
return xElement.ToString();
}
}
Then I defined an ISerializer to serialize (or rather save) my data objects to the IStringFormatter.
interface ISerializer<T>
{
T DeSerialize(IStringFormatter stringFormatter);
void Serialize(T obj, IStringFormatter stringFormatter);
}
And here is how I "Serialize" EntryKeyValurPair by implementing this:
internal class EntryKeyValurPairSerializer : ISerializer<EntryKeyValuePair>
{
public EntryKeyValuePair DeSerialize(IStringFormatter stringFormatter)
{
Dictionary<string, string> fieldDictionary = stringFormatter.FieldDictionary;
try {
string key = fieldDictionary[nameof(EntryKeyValuePair.Key)];
string value = fieldDictionary[nameof(EntryKeyValuePair.Value)];
bool isMandatory = bool.Parse(fieldDictionary[nameof(EntryKeyValuePair.IsMandatory)]);
bool isProtected = bool.Parse(fieldDictionary[nameof(EntryKeyValuePair.IsProtected)]);
bool isMultiline = bool.Parse(fieldDictionary[nameof(EntryKeyValuePair.IsMultiline)]);
return new EntryKeyValuePair(key, value, isMultiline, isMandatory, isProtected);
}
catch (KeyNotFoundException ex) {
throw new SerializationException(ex);
}
catch (FormatException ex) {
throw new SerializationException(ex);
}
}
public void Serialize(EntryKeyValuePair obj, IStringFormatter stringFormatter)
{
stringFormatter.Name = nameof(EntryKeyValuePair);
Dictionary<string, string> fieldDictionary = stringFormatter.FieldDictionary;
fieldDictionary.Add(nameof(EntryKeyValuePair.Key), obj.Key);
fieldDictionary.Add(nameof(EntryKeyValuePair.Value), obj.Value);
fieldDictionary.Add(nameof(EntryKeyValuePair.IsMandatory), obj.IsMandatory.ToString());
fieldDictionary.Add(nameof(EntryKeyValuePair.IsProtected), obj.IsProtected.ToString());
fieldDictionary.Add(nameof(EntryKeyValuePair.IsMultiline), obj.IsMultiline.ToString());
}
}
Now this works fine. But the problem is when I have a complex type like List<Entry> (where Entry is another data class) in my data classes.
As the IStringFormatter contains a Dictionary<string, string>, I can't just convert a List<Entry> to a string because I don't know what kind of IStringFormatter it wants in the context of ISerializer. How can I fix this? I also want to know if my solution is adhering to SOLID principles. If you can suggest a better solution (NOT the typical .NET serialization), I would appreciate it.
Writing your own serializer might be an interesting task, but I doubt that this is a good idea.
As I understood you want to keep your models clean, without any serialization specific attributes. I guess by "typical .NET serialization" you mean methods included with .Net framework.
For simplicity we will use these simple classes as an example:
class Customer
{
public string Name { get; set; }
public int Age { get; set; }
public List<Order> Orders { get; set; }
}
class Order
{
public int Id { get; set; }
public string Details { get; set; }
}
An easy option would be to use Json.NET:
var customer = new Customer
{
Name = "Darth Vader",
Age = 45,
Orders = new List<Order>
{
new Order { Id = 1, Details = "Order1" },
new Order { Id = 2, Details = "Order2" }
}
};
string json = JsonConvert.SerializeObject(customer);
So as you can see you don't need to add any custom attributes to Customer class. It will work until you want to serialize all public properties.
The resulting JSON will be:
{
"Name": "Darth Vader",
"Age": 45,
"Orders": [
{
"Id": 1,
"Details": "Order1"
},
{
"Id": 2,
"Details": "Order2"
}
]
}
After that you can always deserialize it:
var customer = JsonConvert.DeserializeObject<Customer>(json);
Lets say that you don't want Age property to be included. In this case I would suggest to create a different class that will be used for serialization only:
class CostomerSerializationContract
{
public string Name { get; set; }
public List<Order> Orders { get; set; }
}
This main advantage if this approach is that you have separate class for serialization, and you can add any custom attributes there, if you choose to use some other serializer, without violating SOLID principle. The main disadvantage is that you need to keep both objects in sync manually.
You can use AutoMapper to reduce manual work when creating serialization contract from source class.

Deserialization and serialization using newtonsoft c#

My application is binding a REST API, that returns this to me:
{
key: "XXXX-XXXX",
fields: {
customfield_10913: {
value: "L2"
}
}
}
I'm using Newtonsoft JSON to serialize and deserialize and I've created these models to make it work:
public class Issue
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("fields")]
public Fields Fields { get; set; }
}
public class Fields
{
[JsonProperty("customfield_10913")]
public CustomField Level { get; set; }
}
public class CustomField
{
[JsonProperty("value")]
public string Value{ get; set; }
}
The application is deserializing everything ok, using this code:
T model = JsonConvert.DeserializeObject<T>(result);
After a lot of business logic, my WEB API should return a new JSON:
protected T Get()
{
return model;
}
And I've got everything like the JSON I've read from another API.
So, What I need?
I need to read the field CUSTOM_FIELDXXX, but I can't return it with this name in my WEB API. How could I read this field, but when I'm doing the serialization, it assume another one?
You may try below function
Issue model = deserializeObject<Issue>(result);
public T deserializeObject<T>(string result)
{
try
{
var settings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore
};
var items = (T)JsonConvert.DeserializeObject(result, typeof(T), settings);
return items;
}
catch (Exception ex)
{
throw ex;
}
finally
{
}
}
If you can have any property name for your CustomField property, you can serialize that as a Dictionary<string, CustomField>, like so:
public class Issue
{
public Issue()
{
this.Fields = new Dictionary<string, CustomField>();
}
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("fields")]
public Dictionary<string, CustomField> Fields { get; set; }
}
Then you can use any string-valued name for your custom field.
Working fiddle.

Deserialize json to dynamic/anonymous class asp.net

I have one concrete class called ShipFromAddress and where I am deserializing my json this below way
JavaScriptSerializer jss = new JavaScriptSerializer();
oShipFromAddress = jss.Deserialize<ShipFromAddress>(Request.Cookies["ShipFromAddress"].Value);
Concrete class
public class ShipFromAddress
{
public string Weight
{
get;
set;
}
public string addressLine1
{
get;
set;
}
public string addressLine2
{
get;
set;
}
public string city
{
get;
set;
}
public string postcode
{
get;
set;
}
public string countrycode
{
get;
set;
}
public string StateCode
{
get;
set;
}
}
I do not want to create or use concrete class rather I want to do that deserialization on the fly with the help of dynamic object or anonymous class concept. Please guide me with sample code.
i got two solution.....which looks good
1) when need to pass multiple data serialize to anonymous the example would be
var query = from employee in employees select new { Name = employee.Name, Id = employee.Id };
LogEmployees(query);
public void LogEmployees (IEnumerable<dynamic> list)
{
foreach (dynamic item in list)
{
string name = item.Name;
int id = item.Id;
}
}
method argument type must be IEnumerable<dynamic> because LogEmployees() function expecting multiple data
2) when passing single data the code look like
public class Program
{
private static void Thing(dynamic other)
{
Console.WriteLine(other.TheThing);
}
private static void Main()
{
var things = new { TheThing = "Worked!" };
Thing(things);
}
}
With JavaScriptSerializer you can use the DeserializeObject method from serializer which will return just an object:
JavaScriptSerializer jss = new JavaScriptSerializer();
object obj= jss.DeserializeObject(Request.Cookies["ShipFromAddress"].Value);
Internally it will be represented as a Dictionary<string, object>, so you can cast it to it and use like this:
var values = (Dictionary<string, object>)jss.DeserializeObject(Request.Cookies["ShipFromAddress"].Value);
var addressLine1 = values["addressLine1"].ToString();
Or you can cast it to dynamic:
dynamic values = jss.DeserializeObject(Request.Cookies["ShipFromAddress"].Value);
var addressLine1 = values["addressLine1"].ToString();
Alternatively, You can use Json.NET library and it's JsonConvert class (benchmarks show that it performs faster than JavaScriptSerializer). The code will look like this:
dynamic values = JsonConvert.DeserializeObject(Request.Cookies["ShipFromAddress"].Value);
var addressLine1 = values.addressLine1;
I use Newtonsoft.Json
string source = Request.Cookies["ShipFromAddress"].Value as string;
var address = JsonConvert.DeserializeObject<ShipFromAddress>(source);

Property and name at runtime from a class

I have a class
public class ProjectTask
{
public ProjectTask();
[XmlElement("task_creator_id")]
public string task_creator_id { get; set; }
[XmlElement("task_owner_id")]
public string task_owner_id { get; set; }
[XmlElement("task_owner_location")]
public TaskOwnerLocation task_owner_location { get; set; }
[XmlElement("task_owner_type")]
public string task_owner_type { get; set; }
[XmlElement("task_type_description")]
public string task_type_description { get; set; }
[XmlElement("task_type_id")]
public string task_type_id { get; set; }
[XmlElement("task_type_name")]
public string task_type_name { get; set; }
}
An xml will be deserialized to this at runtime.
Is there way to get the field name and value?
Using reflection I can get the property names like so:
PropertyInfo[] projectAttributes = typeof(ProjectTask).GetProperties();
A foreach loop can be applied to get the properties
foreach(PropertyInfo taskName in projectAttributes)
{
Console.WriteLine(taskName.Name);
}
but how do I print the property and the value?
Like
task_creator_id = 1
where task_Id is one of the properties and the value of that at runtime is 1.
Use taskName.GetValue(yourObject,null)
where yourObject should be of an instance of ProjectTask. For ex,
ProjectTask yourObject = (ProjectTask)xmlSerializer.Deserialize(stream)
var propDict = typeof(ProjectTask)
.GetProperties()
.ToDictionary(p => p.Name, p => p.GetValue(yourObject, null));
You can do that using your PropertyInfo object :
var propertyName = MyPropertyInfoObject.Name;
var propertyValue = MyPropertyInfoObject.GetValue(myObject, null);
A foreach loop give you access to all properties of your type, you can also have a specefic property knowing its name, like so :
var MyPropertyInfoObject = myType.GetProperty("propertyName");

Categories