I am writing a simple event dispatcher where my events come in as objects with the clr type name and the json object representing the original event (after the byte[] has been processed into the jobject) that was fired. I'm using GetEventStore if anyone wants to know the specifics.
I want to take that clr type to do 2 things:
find the classes that implements IHandles and
call Consume(clr type) on that class
I have managed to get part 1 working fine with the following code:
var processedEvent = ProcessRawEvent(#event);
var t = Type.GetType(processedEvent.EventClrTypeName);
var type = typeof(IHandlesEvent<>).MakeGenericType(t);
var allHandlers = container.ResolveAll(type);
foreach (var allHandler in allHandlers)
{
var method = allHandler.GetType().GetMethod("Consume", new[] { t });
method.Invoke(allHandler, new[] { processedEvent.Data });
}
ATM the issue is that processedEvent.Data is a JObject - I know the type of processedEvent.Data because I have t defined above it.
How can I parse that JObject into type t without doing any nasty switching on the type name?
Use ToObject:
var data = processedEvent.Data.ToObject(t);
or if you have a known type then:
MyObject data = processedEvent.Data.ToObject<MyObject>();
This turned out to be really easy:
method.Invoke(allHandler, new[] { JsonConvert.DeserializeObject(processedEvent.Data.ToString(), t) });
If, for some reason you're stuck on an older package of Newtonsoft Json.NET (pre 4.5.11 circa 2012) and don't have access to the already mentioned JToken.ToObject(Type), you can reuse what it does internally:
var someJObject = ...; // wherever it came from; actually a JToken
var type = typeof(MyExpectedType);
MyExpectedType myObject;
using (var jsonReader = new JTokenReader(someJObject))
myObject = serializer.Deserialize(jsonReader, type);
I mention this only because I've worked on a project that I couldn't just update the Nuget package for Json.NET to the latest version. Also, this has nothing to do with the .NET framework version.
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 find a way to create an instance of a class from the full type name of the class and the json representation of the instance.
var classTypeFullName = "something.type";
var classType = Type.GetType(classTypeFullName);
var instanceJson = "{....}";
var classInstance = JsonConvert.DeserializeObject(instanceJson, classType);
My problem is it is always returning a JObject, i need it to return the instance type. If i knew the type before the code ran I would normal just do the following:
var classInstance = JsonConvert.DeserializeObject<T>(instanceJson);
Can anyone help?
In case you are looking for an answer to something like this.
The C# Core2.2 application was not loading the assembly as it was a dependency of a referenced dll. As such the classType was null and therefore the Deserialization was always coming back as a JObject not the expect type.
Try this one, i did it some time ago.
convert yr Json to dynamic dynamicObj, and have the full assembly name
var type = Type.GetType("something.type");
var obj = Convert.ChangeType(dynamicObj.Result, type);
I had this issue I was working through where I needed to deserialize my JSON into a dynamic type and I've got that worked out with this now,
var typeObject = System.Reflection.Assembly.GetExecutingAssembly().GetType("SalesForceEndpoints.Models.BaseAccount");
var accountToCreate = JsonConvert.DeserializeObject(body.Data.ToString(), typeObject);
var result = client.Create(accountToCreate); // This line fails because of the type
I can see that it successfully creates an object of my custom BaseAccount type. However when I am trying to pass it to the API I am using in Salesforce it fails. I tested explicitly casting to the type and it works fine like this,
var stronglyTypedAccountToCreate = JsonConvert.DeserializeObject<BaseAccount>(body.Data.ToString());
var result2 = client.Create(stronglyTypedAccountToCreate); // This succeeds
When looking at both of my objects in Visual Studio they appear to be the same at first glance,
And then I noticed this on my watch under the Type column,
The dynamically cast object is being listed as
object {SalesForceEndpoints.Models.BaseAccount}
and the explicitly cast object is being listed as
SalesForceEndpoints.Models.BaseAccount
I'm almost positive this is what is preventing me from executing my call succesfully, any ideas what is going wrong here and how I can execute the dynamic runtime cast to simulate an explicit compile time cast?
--- EDIT ---
I found out part of the issue. When I was calling into the Create function the create function was trying to extract the generic type and when it was dynamically cast it came through as object here instead of Account.
public IResponse<string> Create<T>(T obj) where T : new()
{
try
{
var typeName = typeof(T).Name.Replace("Base", "");
var result = _api.Create(typeName, obj);
return new SuccessResponse<string>(ResponseResult.Success, result);
}
catch (Exception ex)
{
return new FailureResponse<string>(ResponseResult.Failure, $"Unable to create record. | {ex.Message}");
}
}
Still a little curious if anyone knows why as to how a dynamic cast to an object comes through as object {ModelObject} rather than just ModelObject.
You should change it
var typeName = typeof(T).Name.Replace("Base", "");
to
var typeName = obj.GetType().Name.Replace("Base", "");
Because T will be evaluated as System.Object, you are not passing a known type to generic function and T stays with System.Object. If want to use generics, you should specify T in the compiler time.
Is it possible to return a dynamic object from a json deserialization using json.net? I would like to do something like this:
dynamic jsonResponse = JsonConvert.Deserialize(json);
Console.WriteLine(jsonResponse.message);
Json.NET allows us to do this:
dynamic d = JObject.Parse("{number:1000, str:'string', array: [1,2,3,4,5,6]}");
Console.WriteLine(d.number);
Console.WriteLine(d.str);
Console.WriteLine(d.array.Count);
Output:
1000
string
6
Documentation here: LINQ to JSON with Json.NET
See also JObject.Parse and JArray.Parse
As of Json.NET 4.0 Release 1, there is native dynamic support:
[Test]
public void DynamicDeserialization()
{
dynamic jsonResponse = JsonConvert.DeserializeObject("{\"message\":\"Hi\"}");
jsonResponse.Works = true;
Console.WriteLine(jsonResponse.message); // Hi
Console.WriteLine(jsonResponse.Works); // True
Console.WriteLine(JsonConvert.SerializeObject(jsonResponse)); // {"message":"Hi","Works":true}
Assert.That(jsonResponse, Is.InstanceOf<dynamic>());
Assert.That(jsonResponse, Is.TypeOf<JObject>());
}
And, of course, the best way to get the current version is via NuGet.
Updated (11/12/2014) to address comments:
This works perfectly fine. If you inspect the type in the debugger you will see that the value is, in fact, dynamic. The underlying type is a JObject. If you want to control the type (like specifying ExpandoObject, then do so.
If you just deserialize to dynamic you will get a JObject back. You can get what you want by using an ExpandoObject.
var converter = new ExpandoObjectConverter();
dynamic message = JsonConvert.DeserializeObject<ExpandoObject>(jsonString, converter);
I know this is old post but JsonConvert actually has a different method so it would be
var product = new { Name = "", Price = 0 };
var jsonResponse = JsonConvert.DeserializeAnonymousType(json, product);
Yes you can do it using the JsonConvert.DeserializeObject. To do that, just simple do:
dynamic jsonResponse = JsonConvert.DeserializeObject(json);
Console.WriteLine(jsonResponse["message"]);
Note: At the time I answered this question in 2010, there was no way to deserialize without some sort of type, this allowed you to deserialize without having go define the actual class and allowed an anonymous class to be used to do the deserialization.
You need to have some sort of type to deserialize to. You could do something along the lines of:
var product = new { Name = "", Price = 0 };
dynamic jsonResponse = JsonConvert.Deserialize(json, product.GetType());
My answer is based on a solution for .NET 4.0's build in JSON serializer. Link to deserialize to anonymous types is here:
http://blogs.msdn.com/b/alexghi/archive/2008/12/22/using-anonymous-types-to-deserialize-json-data.aspx
If you use JSON.NET with old version which didn't JObject.
This is another simple way to make a dynamic object from JSON:
https://github.com/chsword/jdynamic
NuGet Install
PM> Install-Package JDynamic
Support using string index to access member like:
dynamic json = new JDynamic("{a:{a:1}}");
Assert.AreEqual(1, json["a"]["a"]);
Test Case
And you can use this util as following :
Get the value directly
dynamic json = new JDynamic("1");
//json.Value
2.Get the member in the json object
dynamic json = new JDynamic("{a:'abc'}");
//json.a is a string "abc"
dynamic json = new JDynamic("{a:3.1416}");
//json.a is 3.1416m
dynamic json = new JDynamic("{a:1}");
//json.a is integer: 1
3.IEnumerable
dynamic json = new JDynamic("[1,2,3]");
/json.Length/json.Count is 3
//And you can use json[0]/ json[2] to get the elements
dynamic json = new JDynamic("{a:[1,2,3]}");
//json.a.Length /json.a.Count is 3.
//And you can use json.a[0]/ json.a[2] to get the elements
dynamic json = new JDynamic("[{b:1},{c:1}]");
//json.Length/json.Count is 2.
//And you can use the json[0].b/json[1].c to get the num.
Other
dynamic json = new JDynamic("{a:{a:1} }");
//json.a.a is 1.
Yes it is possible. I have been doing that all the while.
dynamic Obj = JsonConvert.DeserializeObject(<your json string>);
It is a bit trickier for non native type. Suppose inside your Obj, there is a ClassA, and ClassB objects. They are all converted to JObject. What you need to do is:
ClassA ObjA = Obj.ObjA.ToObject<ClassA>();
ClassB ObjB = Obj.ObjB.ToObject<ClassB>();
I have a function that returns an anonymous type which I want to test in my MVC controller.
public JsonResult Foo()
{
var data = new
{
details = "something",
more = "More"
};
return Json(data);
}
I want to verify the data I get from the Foo function, What I'm doing now is getting the data type and get it's properties values with reflection.
[Test]
public void TestOne()
{
var data = _controller.Foo().Data;
var details = data.GetType().GetProperty("details").GetValue(data, null);
var more = data.GetType().GetProperty("more").GetValue(data, null);
Assert.AreEquals("something", details);
Assert.AreEquals("More", more);
}
Is there a simple way similar to this to check the anonymous properties?
[Test]
public void TestTwo()
{
var data = (dynamic) _controller.Foo().Data;
var details = data.details; // RunTimeBinderException object does not contain definition for details
var more = data.more;
Assert.AreEquals("something", details);
Assert.AreEquals("More", more);
}
Anonymous objects are internal, which means their members are very restricted outside of the assembly that declares them. dynamic respects accessibility, so pretends not to be able to see those members. If the call-site was in the same assembly, I expect it would work.
Your reflection code respects the member accessibility, but bypasses the type's accessibility - hence it works.
In short: no.
This blog had a working answer: http://blog.jorgef.net/2011/06/converting-any-object-to-dynamic.html - Thanks #Jorge-Fioranelli.
public static class DynamicExtensions {
public static dynamic ToDynamic(this object value) {
IDictionary<string, object> expando = new ExpandoObject();
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(value.GetType()))
expando.Add(property.Name, property.GetValue(value));
return expando as ExpandoObject;
}
}
Anonymous type is a regular static type in .NET, it's just that you do not give it a name (a compiler, however, does). That's why casting it to dynamic will not work. However, if you have control over Foo(), you can construct and return a dynamic object instead of anonymous, and then your code is going to work. This should do the trick:
dynamic JsonResult Foo() {
dynamic data = new ExpandoObject();
data.details = "something";
data.mode = "More";
return Json(data);
}
As suggested by #TrueWill and #Marc Gravell, who also referred to this blog post
Since this is for unit testing, you could use InternalsVisibleTo. See Anonymous Types are Internal, C# 4.0 Dynamic Beware! Thanks to #MarcGravell for pointing out that anonymous objects are internal!
Bottom line: Set up an [assembly: InternalsVisibleTo("foo")] mapping if you want to share an anonymous object from one assembly to another. In the OP case, it would be a matter of setting this in the MVC controller project, referring to the test project. In my specific case, the other way around (since I'm passing an anonymous object from my test project into the "production code" project).
The easiest way in that "other project" to be able to use it is definitely to cast it to dynamic and then just use the properties like normal. It does work, no problems whatsoever.
So, bottom line: I feel that Marc Gravell's answer is slightly incorrect; this can clearly be done
(iff the projects in question are modifiable by you, so you can set up the InternalsVisibleTo mapping accordingly, and this does not pose a problem for whatever other reason).
You can use NewtonSoft or the Asp.net MVC libraries:
var data = Json.Decode(Json.Encode(_controller.Foo().Data));
var data=JsonConvert.DeserializeObject<Dictionary<string,object>>(JsonConvert.SerializeObject((_controller.Foo().Data))