Deserializing a json file into a static class in C# - c#

I have a static class with static fields and a json.
I can deserialize the json into a dynamic object, so I have all the fields and they match exactly the static fields in the class.
How can I use reflection to enumerate the fields and copy the values from the dynamic class into the static class fields?
I can't change the architecture, make it a singleton, etc; it's shared code and the class is going to remain static since it's globally shared settings object used by a shared library.
The solution needs to use reflection since the class evolves over time with new members. Otherwise I could have written a custom deserializer.
Adding more details, but there is really not much:
I have this static class:
static class A
{
static int I;
static string S;
}
and a json matching the fields exactly:
{
"I" : 3,
"S" : "hello"
}
var Data = JsonConvert.Deserialize<dynamic>(...);
I would like to initialize the static fields of class A with the values I deserialized from the json, into a dynamic object.
Another edit:
I came up with something similar to what David wrote, but this is less efficient since I use the deserializer to convert types, so David's solution is better.
here's what I came up with:
foreach (var Destination in typeof(Settings).GetProperties())
{
var Name = Destination.Name;
var T = Destination.PropertyType;
var Value = JsonConvert.DeserializeObject("\"" + JT[Name] + "\"", T);
Destination.SetValue(null, Value);
}

You can do this quite easily by having a matching non-static class, getting the properties of source and destination and looping through each one. For example, assuming we have two classes:
public static class A
{
public static int I { get; set; }
public static string S { get; set; }
}
public class B
{
public int I { get; set; }
public string S { get; set; }
}
We can now do this:
public void MapToStaticClass(B source)
{
var sourceProperties = source.GetType().GetProperties();
//Key thing here is to specify we want the static properties only
var destinationProperties = typeof(A)
.GetProperties(BindingFlags.Public | BindingFlags.Static);
foreach (var prop in sourceProperties)
{
//Find matching property by name
var destinationProp = destinationProperties
.Single(p => p.Name == prop.Name);
//Set the static property value
destinationProp.SetValue(null, prop.GetValue(source));
}
}
Another option is to deserialise to JToken and use that combined with reflection:
var source = JsonConvert.DeserializeObject<JToken>(json);
And then:
public void MapJTokenToStaticClass(JToken source)
{
var destinationProperties = typeof(A)
.GetProperties(BindingFlags.Public | BindingFlags.Static);
foreach (JProperty prop in source)
{
var destinationProp = destinationProperties
.SingleOrDefault(p => p.Name.Equals(prop.Name, StringComparison.OrdinalIgnoreCase));
var value = ((JValue)prop.Value).Value;
//The ChangeType is required because JSON.Net will deserialise
//numbers as long by default
destinationProp.SetValue(null, Convert.ChangeType(value, destinationProp.PropertyType));
}
}

Related

How can I use a dynamic to find out when a property is used?

I would like to find out which of the properties in a source input object, a method has used. After executing the method I need to store in a database which of the properties was used.
The input could be any class with simple types, like this:
public class MyData : IMyData
{
public string A { get; set; }
public int B { get; set; }
public decimal C { get; set; }
}
I thought it could be done using an interface as input to the method, so I can replace the original object with a more advanced object, which stores usage of properties
public interface IMyData
{
string A { get; }
int B { get; }
decimal C { get; }
}
I can then
Create a dynamic object with the same properties
Use ImpromptuInterface to simulate the dynamic object implements my interface
Call my method with this dynamic interface
private static void Main()
{
var data = new MyData { A = "Test", B = 3, C = new decimal(1.2) };
IDictionary<string, object> replacementObject = new ExpandoObject();
replacementObject.Add("FieldsUsed", new List<string>());
foreach (var property in data.GetType().GetProperties())
replacementObject.Add(property.Name, property.GetValue(data));
var replacementInterface = replacementObject.ActLike<IMyData>();
DoStuff(replacementInterface);
Console.WriteLine($"The method used these fields {string.Join(", ", (List<string>)replacementObject["FieldsUsed"])}");
}
private static void DoStuff(IMyData source)
{
Console.WriteLine($"A is {source.A}");
if (source.B > 5)
Console.WriteLine($"C is {source.C}");
}
In the above example I would like to store that fields A and B have been used.
Only I am stuck at how I should store when a property is used by my DoStuff method.
You can write a wrapper like this:
public class ClassWrapper<T>: DynamicObject where T:class
{
private readonly T _obj;
private readonly List<string> _fieldsUsed=new List<string>();
public ClassWrapper(T obj)
{
_obj = obj;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
PropertyInfo propertyInfo = _obj.GetType().GetProperty(binder.Name);
_fieldsUsed.Add(binder.Name);
result = propertyInfo.GetValue(_obj);
return true;
}
public List<string> GetFieldsUsed() => _fieldsUsed;
public T GetWrapper()
{
return this.ActLike<T>();
}
}
and use it like
var data = new MyData { A = "Test", B = 3, C = new decimal(1.2) };
var mc=new ClassWrapper<IMyData>(data);
IMyData wrapped = mc.GetWrapper();
DoStuff(wrapped);
Console.WriteLine($"The method used these fields {string.Join(", ", (List<string>)mc.GetFieldsUsed())}");
If you want to know when a property is used, a Interface like INotifyPropertyChanged can do that for you at runtime. The exampel is only about notification for writes (that actually changed a value), but it would be trivial to expand it to reads and writes. It is not a perfect thing of course, as different executions might follow different code paths that use different properties.
If a function takes a specific type as input, you have to asume that all properties may be relevant. This is especially true for abstract types and interfaces - often the interface exists for this function. If it is one of those two, you can also always provide your own implementation of those Interfaces and Abstract class.
I can not shake the feeling that this is a XY problem.

Convert class object to class index

Class Person {
int Id
string Name
string Address
// etc
}
instead of accessing it like Person.Id, Person.Name, Person.Address. I want to access it via index just like Person['Id'], Person['Name']. Is there any codegen or linq conversion for this.
You can use Json.NET's JObject class
Person p = new Person() { Id = 1, Address = "A", Name = "B" };
var obj = JObject.FromObject(p);
Console.WriteLine(obj["Id"]); //1
This is a pure C# implementation:
class Program
{
static void Main(string[] args)
{
Person person = new Person
{
Id = 1,
Name = "test",
Address = "tost"
};
Console.WriteLine(person["Id"]);
person["Id"] = 5;
Console.WriteLine(person["Id"]);
}
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public object this[string propertyName]
{
get
{
return this.GetType().GetProperty(propertyName).GetValue(this);
}
set
{
this.GetType().GetProperty(propertyName).SetValue(this, value);
}
}
}
Output:
1
5
Important note:
I would never recommend to use this in a production environment, if you want to use an handly implemented system, atleast you should handle types and properties extractions to avoid consuming more memory than needed and exceeding overheads.
Using reflection and indexers:
public class ExampleClass{
public object this[string name]
{
get
{
var properties = typeof(ExampleClass)
.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var property in properties)
{
if (property.Name == name && property.CanRead)
return property.GetValue(this, null);
}
throw new ArgumentException("Can't find property");
}
set {
return;
}
}
}
An indexer won't make data comparison any easier. I suspect the real question is how to handle data in C# the same way Python's DataFrames work. ADO.NET provides the DataTable class since .NET 1.0. It's meant more for database processing than data analysis, altough it does support operations like searching, merging and diffing.
For data anlysis, the new Microsoft.Data.Analysis package provides the DataFrame class.
That said, to read properties by name, you'll have to use Reflection, an expensive operation. One way to make this cheaper is to cache type and property descriptors. Instead of writing the code yourself though, you can use Marc Gravel's FastMember library that does just that. With this, you can create a TypeAccessor or ObjectAccessor type and read properties by name, eg :
var wrapped = ObjectAccessor.Create(obj);
string propName = // something known only at runtime
Console.WriteLine(wrapped[propName]);
If you want to read from multiple objects, you'll need a TypeAccessor :
var accessor = TypeAccessor.Create(type);
string propName = // something known only at runtime
while( /* some loop of data */ )
{
accessor[obj, propName] = rowValue;
}
The library isn't that big. If you aren't allowed to use NuGet packages, you could copy the code into your project.

c#: how to hide a field which is used only for XML serialization retrocompatibility?

The field is used only during the serialization / deserialization process but I would like to immediately encapsulate it and hide from the class.
Is it possible?
Basically, no.
XmlSerializer only works with public members, so you can't make it internal or private. You can add some attributes to make it less glaring especially in UIs that data-bind:
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public int Foo {get; set; }
but that only masks it. You could also look at IXmlSerializable, but that is a horrible API and most implementations of it are simply buggy - I do not recommend implementing this interface.
But: best practice is that whenever serialization requirements conflict with your model's design: create a dedicated DTO model - one that matches perfectly your chosen serialization library and exists purely for that purpose. And then map between the two. Then you don't have to compromise.
Its not possible with XML-Serialization in C# , if you want to do like that than you should make use of DataContractSerialization, It allows this kind of functionality i.e. you can serialize private field of you object.
Below is possible with DataContractSerialization, I hope you like to try out
[DataContract]
class Person
{
[DataMember]
public string m_name;
[DataMember]
private int m_age;
}
This what I tried when I was learning XML to Linq , and this is wired solution but if you want to try , here i created xml string by using xml to linq
here is my article : Object to XML using LINQ or XmlSerializer
Note : here code field of product class is private field but still you can generate xml string
using System.Collections.Generic;
using System.Xml.Linq;
using System.Linq;
class Program
{
public class Product
{
public Product()
{ }
public Product(string name,int code, List<productType> types)
{
this.Name = name;
this.Code = code;
this.types = types;
}
public string Name { get; set; }
private int Code { get; set; }
public List<productType> types { get; set; }
public string Serialize(List<Product> products)
{
XElement productSer = new XElement("Products",
from c in products
orderby c.Code
select new XElement("product",
new XElement("Code", c.Code),
new XElement("Name", c.Name),
new XElement("Types", (from x in c.types
orderby x.type//descending
select new XElement("Type", x.type))
))
);
return productSer.ToString();
}
}
public class productType
{
public string type { get; set; }
}
public static void Main()
{
List<productType> typ = new List<productType>();
typ.Add((new productType() { type = "Type1" }));
typ.Add((new productType() { type = "Type2" }));
typ.Add((new productType() { type = "Type3" }));
List<Product> products =new List<Product>() { new Product ( "apple", 9,typ) ,
new Product ("orange", 4,typ ),
new Product ("apple", 9 ,typ),
new Product ("lemon", 9,typ ) };
Console.WriteLine(new Product().Serialize(products));
Console.ReadLine();
}
}
Assuming you are using XmlSerializer, then only public fields and properties can be serialized, as explained in Troubleshooting Common Problems with the XmlSerializer:
The serializer examines all public fields and properties of the Type to learn about which types an instance references at runtime. It then proceeds to create C# code for a set of classes to handle serialization and deserialization using the classes in the System.CodeDOM namespace.
So, what are your options? If you are able to construct your XmlSerializer directly, you could make use of the XmlSerializer.UnknownElement event to forward the unknown elements to the object being deserialized for processing.
First, define the following attribute and extension methods:
[System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = false)]
public class XmlUnknownElementEventHandlerAttribute : System.Attribute
{
}
public static partial class XmlSerializationHelper
{
public static T LoadFromXml<T>(this string xmlString, XmlSerializer serial = null)
{
serial = serial ?? new XmlSerializer(typeof(T));
serial.UnknownElement += UnknownXmlElementEventHandler;
using (StringReader reader = new StringReader(xmlString))
{
return (T)serial.Deserialize(reader);
}
}
public static void UnknownXmlElementEventHandler(object sender, XmlElementEventArgs e)
{
var obj = e.ObjectBeingDeserialized;
foreach (var method in obj.GetType().BaseTypesAndSelf()
.SelectMany(t => t.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly))
.Where(m => Attribute.IsDefined(m, typeof(XmlUnknownElementEventHandlerAttribute))))
{
method.Invoke(obj, BindingFlags.Public | BindingFlags.NonPublic, null, new object[] { sender, e }, null);
}
}
}
public static class TypeExtensions
{
public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
{
while (type != null)
{
yield return type;
type = type.BaseType;
}
}
}
Next, say you have some class like:
public partial class MyClass
{
public string MyValue { get; set; }
}
And some XML containing an element that needs to be post-processed and converted into the current model, e.g. <OldValue>:
<MyClass><OldValue>Hello</OldValue></MyClass>
Then add a method to MyClass that:
Can be private or internal (in full trust) or public;
Has the same signature as XmlElementEventHandler;
Is marked with your custom attribute [XmlUnknownElementEventHandler];
Performs the necessary post-processing on the old element.
And now the unknown element will be forwarded to it when using a serializer constructed by XmlSerializationHelper.LoadFromXml().
E.g., your method might look like:
public partial class MyClass
{
[XmlUnknownElementEventHandler]
void HandleOldElement(object sender, XmlElementEventArgs e)
{
if (e.Element.Name == "OldValue")
{
Debug.WriteLine("{0}: processed property {1} with value {2}", this, e.Element.Name, e.Element.OuterXml);
MyValue = "Old value was: " + e.Element.InnerText;
}
}
}
And you would deserialize as follows:
var model = xmlString.LoadFromXml<MyClass>();
One advantage of this solution is that it doesn't modify the XSD generated for your types in any way.
Sample fiddle. (Note that, because the dotnetfiddle code executes in partial trust, the handlers must be public. That's not necessary in full trust.)

Instantiate an instance of a Type including all object properties and adhere to inheritance

I'm not sure if that title is reflective of the actual question, so let me explain. Is there a way to instantiate a class and recursively instantiate all properties that are classes?
For example :
public class Base
{
public int BaseValue{ get; set;}
}
public class Extended : Base
{
public int ExtendedValue{ get; set;}
public AnotherExtendedClass AnotherClass { get; set;}
}
I would like to create a json payload comprised of an empty instance of Extended with all default values and properties instantiated. And use it like:
string representation = Test.CreateDefaultEmptyJson(Extended);
public static string CreateDefaultEmptyJson(Type type)
{
JsonSerializerSettings settings = new JsonSerializerSettings().Configure();
var defaultInstance= Activator.CreateInstance(type);
return JsonConvert.SerializeObject(defaultInstance, settings);
}
The output does not include the Extended class properties. I get back :
{
"BaseValue":0
}
When I would really like to see ( or something similar ):
{
"BaseValue":0,
{
"ExtendedValue":0,
{
...
}
}
}
I suppose I could recursively iterate all types of Extended and call the default constructor, however, before I go down that road there may be a few lines of code to accomplish the same.
As far as I know there is not a built-in way to do this short of writing your own recursive method.
However, assuming that:
your classes all have parameterless (default) constructors,
the non-primitive properties are all concrete types (not interfaces), and
you don't have any reference loops in your class structure,
then you can create such a method in about a dozen lines of code:
public static string CreateDefaultEmptyJson(Type type)
{
return JsonConvert.SerializeObject(RecursiveCreateInstance(type), Formatting.Indented);
}
public static object RecursiveCreateInstance(Type type)
{
object obj = null;
ConstructorInfo ctor = type.GetConstructor(Type.EmptyTypes);
if (ctor != null)
{
obj = ctor.Invoke(null);
foreach (PropertyInfo prop in type.GetProperties())
{
Type propType = prop.PropertyType;
if (prop.CanWrite && propType.IsClass)
{
prop.SetValue(obj, RecursiveCreateInstance(propType));
}
}
}
return obj;
}
Fiddle: https://dotnetfiddle.net/3VMTsC
If the above assumptions don't hold, then things get complicated fast. If you're running into issues where you need an easy way to create fake objects for testing purposes, then you might want to look into using a mocking framework.
This hastily-written class begins to address your question. It returns the settable properties which return reference types and walks through them recursively, creating instances as needed.
It doesn't cover
Indexed properties
Depth of recursion
You may be better off just setting defaults on the properties themselves so that the class won't be created with undesirable nulls.
public class PropertyPopulator
{
public void PopulateProperties(object target)
{
var properties = target.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(p => p.PropertyType.IsClass && p.CanWrite && p.CanRead);
foreach (var property in properties)
{
var propertyValue = property.GetValue(target);
if (propertyValue == null)
{
var constructor = property.PropertyType.GetConstructor(new Type[] { });
if (constructor != null)
{
propertyValue = constructor.Invoke(new object[] { });
property.SetValue(target, propertyValue);
PopulateProperties(propertyValue);
}
}
}
}
}

Is there any way to create in foreach loop?

Here is my code:
var json = File.ReadAllText(#"C:\sira.json");
dynamic x = JsonConvert.DeserializeObject<dynamic>(json);
Arac.adli_tip = x.adli_tip;
Arac.aile_hukuku = x.aile_hukuku;
Arac.avrupa_birligi_hukuku = x.avrupa_birligi_hukuku;
Arac.bankacilik_hukuku = x.bankacilik_hukuku;
Arac.bilisim_hukuku = x.bilisim_hukuku;
Arac.borclar_hukuku = x.borclar_hukuku;
Arac.cevre_hukuku = x.cevre_hukuku;
Arac.deniz_ticareti_hukuku = x.deniz_ticareti_hukuku;
Arac.devletler_ozel_hukuku = x.devletler_ozel_hukuku;
Arac.esya_hukuk = x.esya_hukuk;
.
.
.
sira.json is a configuration file about my winforms app.
Here is content of sira.json file:
{
"adli_tip": 15,
"aile_hukuku": 43,
"avrupa_birligi_hukuku": 22,
"bankacilik_hukuku": 10,
.
.
.
"vergi_hukuku": 3
}
I want to get some values from file and set static variables. But these config variables nearly 60.
Is there any way to set static variables programmatically, for example with forecach or while?
EDIT: #subi_speedrunner comment and #T.J.Crowder reply, I searched about Reflection and I coded like this:
But it gives an error. I did not understand why?
Assuming Arac property names match exactly to json property names and Arac properties are public static properties, the following code will work:
using Newtonsoft.Json.Linq;
using System;
namespace ConsoleApplication1
{
public static class Arac
{
public static int adli_tip { get; set; }
public static int aile_hukuku { get; set; }
public static int avrupa_birligi_hukuku { get; set; }
public static int bankacilik_hukuku { get; set; }
public static string string_value {get; set;}
public static DateTime date_value { get; set; }
}
class Program
{
static void Main(string[] args)
{
var json = #"
{
""adli_tip"": 15,
""aile_hukuku"": 43,
""avrupa_birligi_hukuku"": 22,
""bankacilik_hukuku"": 10,
""string_value"": ""some value"",
""date_value"": ""2016-01-24 11:18:00""
}";
JObject arac = JObject.Parse(json);
foreach (var prop in typeof(Arac).GetProperties(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public))
{
var token = arac.GetValue(prop.Name);
if (token != null)
{
object value = token.ToObject(prop.PropertyType);
prop.SetValue(null, value, null);
}
}
Console.WriteLine("adli_tip {0}", Arac.adli_tip);
Console.WriteLine("aile_hukuku {0}", Arac.aile_hukuku);
Console.WriteLine("avrupa_birligi_hukuku {0}", Arac.avrupa_birligi_hukuku);
Console.WriteLine("bankacilik_hukuku {0}", Arac.bankacilik_hukuku);
Console.WriteLine("string_value {0}", Arac.string_value);
Console.WriteLine("date_value {0}", Arac.date_value);
}
}
}
Note that I use JObject directly instead of JsonConvert.DeserializeObject, this is because JsonConvert.DeserializeObject<dynamic> actually returns a JObject, and I prefer all features of JObject than working with a generic dynamic object.
The code works with integer properties as well as other type of properties as you can see on the sample code.
The following is the relevant code:
JObject arac = JObject.Parse(json);
foreach (var prop in typeof(Arac).GetProperties(BindingFlags.Static | BindingFlags.Public))
{
var token = arac.GetValue(prop.Name);
if (token != null)
{
object value = token.ToObject(prop.PropertyType);
prop.SetValue(null, value, null);
}
}
Yes, you can use reflection, defined in the System.Reflection namespace. Rough sketch:
Get the Type objects for Arac and x
Have an array of the field names you want to process, or use Type object for Arac's GetProperties and/or GetFields methods to get an array of all of its properties/fields (you can specify various features, like whether you only want public ones, etc.)
Loop through the array or the list and for each field:
If you don't already have a FieldInfo/PropertyInfo object from Arac's Type object, use Type#GetField or Type#GetProperty (or one of their relatives) to get it
Get the FieldInfo/PropertyInfo for x's Type object for the same field/property
Use the GetValue method(s) of the FieldInfo/PropertyInfo you got from x's type object to read the value, and the SetValue method(s) of the other FieldInfo/PropertyInfo object to write the value to Arac
Not necessarily saying it's a good idea, since as soon as you put those field names into strings, you make it harder to refactor them with tools, etc., but if the question is "Can I do this?" the answer is "Yes" (and sometimes it's a reasonable choice, there are just trade-offs).

Categories

Resources