Change value in Dynamic array based on string input - c#

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;
}

Related

Newtonsoft JsonConvert vs System.Web.Helpers Json - type cast problem

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);
}

Custom JSON field names with Newtonsoft as defined by a variable [duplicate]

I have a method which accepts a key and a value. Both variables can have a dynamic content.
key => is a dynamic string which can be everything like e.g. "LastSentDate"
value => is an object which can be everything like e.g. "2014-10-10"
As key is a dynamic value like "LastSentDate" or whatever key is passed to the method then I want that the json property is the value of the key string and not literally key itself...
public void SetRowVariable(string key, object value)
{
var obj = new { key = value }; // key property is literally taken maybe anonym object is not a good idea?
string jsonString = JsonConvert.SerializeObject(obj);
// jsonString should have that output => "{ "LastSentDate": "2014-10-10" }"
}
How do I have to serialize the obj that I get the wished output?
It must also be possible that the "key" property can contain special chars like "!"ยง$%&/()=?"`
I am using .NET 3.5 sadly.
You could use a JObject (in Newtonsoft.Json.Linq):
var obj = new JObject();
obj[key] = JToken.FromObject(value);
string jsonString = obj.ToString();
You may try using a Dictionary<string, object>:
public void SetRowVariable(string key, object value)
{
var obj = new Dictionary<string, object>();
obj[key] = value; // Of course you can put whatever crap you want here as long as your keys are unique
string jsonString = JsonConvert.SerializeObject(obj);
...
}

Read List<double> from Json via Newtonsoft in C#

I would like to read a JSON string via the Newtonsoft Json library. It works fine for any basic datatype, but it does not work for a List<double> or any List for that matter.
The test application looks like the following:
static void main()
{
string jsonString = #"
{
'name': 'set1',
'Xvv': {
'parameter': 'hByT',
'values': '[1,2,3]'
}
}";
JObject Json = JObject.Parse(jsonString);
var name = Json["name"].ToString();
var data = Json["Xvv"]["values"].Value<List<double> >(); // Raises error
}
The last line throws the following exception:
System.InvalidCastException: Invalid cast from 'System.String' to 'System.Collections.Generic.List
Is there a way to access the data directly as a List<double>?
In the example JSON you've provided, values is a string. A proper JSON array would be
'values': [1,2,3]
Anyway, after changing the string to the array, .Value<List<double>>() would throw an exception, that a JArray cannot be cast to a JToken - unfortunately I do not really know, why it does not work.
However, JToken.ToObject<T> does the trick, it
Creates an instance of the specified .NET type from the JToken
(see the documentation for ToObject)
With the line
var data = Json["Xvv"]["values"].ToObject<List<double>>();
you can cast the array correctly.
If an IEnumerable would be fine for you, too, you could also use
var data = Json["Xvv"]["values"].Values<double>();

Referring to dynamic members of C# 'dynamic' object

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'

JObject how to read values in the array?

This is the json string:
{"d":[{"numberOfRowsAdded":"26723"}]}
string json = DAO.getUploadDataSummary();
JObject uploadData = JObject.Parse(json);
string array = (string)uploadData.SelectToken("d");
How do I change the code to reader the values in 'numberOfRowsAdded?
JObject uploadData = JObject.Parse(json);
int rowsAdded = Convert.ToInt32((string)uploadData["d"][0]["numberOfRowsAdded"])
You need to cast to JArray:
string json = "{\"d\":[{\"numberOfRowsAdded\":\"26723\"}]}";
JObject parsed = JObject.Parse(json);
JArray array = (JArray) parsed["d"];
Console.WriteLine(array.Count);
You can cast your JObject as a dynamic object.
You can also cast your array to JArray object.
JObject yourObject;
//To access to the properties in "dot" notation use a dynamic object
dynamic obj = yourObject;
//Loop over the array
foreach (dynamic item in obj.d) {
var rows = (int)item.numberOfRowsAdded;
}
I played around with writing a generic method that can read any part of my json string. I tried a lot of the answers on this thread and it did not suit my need. So this is what I came up with. I use the following method in my service layer to read my configuration properties from the json string.
public T getValue<T>(string json,string jsonPropertyName)
{
var parsedResult= JObject.Parse(json);
return parsedResult.SelectToken(jsonPropertyName).ToObject<T>();
}
and this is how you would use it :
var result = service.getValue<List<string>>(json, "propertyName");
So you can use this to get specific properties within your json string and cast it to whatever you need it to be.

Categories