I'm using JSON.NET to deserialize a JSON file to a dynamic object in C#.
Inside a method, I would like to pass in a string and refer to that specified attribute in the dynamic object.
For example:
public void Update(string Key, string Value)
{
File.Key = Value;
}
Where File is the dynamic object, and Key is the string that gets passed in. Say I'd like to pass in the key "foo" and a value of "bar", I would do:
Update("foo", "bar");, however due to the nature of the dynamic object type, this results in
{
"Key":"bar"
}
As opposed to:
{
"foo":"bar"
}
Is it possible to do what I'm asking here with the dynamic object?
I suspect you could use:
public void Update(string key, string Value)
{
File[key] = Value;
}
That depends on how the dynamic object implements indexing, but if this is a Json.NET JObject or similar, I'd expect that to work. It's important to understand that it's not guaranteed to work for general dynamic expressions though.
If you only ever actually need this sort of operation (at least within the class) you might consider using JObject as the field type, and then just exposing it as dynamic when you need to.
Okay so it turns out I'm special. Here's the answer for those that may stumble across this in future,
Turns out you can just use the key like an array index and it works perfectly. So:
File[Key] = Value; Works the way I need as opposed to
File.Key = Value;
Thanks anyway!
You can do it, if you're using JObject from JSON.NET. It does not work with an ExpandoObject.
Example:
void Main()
{
var j = new Newtonsoft.Json.Linq.JObject();
var key = "myKey";
var value = "Hello World!";
j[key] = value;
Console.WriteLine(j["myKey"]);
}
This simple example prints "Hello World!" as expected. Hence
var File = new Newtonsoft.Json.Linq.JObject();
public void Update(string key, string Value)
{
File[key] = Value;
}
does what you expect. If you would declare File in the example above as
dynamic File = new ExpandoObject();
you would get a runtime error:
CS0021 Cannot apply indexing with [] to an expression of type 'ExpandoObject'
Related
I've been using System.Web.Helpers.Json to deserialize objects quite successfully up until I received a json with the keys that only differ in case of the letters and the Decode() method throws an ArgumentException. I tried to figure out how to make this class work in case-sensitive way and couldn't, so I decided to go with Newtonsoft library instead. Json.NET works fine case-wise, however the deserialized objects it returns would require a type cast as follows:
[TestMethod]
public void CaseSensitivityTest() {
string json = "{\"e\":\"executionReport\",\"E\":1616877261436}";
dynamic result = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
string executionReport = result.e;//have to assign to a typed variable for the assert below to work
Assert.AreEqual("executionReport", executionReport);
Assert.IsTrue(1616877261436 == (long)result.E);//or explicitly cast to a type
result = System.Web.Helpers.Json.Decode(json);//System.ArgumentException: An item with the same key has already been added.
Assert.IsTrue(1616877261436 == result.E);//this would've worked without any type cast as in the example below
}
The rest of my code relies heavily on deserialized objects having properly typed properties (e.g. my typical code decimal.Parse(deserializedResponse.price) expects price to be string and not JValue<string>). Here's another comparison:
[TestMethod]
public void TypeCastTest() {
string json = "{\"intValue\":123}";
dynamic webHelpersResult = System.Web.Helpers.Json.Decode(json);
dynamic newtonSoftResult = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
Assert.AreEqual(123, webHelpersResult.intValue);//All good here, I want JsonConvert to work the same way
Assert.AreEqual(123, newtonSoftResult.intValue);//Assert.AreEqual failed. Expected:<123 (System.Int32)>. Actual:<123 (Newtonsoft.Json.Linq.JValue)>.
}
It would be very difficult to refactor adding type casts everywhere, so I would prefer a single point of fix. I need either to make the System.Web.Helpers.Json case-sensitive or Newtonsoft.Json.JsonConvert to return .NET-typed values and not of JValue type. What would be the best way to achieve this? I'm writing a console application running on Windows 7 machine, so all fancy web/WINRT/Xamarin/etc stuff is not always available.
UPDATE
the suggestion of deserializing into ExpandoObject as below:
dynamic newtonSoftResult = JsonConvert.DeserializeObject<ExpandoObject>(json);
seems to work initially, however it fails to deserialize json lists and I couldn't make it backward compatible with System.Web.Helpers.Json.Decode() result:
string single = "{\"s\":\"String1\",\"f\":\"0.00\"}";
string multiple = "[{\"s\":\"String1\",\"f\":\"0.00\"},{\"s\":\"String2\",\"f\":\"1.23\"}]";
var helpersSingle = System.Web.Helpers.Json.Decode(single);
var helpersMultiple = System.Web.Helpers.Json.Decode(multiple);
var newtonSingle = Newtonsoft.Json.JsonConvert.DeserializeObject<ExpandoObject>(single);
var newtonMultiple = JsonConvert.DeserializeObject<ExpandoObject>(multiple);//System.InvalidCastException: Unable to cast object of type 'System.Collections.Generic.List`1[System.Object]' to type 'System.Dynamic.ExpandoObject'.
Assert.AreEqual("String1", helpersSingle.s);
Assert.AreEqual("String2", helpersMultiple[1].s);
Assert.IsFalse(helpersSingle is IEnumerable);
Assert.IsFalse(newtonSingle is IEnumerable);//This fails as well as ExpandoObject would implement IEnumerable for its properties
You can use Newtonsoft.Json with the following helper class:
public static class JsonHelper
{
public static object Deserialize(string json)
{
return ToObject(JToken.Parse(json));
}
private static object ToObject(JToken token)
{
switch (token.Type)
{
case JTokenType.Object:
var expando = new ExpandoObject() as IDictionary<string, object>;
foreach (JProperty prop in token.Children<JProperty>())
{
expando.Add(prop.Name, ToObject(prop.Value));
}
return expando;
case JTokenType.Array:
return token.Select(ToObject).ToList();
default:
return ((JValue)token).Value;
}
}
}
In your tests you can do:
dynamic result = JsonHelper.Deserialize(json);
The result will either be an ExpandoObject or a List<ExpandoObject> which should work with most of your tests. You will have to make an adjustment for tests that check for IEnumerable since ExpandoObject does implement this interface. If you need to differentiate between a single object or multiple, you could check for IList instead.
Working example here: https://dotnetfiddle.net/n2jI1d
Newtonsoft wraps the JSON Properties.. for various very good reasons I wont get into. You don't need to cast you can just use .Value i.e newtonSoft.intValue.Value. I should point out that you are still parsing the entire JSON string here using dynamic types because you don't use most of it isn't a good excuse to use dynamic typing. I would highly recommend you not use dynamic types but to each there own.
[TestMethod]
public void TypeCastTest() {
string json = "{\"intValue\":123}";
dynamic webHelpersResult = System.Web.Helpers.Json.Decode(json);
dynamic newtonSoftResult = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
Assert.AreEqual(123, webHelpersResult.intValue);
Assert.AreEqual(123, newtonSoftResult.intValue.Value);
}
I am trying to create a dynamic array using JSON.net, My idea is pretty simple but I have encountered an issue I don't know how to get around.
static dynamic SaveValue <T> (dynamic JsonArray, string Object, T Value) {
JsonArray.Object = Value;
return JsonArray;
}
Now the object is where the string should be used to get the element, It will contain the value to be serialized later
Json = SaveValue<bool>(Json, "Bhop.Enabled", true);
The usage is like this
I have tried:
Googling this and looking for functions but I'm stuck, Any help would be appreciated.
I am still not getting fully what you are trying to achieve but the following code will help to dynamically generate the json object based on string key and the value.
static void Main(string[] args)
{
dynamic Json = new JObject() as dynamic;
Json = SaveValue<bool>(Json, "Bhop.Enabled", true);
Console.ReadLine();
}
static dynamic SaveValue<T>(dynamic jsonArray, string Object, T Value)
{
jsonArray = new JObject();
jsonArray.Add(Object,Value);
return jsonArray;
}
Story
I'm trying to write a generic method which combines property names, types and content value to generate a unique string for the value held by the object passed.
The idea is to generate a unique SHA3-512 Hash based on the generated string sequence which can be used to compare objects on generic bases by looking at their content.
Example
Let's say we have a class like this ...
class MyClass {
private Int32 Id = 5;
public String Name = "some string";
protected DateTime CreateDate = DateTime.Parse("2017-08-21 15:00:07");
}
... and the mentioned method to generate the unique string
static String GetContentString<T>(T obj) where T : class {
...
}
In theory this should work somewhat like this:
var myObj = new MyClass();
var uniqueContentString = GetContentString(myObj);
Console.WriteLine(uniqueContentString);
>>> Id:Int32:5$Name:String:some string$CreateDate:DateTime:2017-08-21 15:00:07
Problem
I'm having difficulties building the GetContentString Method. This is what I have already:
Object obj = ... // given object
Type type = obj.GetType();
IList<PropertyInfo> propertyInfos = type.GetProperties().Where(x => x.CanRead).ToList(); // Marker #2
StringBuilder sb = new StringBuilder();
foreach (PropertyInfo pi in propertyInfos)
{
sb.Append(pi.Name);
sb.Append(":");
sb.Append(pi.PropertyType.Name);
sb.Append(":");
sb.Append(pi.GetValue(obj) ?? "[ISNULL]"); // Marker #1
sb.Append(":");
}
return sb.ToString();
I tried running the method for a few different types of values like "some string" or 1234 (Int32) and ran into a few issues.
Given a string, the method call throws an exception of type System.Reflection.TargetParameterCountException and the message Parameter count mismatch at #1. I found out that an optional index can be passed to an overloaded version of pi.GetValue(..) which then returns one of the single letters. But how do you know when to stop? If you call an index which doesn't exist it throwns an exception of the type System.Reflection.TargetInvocationException. How do you get the value of a string object using reflection?
Given an integer value, the method call doesn't find any properties at #2. Which brings up the question of how to get the value of an integer object using reflection?
And also some general questions; do you guys think this is a good approach to get a unique string? Is reflection the way to go here? Is it even possible to write a generic solution to this problem?
Without looking at reflection, how about JSON serialization with something that .net framework is able to ?
Reflection isn't something extremely fast and you'll run into issues at the first unhandled exception.
Then, you should do that recursivly if your objects can contains complex properties, wich is not a problem with json serialization !
I have a method in Base class which calls ( by reflection to another method).
type.InvokeMember(context.Request["MethodName"],
System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance,
null,
this,
new object[] { context, Jobj }); // jObj is dynamic
jObj parameter type is dynamic ( can't change this type).
if the MethodName string value is : "getFinanceDetails" so that method is called..
void getFinanceDetails(object contextObj, dynamic obj)
{
//Here I need to do obj["Inv_num"].ToString().Decrpyt() ( my extension method).
//but it goes Bang cause I cant use extension method for dynamic.
//But I cant also send it decrypted from base cause not all values are encrpyrted.
}
However - I did solve it by using (inside the method):
((object) obj["Inv_num"]).ToString().Decrypt();
But I dont want to cast every time to object , just to enable extension method.
Is there anything I can do with the param type sending to fix it ?
my desire :
I want to be able to do : obj.ToString().Decrpyt() obj["Inv_num"].ToString().Decrpyt()
edit
public static string Decrypt(this string obj)
{
Func<string, string> Decrypt = Encryptions.GetDecryptedCode;
return Decrypt(obj);
}
obj ( in this case is IDictionary<string , object>) .
so I should be able to read properties. (inv_num in this sample.
Probably not exactly the syntax you were looking for but you could call the extension method as a simple static method on the dynamic object:
void getFinanceDetails(object contextObj, dynamic obj)
{
var decryptedValue = MyExtensions.Decrypt(obj);
}
This obviously assumes that at runtime obj is of the correct type that your extension method operates on. In your question you have shown some obj["Inv_num"] as if obj was a complex type with a property called Inv_num which is of type string. So you might need to adjust the call on the proper type:
var decryptedValue = MyExtensions.Decrypt(obj["Inv_num"]);
Assuming obj["Inv_num"].ToString() already returns the right value, you could easily do it in two steps:
string text = obj["Inv_num"].ToString();
var decrypted = text.Decrypt();
To be honest, it's not clear why getFinanceDetails (which should be changed to follow .NET naming conventions) can't be written as:
void getFinanceDetails(object contextObj, IDictionary<string, object> obj)
{
var decrypted = obj["Inv_num"].ToString().Decrypt();
}
Do you ever need to call it with something that doesn't implement IDictionary<string, object>?
If I have this string list:
string myObjectString = "MyObject, SetWidth, int, 10, 0, 1";
in which:
- MyObject: the object class
- SetWidth: the property of the object
- int: type of the SetWidth is int
- 10: default value
- 0: object order
- 1: property order
Then how can I construct an object like this:
[ObjectOrder(0)]
public class MyObject:
{
private int _SetWidth = 10;
[PropertyOrder(1)]
public int SetWidth
{
set{_SetWidth=value;}
get{return _SetWidth;}
}
}
So, I would like have something like this:
Object myObject = ConstructAnObject(myObjectString);
and the myObject is an instance of MyObject. Could it be possible in C#?
Thanks in advance.
I think you better use the Object Serialization/Deserialization instead of creating a custom method that basically needs to do the same thing
more info at:
http://msdn.microsoft.com/en-us/library/ms233843.aspx
Here is some quick and dirty code to get you started:
string myObjectString = "MyObject, SetWidth, int, 10, 0, 1";
var info = myObjectString.Split(',');
string objectName = info[0].Trim();
string propertyName = info[1].Trim();
string defaultValue = info[3].Trim();
//find the type
Type objectType = Assembly.GetExecutingAssembly().GetTypes().Where(t=>t.Name.EndsWith(objectName)).Single();//might want to redirect to proper assembly
//create an instance
object theObject = Activator.CreateInstance(objectType);
//set the property
PropertyInfo pi = objectType.GetProperty(propertyName);
object valueToBeSet = Convert.ChangeType(defaultValue, pi.PropertyType);
pi.SetValue(theObject, valueToBeSet, null);
return theObject;
This will find the MyObject, create an object of the proper propertytype, and set the matching property.
If you use C# 4.0, you can use the new dynamic feature.
string myObjectString = "MyObject, SetWidth, int, 10, 0, 1";
String[] properties = myObjectString.Split(',');
dynamic myObj;
myObj.MyObject = (objtect)properties[0];
myObj.SetWidth = Int32.Parse(properties[1]);
// cast dynamic to your object. Exception may be thrown.
MyObject result = (MyObject)myObj;
I don't quite understand why do you need ObjectOrder and PropertyOrder... Once you have their names you probably don't need them, at least for "deserialization"...
Or please advice what is their role?
You definitely can simply do it via reflection:
Split the string by comma (using myString.Split)
Use reflection to find an object within your application:
Find the type with name = splittedString[0] (enumerate all the assemblies within the domain and all the types within each assembly);
Instantiate the type found (using Activator.CreateInstance)
Find the property by name (Using objectType.GetProperty)
Set the property value (using propertyInfo.SetValue)
Return the object
Assuming you need to generate new types there are two possible ways to do so:
Using Reflection Emit
Using CodeDom provider
I think the simpler solution is CodeDom provider. All needed is to generate the source as a string in memory, and then compile the code and instantiate a new instance with Activator. This is a nice example I just found.
The reason I think that CodeDom provider is simpler is that it has shorter setup - no need to generate dynamic module and assembly and then work with type builder and members builder. In addition, it doesn't require working with IL to generate the getter and setter bodies.
An advantage that reflection emit has is performance - dynamic module can add more types to itself even after one of the types was used. CodeDom provider requires creating all the types at once, otherwise it creates a new assembly each time.