I am trying to create a JObject on the fly for unit testing but it does not seem to like name.subname as a property
The json that I am trying to create JObject for is
{ "UserDetails": {
"FirstName": "Test1",
"Surname": "Test2",
"UserID": "123456789",
"Children1.Active1": false,
"Children1.Active2": false
}
}
The C# code I have is
dynamic jsonObject = new JObject();
jsonObject.UserDetails = new JObject();
jsonObject.UserDetails.UserID = "123456789";
jsonObject.UserDetails.FirstName = "Test1";
jsonObject.UserDetails.Surname= "Test2";
However i am not sure how to add Children1.Active to the JObject jsonObject. UserDetails it does not work with jsonObject.UserDetails.Children.Active1 = false.
I have validated the json and it passes validation for being valid json.
How can I add Children1.Active to JObject?
I have tried jsonObject.UserDetails.Children1 = new JObject();
But this creates a new sub object which is not what I need.
"Children1.Active2" isn't a nested object, it's a property with a period in the name string. And since you're doing everything via dynamic, this becomes a nuisance, as c# identifiers can't include such a character even though JSON property names can.
In such cases, instead of using dynamic, it's easier to do things with a typed JObject and its compile-time-bound methods, specifically its string indexer method:
var jsonObject = new JObject();
var userDetails = new JObject();
jsonObject["UserDetails"] = userDetails;
userDetails["UserID"] = "123456789";
userDetails["FirstName"] = "Test1";
userDetails["Surname"] = "Test2";
userDetails["Children1.Active1"] = false;
userDetails["Children1.Active2"] = false;
This should also be more performant as dynamic can have some run-time performance penalties.
Sample fiddle here.
Related
I want to update a field in CosmosDB to array like below:
"Aliases": [
"John",
"Johnny"
],
Below is my code:
dynamic GuideDoc= new
{
id = "00B2845F-F394-5E93-9A26-E70000452562",
type = "Guide",
GuideGuid= "00B2845F-F394-5E93-9A26-E70000452562"
};
ItemResponse<dynamic> clientGuideResponse1 = await this.container.ReadItemAsync<dynamic>(GuideGuid, new PartitionKey(PartitionKey));
dynamic itemBody1 = clientGuideResponse1.Resource;
String[] AliasesConverted= JSONDecoders.DecodeJSONArray(AliasesValues);
itemBody1.Aliases= AliasesConverted;
GuideDoc= itemBody1;
GuideResponse = await this.container.ReplaceItemAsync<dynamic>(GuideDoc, GuideGuid, new PartitionKey(PartitionKey));
The part that stores the data to itemBody1.Aliases is returning an error "Could not determine JSON object type for type System.String[]"
How do I make it save the value to Aliases as an array?
The easiest way to add new properties while keeping everything else intact is deserializing it into a Dictionary. As example:
var response = await container.ReadItemAsync<Dictionary<string, object>>(id, partitionKey);
var item = response.Resource;
item["Aliases"] = new[] { "John", "Johnny" };
await container.ReplaceItemAsync(item, id, partitionKey);
The reason it doesn't work is because the deserializer will return the result as JObject when you use dynamic as type. You can't navigate the JObject through the properties as if it were deserialized into a class with properties. The error you describe is a bit different than I would have thought though.
I would like to render a Scriban template with an ExpandoObject or any other data type that can be generated from a JSON string:
var json = "....";
var dyn = JsonConvert.DeserializeObject<ExpandoObject>(json);
var template = Scriban.Template.Parse("Hello {{ data.foo }}!");
var result = template.Render(dyn);
Scriban does not work with ExpandoObjects, as they are parsed as a list of { key = '', value = '' } objects. Declaring a type for the data is not an option in my use case as the JSON schema is not known a priori.
Casting ExpandoObject to dynamic shows the same behavior as using ExpandoObject directly.
I've tried deserializing the JSON to dynamic which leads to an exception:
System.Reflection.TargetParameterCountException: "Parameter count mismatch."
Can I somehow convert the data or configure Scriban to render dynamic data?
Based on the previous answer this is a solution for complex objects:
public static class ScribanRenderer
{
public static string RenderJson(string json, string content)
{
var expando = JsonConvert.DeserializeObject<ExpandoObject>(json);
var sObject = BuildScriptObject(expando);
var templateCtx = new Scriban.TemplateContext();
templateCtx.PushGlobal(sObject);
var template = Scriban.Template.Parse(content);
var result = template.Render(templateCtx);
return result;
}
private static ScriptObject BuildScriptObject(ExpandoObject expando)
{
var dict = (IDictionary<string, object>) expando;
var scriptObject = new ScriptObject();
foreach (var kv in dict)
{
var renamedKey = StandardMemberRenamer.Rename(kv.Key);
if (kv.Value is ExpandoObject expandoValue)
{
scriptObject.Add(renamedKey, BuildScriptObject(expandoValue));
}
else
{
scriptObject.Add(renamedKey, kv.Value);
}
}
return scriptObject;
}
}
It leverages the fact that complex properties of an ExpandoObject are always ExpandoObjects when it gets deserialized from JSON. It recursively adds ScriptObjects for complex member types and the object itself for all other properties to the ``ScriptObject`.
Please not that my solution uses Scriban's default member renaming, where FirstName becomes first_name and so on.
In cases when you have a simple object you can try something like this.
var json = "{\"Phone\":\"555000000\",\"Name\":\"Maia\"}";
var scriptObject = new Scriban.Runtime.ScriptObject();
var data = JsonConvert.DeserializeObject<ExpandoObject>(json);
foreach (var prop in data)
{
scriptObject.Add(prop.Key, prop.Value);
}
var templateCtx = new Scriban.TemplateContext();
templateCtx.PushGlobal(scriptObject);
var template = Scriban.Template.Parse("Hello {{Name}}");
var result = template.Render(templateCtx); // result will be "Hello Maia"
Not sure if the previous versions supported doing it, but it looks like this is more efficient, and the Scriban example (github) does this.
var json = "{\"name\":{\"first\":\"Maia\"}}";
var template = Template.Parse("Hello {{model.name.first}}");
var result = template.Render(new { model = JsonConvert.DeserializeObject<ExpandoObject>(json) });
Console.WriteLine(result);
// Output: Hello Maia
I need to pass a Json object to an API, but the API requires the Json properties to have a double label of sorts, such as:
{
"name:id":"1234"
}
However, using Newtonsoft.Json.Linq, I can't get this to format the label exactly. Here is what I've tried so far (which throws an error)
dynamic json= new JObject();
json.name.id = "1234";
Doing
json.id = "1234";
Works just fine. I have also tried
json.name = new JProperty("id", "1234");
Which also throws an error. I have also tried hard coding the json file as a single string and converting that to a JObject, which also threw an error. Is what I'm trying to do possible or am I missing something? Is there another Json package I could use that would support what I want to do?
Use JObject's string indexer notation.
dynamic json = new JObject();
json["name.id"] = "1234";
Since the json is essentially built as a key/value pair, using a string indexer can allow you to overcome atypical property names.
There multiple ways to achieve that.
You can use JsonProperty attribute and specify the property name as name:id like:
class MyClass
{
[JsonProperty("name:id")]
public string Name_Id { get; set; }
}
and then you can do:
MyClass obj = new MyClass();
obj.Name_Id = "1234";
var strJson = JsonConvert.SerializeObject(obj);
and you will get back:
{"name:id":"1234"}
I am serializing and then deserializing a Dictionary in Newtonsoft.Json, however the output is not what I expected and is throwing exceptions. Can someone let me know what I'm doing wrong?
Here is my code
using Newtonsoft.Json;
namespace Task1
{
public class Class1
{
public static void Main()
{
var dict = new Dictionary<string, object>();
dict["int"] = new GenericItem<int> {CreatedAt = DateTime.Now, Object = 111};
dict["string"] = new GenericItem<string> {CreatedAt = DateTime.Now.Date, Object = "test test"};
var json = JsonConvert.SerializeObject(dict);
var desDict = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
var test = dict["int"] is GenericItem<int>;
var test2 = dict["int"] as GenericItem<int>;
var test3 = (GenericItem<int>) dict["int"];
var desTest = desDict["int"] is GenericItem<int>;
var desTest2 = desDict["int"] as GenericItem<int>;
var desTest3 = (GenericItem<int>) desDict["int"];
}
}
public class GenericItem<T>
{
public DateTime CreatedAt { get; set; }
public T Object { get; set; }
}
}
First three tests return True, Instance of GenericItem<Int> and again Instance of GenericItem<Int>.
But after deserialization return false, null, and InvalidCastException.
What is the problem? Why after deserealization does it throw InvalidCastException?
What is the problem? Why after deserealization does it throw InvalidCastException?
The reason of this behaviour is simple - if you check the type of this element with desDict["int"].GetType(), you will see that it returns Newtonsoft.Json.Linq.JObject and that's definitely not the type you were expecting.
It is possible to do what you want with usage of TypeNameHandling parameter of JsonSerializerSettings class, as suggested in this SO answer. Change this:
var json = JsonConvert.SerializeObject(dict);
var desDict = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
to this:
var settings = new JsonSerializerSettings();
settings.TypeNameHandling = TypeNameHandling.Objects;
var json = JsonConvert.SerializeObject(dict, settings);
var desDict = JsonConvert.DeserializeObject<Dictionary<string, object>>(json, settings);
Info from reffered answer:
The TypeNameHandling flag will add a $type property to the JSON, which
allows Json.NET to know which concrete type it needs to deserialize
the object into. This allows you to deserialize an object while still
fulfilling an interface or abstract base class.
The downside, however, is that this is very Json.NET-specific. The
$type will be a fully-qualified type, so if you're serializing it with
type info,, the deserializer needs to be able to understand it as
well.
As an alternative you can loop throught the deserialized dictionary to convert values for each key from JObject to desired type:
var desDict = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
var newDict = new Dictionary<string, object>(); //as you can't modify collection in foreach loop and the original dictionary can be out of scope of deserialization code
foreach (var key in desDict.Keys)
{
switch(key)
{
case "int":
newDict[key] = ((JObject)desDict[key]).ToObject<GenericItem<int>>();
break;
case "string":
newDict[key] = ((JObject)desDict[key]).ToObject<GenericItem<string>>();
break;
}
}
desDict = newDict;
You don't need that manual solution here, but maybe someday this knowledge will be useful. I found info about JOobject.ToObject() method in this SO answer. I've checked both methods and both work fine with the code you provided.
You ask it to deserialize to a Dictionary<string, object> so it does so. It doesn't store the type information so it builds an object to store the deserialized information in.
I don't think you can get what you want, due to variance issues. You could deserialize to a Dictionary<string, GenericItem<object>> but that doesn't seem to be what you wanted. You get a json.net specific object in place of your int or string. You could cast that to what you want.
try :
var desDict = JsonConvert.DeserializeObject<Dictionary<string, GenericItem<object>>>(json);
or :
var desDict = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(json);
using dynamic you don't need to cast your object at all, you can just use it's properties
In C#, I have successfully serialized an anonymous object into JSON by use of code like this...
var obj = new { Amount = 108, Message = "Hello" };
JavaScriptSerializer serializer = new JavaScriptSerializer();
String output = serializer.Serialize(obj);
However, what I would like to be able to do later is to deserialize the JSON string back into an anonymous object. Something like this...
var obj2 = serializer.Deserialize(output, object);
But the serializer.Deserialize() method requires a second parameter that is the type of object it will deserialize to.
I tried this...
var obj2 = serializer.Deserialize(output, obj.GetType());
But this produces an error:
No parameterless constructor defined for type of '<>f__AnonymousType0`2[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'.
I'm not sure what this error means.
how about dynamics, the fastest way I see is this:
dynamic myObject = JsonConvert.DeserializeObject<dynamic>(output);
decimal Amount = Convert.ToDecimal(myObject.Amount);
string Message = myObject.Message;
Note:
You will need Newtonsoft.json.dll reference
JSON.Net is a powerful library to work with JSON in .Net
There's a method DeserializeAnonymousType you can tap in to.
Update: Json.Net is now included with ASP.Net, however my latest favorite that I use is JsonFX. It's got great linq support as well, check it out.
Update 2: I've moved on from JsonFX, and currently use ServiceStack.Text, it's fast!
How about using the DeserializeObject method, it does not require a specific type. This also solved a similar SO question. The method deserializes to a Dictionary<string, object> containing name/value pairs.
Update: to clarify the error you get when doing this:
var obj2 = serializer.Deserialize(output, obj.GetType());
Given the type of obj, Deserialize will try to create a new instance of the type using a default constructor. Anonymous types in C# does not have a public parameterless constructor, and thus the operation fails.
This can also be done using the in-built JavaScriptSerializer, as follows:
object result = new JavaScriptSerializer().DeserializeObject(JSONData);
This will return an object[] instance, with Key-Value pairs.
Recently I have been using the awesome JsonFx.Net library and I've come to appreciate what it does. You can use Nuget Package Manager to install it right inside Visual Studio.
The code goes like this,
var reader = new JsonReader();
string input = #"{ ""first"": ""Foo"", ""last"": ""Bar"" }";
var template = new { first=String.Empty, middle=String.Empty, last=String.Empty };
var output = reader.Read(input, template);
As you can see you can even specify the template for Anonymous Type.
if you use Newtonsoft.Json you can try DeserializeAnonymousType method
var obj1 = new { Amount = 108, Message = "Hello" };
var json=JsonConvert.SerializeObject(obj1);
// or as well
var json= "{ \"Amount\" : 108, \"Message\" : \"Hello\" }";
//Deserialization
var definition = new { Amount = 0, Message = "" };
//obj2 type is "anonymous"
var obj2 = JsonConvert.DeserializeAnonymousType(json,definition);
result
{ Amount = 108, Message = "Hello" }
If you do not want to manually provide the type i found the easyest way is just:
var jsonString = JsonHelper.SerializeObject(item);
ExpandoObject obj = JsonHelper.DeserializeObject<ExpandoObject>(jsonString);
You can use JObject instead to deserialize the JSON string:
using Newtonsoft.Json.Linq;
string output = "{\"Amount\" = 108, \"Message\" = \"Hello\"}";
var amount = JObject.Parse(output)["Amount"];