Is there a way to deserialize JSON content into a C# dynamic type? It would be nice to skip creating a bunch of classes in order to use the DataContractJsonSerializer.
If you are happy to have a dependency upon the System.Web.Helpers assembly, then you can use the Json class:
dynamic data = Json.Decode(json);
It is included with the MVC framework as an additional download to the .NET 4 framework. Be sure to give Vlad an upvote if that's helpful! However if you cannot assume the client environment includes this DLL, then read on.
An alternative deserialisation approach is suggested here. I modified the code slightly to fix a bug and suit my coding style. All you need is this code and a reference to System.Web.Extensions from your project:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Web.Script.Serialization;
public sealed class DynamicJsonConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
return type == typeof(object) ? new DynamicJsonObject(dictionary) : null;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
public override IEnumerable<Type> SupportedTypes
{
get { return new ReadOnlyCollection<Type>(new List<Type>(new[] { typeof(object) })); }
}
#region Nested type: DynamicJsonObject
private sealed class DynamicJsonObject : DynamicObject
{
private readonly IDictionary<string, object> _dictionary;
public DynamicJsonObject(IDictionary<string, object> dictionary)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
_dictionary = dictionary;
}
public override string ToString()
{
var sb = new StringBuilder("{");
ToString(sb);
return sb.ToString();
}
private void ToString(StringBuilder sb)
{
var firstInDictionary = true;
foreach (var pair in _dictionary)
{
if (!firstInDictionary)
sb.Append(",");
firstInDictionary = false;
var value = pair.Value;
var name = pair.Key;
if (value is string)
{
sb.AppendFormat("{0}:\"{1}\"", name, value);
}
else if (value is IDictionary<string, object>)
{
new DynamicJsonObject((IDictionary<string, object>)value).ToString(sb);
}
else if (value is ArrayList)
{
sb.Append(name + ":[");
var firstInArray = true;
foreach (var arrayValue in (ArrayList)value)
{
if (!firstInArray)
sb.Append(",");
firstInArray = false;
if (arrayValue is IDictionary<string, object>)
new DynamicJsonObject((IDictionary<string, object>)arrayValue).ToString(sb);
else if (arrayValue is string)
sb.AppendFormat("\"{0}\"", arrayValue);
else
sb.AppendFormat("{0}", arrayValue);
}
sb.Append("]");
}
else
{
sb.AppendFormat("{0}:{1}", name, value);
}
}
sb.Append("}");
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (!_dictionary.TryGetValue(binder.Name, out result))
{
// return null to avoid exception. caller can check for null this way...
result = null;
return true;
}
result = WrapResultObject(result);
return true;
}
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
if (indexes.Length == 1 && indexes[0] != null)
{
if (!_dictionary.TryGetValue(indexes[0].ToString(), out result))
{
// return null to avoid exception. caller can check for null this way...
result = null;
return true;
}
result = WrapResultObject(result);
return true;
}
return base.TryGetIndex(binder, indexes, out result);
}
private static object WrapResultObject(object result)
{
var dictionary = result as IDictionary<string, object>;
if (dictionary != null)
return new DynamicJsonObject(dictionary);
var arrayList = result as ArrayList;
if (arrayList != null && arrayList.Count > 0)
{
return arrayList[0] is IDictionary<string, object>
? new List<object>(arrayList.Cast<IDictionary<string, object>>().Select(x => new DynamicJsonObject(x)))
: new List<object>(arrayList.Cast<object>());
}
return result;
}
}
#endregion
}
You can use it like this:
string json = ...;
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new[] { new DynamicJsonConverter() });
dynamic obj = serializer.Deserialize(json, typeof(object));
So, given a JSON string:
{
"Items":[
{ "Name":"Apple", "Price":12.3 },
{ "Name":"Grape", "Price":3.21 }
],
"Date":"21/11/2010"
}
The following code will work at runtime:
dynamic data = serializer.Deserialize(json, typeof(object));
data.Date; // "21/11/2010"
data.Items.Count; // 2
data.Items[0].Name; // "Apple"
data.Items[0].Price; // 12.3 (as a decimal)
data.Items[1].Name; // "Grape"
data.Items[1].Price; // 3.21 (as a decimal)
It's pretty simple using Json.NET:
dynamic stuff = JsonConvert.DeserializeObject("{ 'Name': 'Jon Smith', 'Address': { 'City': 'New York', 'State': 'NY' }, 'Age': 42 }");
string name = stuff.Name;
string address = stuff.Address.City;
Also using Newtonsoft.Json.Linq:
dynamic stuff = JObject.Parse("{ 'Name': 'Jon Smith', 'Address': { 'City': 'New York', 'State': 'NY' }, 'Age': 42 }");
string name = stuff.Name;
string address = stuff.Address.City;
Documentation: Querying JSON with dynamic
You can do this using System.Web.Helpers.Json - its Decode method returns a dynamic object which you can traverse as you like.
It's included in the System.Web.Helpers assembly (.NET 4.0).
var dynamicObject = Json.Decode(jsonString);
.NET 4.0 has a built-in library to do this:
using System.Web.Script.Serialization;
JavaScriptSerializer jss = new JavaScriptSerializer();
var d = jss.Deserialize<dynamic>(str);
This is the simplest way.
Simple "string JSON data" to object without any third-party DLL file:
WebClient client = new WebClient();
string getString = client.DownloadString("https://graph.facebook.com/zuck");
JavaScriptSerializer serializer = new JavaScriptSerializer();
dynamic item = serializer.Deserialize<object>(getString);
string name = item["name"];
//note: JavaScriptSerializer in this namespaces
//System.Web.Script.Serialization.JavaScriptSerializer
Note: You can also using your custom object.
Personel item = serializer.Deserialize<Personel>(getString);
You can achieve that with the help of Newtonsoft.Json. Install it from NuGet and then:
using Newtonsoft.Json;
dynamic results = JsonConvert.DeserializeObject<dynamic>(YOUR_JSON);
JsonFx can deserialize JSON content into dynamic objects.
Serialize to/from dynamic types (default for .NET 4.0):
var reader = new JsonReader(); var writer = new JsonWriter();
string input = #"{ ""foo"": true, ""array"": [ 42, false, ""Hello!"", null ] }";
dynamic output = reader.Read(input);
Console.WriteLine(output.array[0]); // 42
string json = writer.Write(output);
Console.WriteLine(json); // {"foo":true,"array":[42,false,"Hello!",null]}
Another way using Newtonsoft.Json:
dynamic stuff = Newtonsoft.Json.JsonConvert.DeserializeObject("{ color: 'red', value: 5 }");
string color = stuff.color;
int value = stuff.value;
I came here to find an answer for .NET Core, without any third-party or additional references. It works fine if you use ExpandoObject with the standard JsonSerializer class. Here is the example that worked for me:
using System.Text.Json;
using System.Dynamic;
dynamic json = JsonSerializer.Deserialize<ExpandoObject>(jsonText);
Console.WriteLine(json.name);
This code prints out the string value of a name property that exists within the JSON text passed into the Deserialize method. Voila - no additional libraries, no nothing. Just .NET core.
Edit: May have a problem for several levels of json with nested elements. Worked for a single-level flat object.
I made a new version of the DynamicJsonConverter that uses Expando Objects. I used expando objects, because I wanted to Serialize the dynamic back into JSON using Json.NET.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Dynamic;
using System.Web.Script.Serialization;
public static class DynamicJson
{
public static dynamic Parse(string json)
{
JavaScriptSerializer jss = new JavaScriptSerializer();
jss.RegisterConverters(new JavaScriptConverter[] { new DynamicJsonConverter() });
dynamic glossaryEntry = jss.Deserialize(json, typeof(object)) as dynamic;
return glossaryEntry;
}
class DynamicJsonConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
var result = ToExpando(dictionary);
return type == typeof(object) ? result : null;
}
private static ExpandoObject ToExpando(IDictionary<string, object> dictionary)
{
var result = new ExpandoObject();
var dic = result as IDictionary<String, object>;
foreach (var item in dictionary)
{
var valueAsDic = item.Value as IDictionary<string, object>;
if (valueAsDic != null)
{
dic.Add(item.Key, ToExpando(valueAsDic));
continue;
}
var arrayList = item.Value as ArrayList;
if (arrayList != null && arrayList.Count > 0)
{
dic.Add(item.Key, ToExpando(arrayList));
continue;
}
dic.Add(item.Key, item.Value);
}
return result;
}
private static ArrayList ToExpando(ArrayList obj)
{
ArrayList result = new ArrayList();
foreach (var item in obj)
{
var valueAsDic = item as IDictionary<string, object>;
if (valueAsDic != null)
{
result.Add(ToExpando(valueAsDic));
continue;
}
var arrayList = item as ArrayList;
if (arrayList != null && arrayList.Count > 0)
{
result.Add(ToExpando(arrayList));
continue;
}
result.Add(item);
}
return result;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
public override IEnumerable<Type> SupportedTypes
{
get { return new ReadOnlyCollection<Type>(new List<Type>(new[] { typeof(object) })); }
}
}
}
Creating dynamic objects with Newtonsoft.Json works really great.
//json is your string containing the JSON value
dynamic data = JsonConvert.DeserializeObject<dynamic>(json);
Now you can access the data object just like if it was a regular object. This is the JSON object we currently have as an example:
{ "ID":123,"Name":"Jack","Numbers":[1, 2, 3] }
This is how you access it after deserialization:
data.ID //Retrieve the int
data.Name //Retrieve the string
data.Numbers[0] //Retrieve the first element in the array
I use http://json2csharp.com/ to get a class representing the JSON object.
Input:
{
"name":"John",
"age":31,
"city":"New York",
"Childs":[
{
"name":"Jim",
"age":11
},
{
"name":"Tim",
"age":9
}
]
}
Output:
public class Child
{
public string name { get; set; }
public int age { get; set; }
}
public class Person
{
public string name { get; set; }
public int age { get; set; }
public string city { get; set; }
public List<Child> Childs { get; set; }
}
After that I use Newtonsoft.Json to fill the class:
using Newtonsoft.Json;
namespace GitRepositoryCreator.Common
{
class JObjects
{
public static string Get(object p_object)
{
return JsonConvert.SerializeObject(p_object);
}
internal static T Get<T>(string p_object)
{
return JsonConvert.DeserializeObject<T>(p_object);
}
}
}
You can call it like this:
Person jsonClass = JObjects.Get<Person>(stringJson);
string stringJson = JObjects.Get(jsonClass);
PS:
If your JSON variable name is not a valid C# name (name starts with $) you can fix that like this:
public class Exception
{
[JsonProperty(PropertyName = "$id")]
public string id { get; set; }
public object innerException { get; set; }
public string message { get; set; }
public string typeName { get; set; }
public string typeKey { get; set; }
public int errorCode { get; set; }
public int eventId { get; set; }
}
The simplest way is:
Just include this DLL file.
Use the code like this:
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
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.
Another option is to "Paste JSON as classes" so it can be deserialised quick and easy.
Simply copy your entire JSON
In Visual Studio: Click Edit → Paste Special → Paste JSON as classes
Here is a better explanation n piccas... ‘Paste JSON As Classes’ in ASP.NET and Web Tools 2012.2 RC
You can extend the JavaScriptSerializer to recursively copy the dictionary it created to expando object(s) and then use them dynamically:
static class JavaScriptSerializerExtensions
{
public static dynamic DeserializeDynamic(this JavaScriptSerializer serializer, string value)
{
var dictionary = serializer.Deserialize<IDictionary<string, object>>(value);
return GetExpando(dictionary);
}
private static ExpandoObject GetExpando(IDictionary<string, object> dictionary)
{
var expando = (IDictionary<string, object>)new ExpandoObject();
foreach (var item in dictionary)
{
var innerDictionary = item.Value as IDictionary<string, object>;
if (innerDictionary != null)
{
expando.Add(item.Key, GetExpando(innerDictionary));
}
else
{
expando.Add(item.Key, item.Value);
}
}
return (ExpandoObject)expando;
}
}
Then you just need to having a using statement for the namespace you defined the extension in (consider just defining them in System.Web.Script.Serialization... another trick is to not use a namespace, then you don't need the using statement at all) and you can consume them like so:
var serializer = new JavaScriptSerializer();
var value = serializer.DeserializeDynamic("{ 'Name': 'Jon Smith', 'Address': { 'City': 'New York', 'State': 'NY' }, 'Age': 42 }");
var name = (string)value.Name; // Jon Smith
var age = (int)value.Age; // 42
var address = value.Address;
var city = (string)address.City; // New York
var state = (string)address.State; // NY
You can use using Newtonsoft.Json
var jRoot =
JsonConvert.DeserializeObject<dynamic>(Encoding.UTF8.GetString(resolvedEvent.Event.Data));
resolvedEvent.Event.Data is my response getting from calling core Event .
Try this:
var units = new { Name = "Phone", Color= "White" };
var jsonResponse = JsonConvert.DeserializeAnonymousType(json, units);
I am using like this in my code and it's working fine
using System.Web.Script.Serialization;
JavaScriptSerializer oJS = new JavaScriptSerializer();
RootObject oRootObject = new RootObject();
oRootObject = oJS.Deserialize<RootObject>(Your JSon String);
Look at the article I wrote on CodeProject, one that answers the question precisely:
Dynamic types with JSON.NET
There is way too much for re-posting it all here, and even less point since that article has an attachment with the key/required source file.
For that I would use JSON.NET to do the low-level parsing of the JSON stream and then build up the object hierarchy out of instances of the ExpandoObject class.
To get an ExpandoObject:
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
Container container = JsonConvert.Deserialize<Container>(jsonAsString, new ExpandoObjectConverter());
Deserializing in JSON.NET can be dynamic using the JObject class, which is included in that library. My JSON string represents these classes:
public class Foo {
public int Age {get;set;}
public Bar Bar {get;set;}
}
public class Bar {
public DateTime BDay {get;set;}
}
Now we deserialize the string WITHOUT referencing the above classes:
var dyn = JsonConvert.DeserializeObject<JObject>(jsonAsFooString);
JProperty propAge = dyn.Properties().FirstOrDefault(i=>i.Name == "Age");
if(propAge != null) {
int age = int.Parse(propAge.Value.ToString());
Console.WriteLine("age=" + age);
}
//or as a one-liner:
int myage = int.Parse(dyn.Properties().First(i=>i.Name == "Age").Value.ToString());
Or if you want to go deeper:
var propBar = dyn.Properties().FirstOrDefault(i=>i.Name == "Bar");
if(propBar != null) {
JObject o = (JObject)propBar.First();
var propBDay = o.Properties().FirstOrDefault (i => i.Name=="BDay");
if(propBDay != null) {
DateTime bday = DateTime.Parse(propBDay.Value.ToString());
Console.WriteLine("birthday=" + bday.ToString("MM/dd/yyyy"));
}
}
//or as a one-liner:
DateTime mybday = DateTime.Parse(((JObject)dyn.Properties().First(i=>i.Name == "Bar").First()).Properties().First(i=>i.Name == "BDay").Value.ToString());
See post for a complete example.
The object you want DynamicJSONObject is included in the System.Web.Helpers.dll from the ASP.NET Web Pages package, which is part of WebMatrix.
There is a lightweight JSON library for C# called SimpleJson.
It supports .NET 3.5+, Silverlight and Windows Phone 7.
It supports dynamic for .NET 4.0
It can also be installed as a NuGet package
Install-Package SimpleJson
Use DataSet(C#) with JavaScript. A simple function for creating a JSON stream with DataSet input. Create JSON content like (multi table dataset):
[[{a:1,b:2,c:3},{a:3,b:5,c:6}],[{a:23,b:45,c:35},{a:58,b:59,c:45}]]
Just client side, use eval. For example,
var d = eval('[[{a:1,b:2,c:3},{a:3,b:5,c:6}],[{a:23,b:45,c:35},{a:58,b:59,c:45}]]')
Then use:
d[0][0].a // out 1 from table 0 row 0
d[1][1].b // out 59 from table 1 row 1
// Created by Behnam Mohammadi And Saeed Ahmadian
public string jsonMini(DataSet ds)
{
int t = 0, r = 0, c = 0;
string stream = "[";
for (t = 0; t < ds.Tables.Count; t++)
{
stream += "[";
for (r = 0; r < ds.Tables[t].Rows.Count; r++)
{
stream += "{";
for (c = 0; c < ds.Tables[t].Columns.Count; c++)
{
stream += ds.Tables[t].Columns[c].ToString() + ":'" +
ds.Tables[t].Rows[r][c].ToString() + "',";
}
if (c>0)
stream = stream.Substring(0, stream.Length - 1);
stream += "},";
}
if (r>0)
stream = stream.Substring(0, stream.Length - 1);
stream += "],";
}
if (t>0)
stream = stream.Substring(0, stream.Length - 1);
stream += "];";
return stream;
}
How to parse easy JSON content with dynamic & JavaScriptSerializer
Please add reference of System.Web.Extensions and add this namespace using System.Web.Script.Serialization; at top:
public static void EasyJson()
{
var jsonText = #"{
""some_number"": 108.541,
""date_time"": ""2011-04-13T15:34:09Z"",
""serial_number"": ""SN1234""
}";
var jss = new JavaScriptSerializer();
var dict = jss.Deserialize<dynamic>(jsonText);
Console.WriteLine(dict["some_number"]);
Console.ReadLine();
}
How to parse nested & complex json with dynamic & JavaScriptSerializer
Please add reference of System.Web.Extensions and add this namespace using System.Web.Script.Serialization; at top:
public static void ComplexJson()
{
var jsonText = #"{
""some_number"": 108.541,
""date_time"": ""2011-04-13T15:34:09Z"",
""serial_number"": ""SN1234"",
""more_data"": {
""field1"": 1.0,
""field2"": ""hello""
}
}";
var jss = new JavaScriptSerializer();
var dict = jss.Deserialize<dynamic>(jsonText);
Console.WriteLine(dict["some_number"]);
Console.WriteLine(dict["more_data"]["field2"]);
Console.ReadLine();
}
I want to do this programmatically in unit tests, I do have the luxury of typing it out.
My solution is:
var dict = JsonConvert.DeserializeObject<ExpandoObject>(json) as IDictionary<string, object>;
Now I can assert that
dict.ContainsKey("ExpectedProperty");
With Cinchoo ETL - an open source library available to parse JSON into a dynamic object:
string json = #"{
""key1"": [
{
""action"": ""open"",
""timestamp"": ""2018-09-05 20:46:00"",
""url"": null,
""ip"": ""66.102.6.98""
}
]
}";
using (var p = ChoJSONReader.LoadText(json)
.WithJSONPath("$..key1")
)
{
foreach (var rec in p)
{
Console.WriteLine("Action: " + rec.action);
Console.WriteLine("Timestamp: " + rec.timestamp);
Console.WriteLine("URL: " + rec.url);
Console.WriteLine("IP address: " + rec.ip);
}
}
Output:
Action: open
Timestamp: 2018-09-05 20:46:00
URL: http://www.google.com
IP address: 66.102.6.98
Sample fiddle: https://dotnetfiddle.net/S0ehSV
For more information, please visit codeproject articles
Disclaimer: I'm the author of this library.
try this way!
JSON example:
[{
"id": 140,
"group": 1,
"text": "xxx",
"creation_date": 123456,
"created_by": "xxx#gmail.co",
"tags": ["xxxxx"]
}, {
"id": 141,
"group": 1,
"text": "xxxx",
"creation_date": 123456,
"created_by": "xxx#gmail.com",
"tags": ["xxxxx"]
}]
C# code:
var jsonString = (File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(),"delete_result.json")));
var objects = JsonConvert.DeserializeObject<dynamic>(jsonString);
foreach(var o in objects)
{
Console.WriteLine($"{o.id.ToString()}");
}
I really like System.Web.Helpers,
dynamic data = Json.Decode(json);
as it supports usage like
var val = data.Members.NumberTen;
or
var val data.Members["10"];
The reference to System.Web.Helpers.DLL is really crazy, it is not even console and desktop app friendly. Here is my attempt to extract the same functionalities as a standalone file directly from https://github.com/mono/aspnetwebstack/tree/master/src/System.Web.Helpers
(Share this as for education purpose only)
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Dynamic;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using Microsoft.CSharp.RuntimeBinder;
using System.Web.Script.Serialization;
using System.IO;
using System.Collections;
using System.Linq;
using System.Globalization;
namespace System.Web.Helpers
{
public static class Json
{
private static readonly JavaScriptSerializer _serializer = CreateSerializer();
public static string Encode(object value)
{
// Serialize our dynamic array type as an array
DynamicJsonArray jsonArray = value as DynamicJsonArray;
if (jsonArray != null)
{
return _serializer.Serialize((object[])jsonArray);
}
return _serializer.Serialize(value);
}
public static void Write(object value, TextWriter writer)
{
writer.Write(_serializer.Serialize(value));
}
public static dynamic Decode(string value)
{
return WrapObject(_serializer.DeserializeObject(value));
}
public static dynamic Decode(string value, Type targetType)
{
return WrapObject(_serializer.Deserialize(value, targetType));
}
public static T Decode<T>(string value)
{
return _serializer.Deserialize<T>(value);
}
private static JavaScriptSerializer CreateSerializer()
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new[] { new DynamicJavaScriptConverter() });
return serializer;
}
internal class DynamicJavaScriptConverter : JavaScriptConverter
{
public override IEnumerable<Type> SupportedTypes
{
get
{
yield return typeof(IDynamicMetaObjectProvider);
yield return typeof(DynamicObject);
}
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
throw new NotSupportedException();
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
IEnumerable<string> memberNames = DynamicHelper.GetMemberNames(obj);
foreach (string item in memberNames)
{
dictionary[item] = DynamicHelper.GetMemberValue(obj, item);
}
return dictionary;
}
}
internal static dynamic WrapObject(object value)
{
// The JavaScriptSerializer returns IDictionary<string, object> for objects
// and object[] for arrays, so we wrap those in different dynamic objects
// so we can access the object graph using dynamic
var dictionaryValues = value as IDictionary<string, object>;
if (dictionaryValues != null)
{
return new DynamicJsonObject(dictionaryValues);
}
var arrayValues = value as object[];
if (arrayValues != null)
{
return new DynamicJsonArray(arrayValues);
}
return value;
}
}
// REVIEW: Consider implementing ICustomTypeDescriptor and IDictionary<string, object>
public class DynamicJsonObject : DynamicObject
{
private readonly IDictionary<string, object> _values;
public DynamicJsonObject(IDictionary<string, object> values)
{
Debug.Assert(values != null);
_values = values.ToDictionary(p => p.Key, p => Json.WrapObject(p.Value),
StringComparer.OrdinalIgnoreCase);
}
public override bool TryConvert(ConvertBinder binder, out object result)
{
result = null;
if (binder.Type.IsAssignableFrom(_values.GetType()))
{
result = _values;
}
else
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "HelpersResources.Json_UnableToConvertType", binder.Type));
}
return true;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = GetValue(binder.Name);
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_values[binder.Name] = Json.WrapObject(value);
return true;
}
public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
{
string key = GetKey(indexes);
if (!String.IsNullOrEmpty(key))
{
_values[key] = Json.WrapObject(value);
}
return true;
}
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
string key = GetKey(indexes);
result = null;
if (!String.IsNullOrEmpty(key))
{
result = GetValue(key);
}
return true;
}
private static string GetKey(object[] indexes)
{
if (indexes.Length == 1)
{
return (string)indexes[0];
}
// REVIEW: Should this throw?
return null;
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return _values.Keys;
}
private object GetValue(string name)
{
object result;
if (_values.TryGetValue(name, out result))
{
return result;
}
return null;
}
}
[SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification = "This class isn't meant to be used directly")]
public class DynamicJsonArray : DynamicObject, IEnumerable<object>
{
private readonly object[] _arrayValues;
public DynamicJsonArray(object[] arrayValues)
{
Debug.Assert(arrayValues != null);
_arrayValues = arrayValues.Select(Json.WrapObject).ToArray();
}
public int Length
{
get { return _arrayValues.Length; }
}
public dynamic this[int index]
{
get { return _arrayValues[index]; }
set { _arrayValues[index] = Json.WrapObject(value); }
}
public override bool TryConvert(ConvertBinder binder, out object result)
{
if (_arrayValues.GetType().IsAssignableFrom(binder.Type))
{
result = _arrayValues;
return true;
}
return base.TryConvert(binder, out result);
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
// Testing for members should never throw. This is important when dealing with
// services that return different json results. Testing for a member shouldn't throw,
// it should just return null (or undefined)
result = null;
return true;
}
public IEnumerator GetEnumerator()
{
return _arrayValues.GetEnumerator();
}
private IEnumerable<object> GetEnumerable()
{
return _arrayValues.AsEnumerable();
}
IEnumerator<object> IEnumerable<object>.GetEnumerator()
{
return GetEnumerable().GetEnumerator();
}
[SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates", Justification = "This class isn't meant to be used directly")]
public static implicit operator object[](DynamicJsonArray obj)
{
return obj._arrayValues;
}
[SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates", Justification = "This class isn't meant to be used directly")]
public static implicit operator Array(DynamicJsonArray obj)
{
return obj._arrayValues;
}
}
/// <summary>
/// Helper to evaluate different method on dynamic objects
/// </summary>
public static class DynamicHelper
{
// We must pass in "object" instead of "dynamic" for the target dynamic object because if we use dynamic, the compiler will
// convert the call to this helper into a dynamic expression, even though we don't need it to be. Since this class is internal,
// it cannot be accessed from a dynamic expression and thus we get errors.
// Dev10 Bug 914027 - Changed the first parameter from dynamic to object, see comment at top for details
public static bool TryGetMemberValue(object obj, string memberName, out object result)
{
try
{
result = GetMemberValue(obj, memberName);
return true;
}
catch (RuntimeBinderException)
{
}
catch (RuntimeBinderInternalCompilerException)
{
}
// We catch the C# specific runtime binder exceptions since we're using the C# binder in this case
result = null;
return false;
}
// Dev10 Bug 914027 - Changed the first parameter from dynamic to object, see comment at top for details
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We want to swallow exceptions that happen during runtime binding")]
public static bool TryGetMemberValue(object obj, GetMemberBinder binder, out object result)
{
try
{
// VB us an instance of GetBinderAdapter that does not implement FallbackGetMemeber. This causes lookup of property expressions on dynamic objects to fail.
// Since all types are private to the assembly, we assume that as long as they belong to CSharp runtime, it is the right one.
if (typeof(Binder).Assembly.Equals(binder.GetType().Assembly))
{
// Only use the binder if its a C# binder.
result = GetMemberValue(obj, binder);
}
else
{
result = GetMemberValue(obj, binder.Name);
}
return true;
}
catch
{
result = null;
return false;
}
}
// Dev10 Bug 914027 - Changed the first parameter from dynamic to object, see comment at top for details
public static object GetMemberValue(object obj, string memberName)
{
var callSite = GetMemberAccessCallSite(memberName);
return callSite.Target(callSite, obj);
}
// Dev10 Bug 914027 - Changed the first parameter from dynamic to object, see comment at top for details
public static object GetMemberValue(object obj, GetMemberBinder binder)
{
var callSite = GetMemberAccessCallSite(binder);
return callSite.Target(callSite, obj);
}
// dynamic d = new object();
// object s = d.Name;
// The following code gets generated for this expression:
// callSite = CallSite<Func<CallSite, object, object>>.Create(Binder.GetMember(CSharpBinderFlags.None, "Name", typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));
// callSite.Target(callSite, d);
// typeof(Program) is the containing type of the dynamic operation.
// Dev10 Bug 914027 - Changed the callsite's target parameter from dynamic to object, see comment at top for details
public static CallSite<Func<CallSite, object, object>> GetMemberAccessCallSite(string memberName)
{
var binder = Binder.GetMember(CSharpBinderFlags.None, memberName, typeof(DynamicHelper), new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) });
return GetMemberAccessCallSite(binder);
}
// Dev10 Bug 914027 - Changed the callsite's target parameter from dynamic to object, see comment at top for details
public static CallSite<Func<CallSite, object, object>> GetMemberAccessCallSite(CallSiteBinder binder)
{
return CallSite<Func<CallSite, object, object>>.Create(binder);
}
// Dev10 Bug 914027 - Changed the first parameter from dynamic to object, see comment at top for details
public static IEnumerable<string> GetMemberNames(object obj)
{
var provider = obj as IDynamicMetaObjectProvider;
Debug.Assert(provider != null, "obj doesn't implement IDynamicMetaObjectProvider");
Expression parameter = Expression.Parameter(typeof(object));
return provider.GetMetaObject(parameter).GetDynamicMemberNames();
}
}
}
Related
I want create a customized JSON string like this:
{"service1":"hello"}
( I simplified the example. In reality the required JSON is more complex.
But to explain the problem, this example is fine )
My problem is that the service name "service1" is contained in a variable
This is my code:
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema;
using Newtonsoft.Json;
public static string CreateCustomJSON(string serviceName, object value)
{
var v = new { serviceName = value };
string json = JsonConvert.SerializeObject(v);
Console.WriteLine(json);
return json;
}
CreateCustomJSON("service1", "hello");
CreateCustomJSON("service2", "John");
CreateCustomJSON("service3", 13);
I got this result:
{"serviceName":"hello"}
{"serviceName":"John"}
{"serviceName":13}
because i do not know how to use Anonymous Types properly
The error is in this line:
var v = new { serviceName = value };
Or maybe there is another way to follow,
to build a customized json string
Can you help me?
Use a Dictionary<string,string> for this. Json object are dictionaries after all. Try it online!
public static string CreateCustomJSON(string serviceName, string value)
{
var v = new Dictionary<string,string> {{serviceName, value}};
string json = JsonConvert.SerializeObject(v);
Console.WriteLine(json);
return json;
}
public static void Main()
{
CreateCustomJSON("service1", "hello");
CreateCustomJSON("service2", "John");
}
output:
{"service1":"hello"}
{"service2":"John"}
You can simply use a string to return that ...
public static string CreateCustomJSON(string serviceName, string value)
{
var json = $"{{ \"{serviceName}\":\"{ value}\" }}";
Console.WriteLine(json);
return json;
}
Unless the required JSON is more complex then you can use reflection
maybe you can use ExpandoObject. Try this if fits your needs
public static void AddPropertyToObject(ExpandoObject o, string propertyName, object propertyValue)
{
IDictionary<string, object> d = o as IDictionary<string, object>;
if (d == null) return;
if (!d.ContainsKey(propertyName))
{
d.Add(propertyName, propertyValue);
}
else
{
d[propertyName] = propertyValue;
}
}
And then
dynamic eo = new ExpandoObject();
AddPropertyToObject(eo, "test", "fsdf");
string json = JsonConvert.SerializeObject(eo);
You could use a Dictionary
var x = new Dictionary<string,string>();
x.Add ("service1", "val1");
Edit - full example
public static string CreateCustomJSON(string serviceName, string value)
{
var x = new Dictionary<string, string>();
x.Add(serviceName, value);
return JsonConvert.SerializeObject(x);
}
public static void Main()
{
Console.WriteLine(CreateCustomJSON("service1", "hello"));
Console.WriteLine(CreateCustomJSON("service2", "John"));
}
Retult:
I use a template engine that renders templates from c# objects (nested). I would like to reflect and figure out which properties / objects are used in each template string.
An ideal way would be to build a "dummy" object representing the right shape and render this in the template. I would then inspect this object afterwards to find out which properties were accessed. This would allow me to keep this logic independant of the template library.
Any idea how i might implement this? The expando object is built dynamically like this:
var dynamicObject = new ExpandoObject() as IDictionary<string, Object>;
foreach (var property in properties) {
dynamicObject.Add(property.Key,property.Value);
}
Had some ideas along these lines:
public class DummyObject {
public DummyObject() {
Accessed = new Dictionary<string, bool>();
}
public Dictionary<string, bool> Accessed;
object MyProp {
get {
Accessed["MyProp"] = true;
return "";
}
}
}
But this custom property obviously doesn't work with the dictionary / expando object. Any ideas of a route forward here?
You can override the TryGetMember method on DynamicObject:
public sealed class LoggedPropertyAccess : DynamicObject {
public readonly HashSet<string> accessedPropertyNames = new HashSet<string>();
public override bool TryGetMember(GetMemberBinder binder, out object result) {
accessedPropertyNames.Add(binder.Name);
result = "";
return true;
}
}
and then the following will output the accessed property names
dynamic testObject = new LoggedPropertyAccess();
string firstname = testObject.FirstName;
string lastname = testObject.LastName;
foreach (var propertyName in testObject.accessedPropertyNames) {
Console.WriteLine(propertyName);
}
Console.ReadKey();
N.B. There is still an issue here -- this works only as long as the template library expects only strings from the properties. The following code will fail, because every property will return a string:
DateTime dob = testObject.DOB;
In order to resolve this, and also allow for nested objects, have TryGetMember return a new instance of LoggedPropertyAccess. Then, you can override the TryConvert method as well; where you can return different values based on the conversion to different types (complete code):
using System;
using System.Collections.Generic;
using System.Dynamic;
namespace DynamicObjectGetterOverride {
public sealed class LoggedPropertyAccess : DynamicObject {
public readonly Dictionary<string, object> __Properties = new Dictionary<string, object>();
public readonly HashSet<string> __AccessedProperties = new HashSet<string>();
public override bool TryGetMember(GetMemberBinder binder, out object result) {
if (!__Properties.TryGetValue(binder.Name, out result)) {
var ret = new LoggedPropertyAccess();
__Properties[binder.Name] = ret;
result = ret;
}
__AccessedProperties.Add(binder.Name);
return true;
}
//this allows for setting values which aren't instances of LoggedPropertyAccess
public override bool TrySetMember(SetMemberBinder binder, object value) {
__Properties[binder.Name] = value;
return true;
}
private static Dictionary<Type, Func<object>> typeActions = new Dictionary<Type, Func<object>>() {
{typeof(string), () => "dummy string" },
{typeof(int), () => 42 },
{typeof(DateTime), () => DateTime.Today }
};
public override bool TryConvert(ConvertBinder binder, out object result) {
if (typeActions.TryGetValue(binder.Type, out var action)) {
result = action();
return true;
}
return base.TryConvert(binder, out result);
}
}
}
and use as follows:
using System;
using static System.Console;
namespace DynamicObjectGetterOverride {
class Program {
static void Main(string[] args) {
dynamic testObject = new LoggedPropertyAccess();
DateTime dob = testObject.DOB;
string firstname = testObject.FirstName;
string lastname = testObject.LastName;
dynamic address = testObject.Address;
address.House = "123";
address.Street = "AnyStreet";
address.City = "Anytown";
address.State = "ST";
address.Country = "USA";
WriteLine("----- Writes the returned values from reading the properties");
WriteLine(new { firstname, lastname, dob });
WriteLine();
WriteLine("----- Writes the actual values of each property");
foreach (var kvp in testObject.__Properties) {
WriteLine($"{kvp.Key} = {kvp.Value}");
}
WriteLine();
WriteLine("----- Writes the actual values of a nested object");
foreach (var kvp in testObject.Address.__Properties) {
WriteLine($"{kvp.Key} = {kvp.Value}");
}
WriteLine();
WriteLine("----- Writes the names of the accessed properties");
foreach (var propertyName in testObject.__AccessedProperties) {
WriteLine(propertyName);
}
ReadKey();
}
}
}
I have an issue while deserializing json data which can have both float or array type of data. The same issue from here
Dealing with JSON field that holds different types in C#
But everywhere the solution is to use json.net with a JsonConverter. I need to achieve the deserialization using only System.Web.Script.Serialization.JavaScriptSerializer in c#. Can anyone help, pls?
You can use a JavaScriptConverter for this purpose. However, unlike Json.NET's JsonConverter a JavaScriptConverter can only be used for types that map from and to a JSON object -- not an array or primitive type. Thus you will need to create a custom converter for any object that may contain a polymorphic property that could be an array or singleton item.
Let's imagine you have JSON that looks like the following:
{
"name": "my name",
"data": {
"foo": "Foo",
"bar": "Bar"
},
"values": [
3.14,
2.718
]
}
Where "values" might sometimes be a primitive value like so:
"values": 3.14
And, you want to map this to the following POCO:
public class RootObject
{
public string name { get; set; }
public NestedData data { get; set; }
public float[] Values { get; set; }
}
public class NestedData
{
public string foo { get; set; }
public string bar { get; set; }
}
As JavaScriptConverter.Deserialize() is passed an IDictionary<string, object> of parsed values, the steps to take are:
Detach any properties that need custom processing (keeping in mind that JavaScriptSerializer is case-insensitive but that the dictionary is not).
Generate a default deserialization for any remaining properties using JavaScriptSerializer.ConvertToType<T>() using a fresh serializer that does not contain the converter.
Manually deserialize and populate the custom properties into the partially deserialized object, and return it.
For the type shown above, the following converter, based somewhat on this answer, does the job:
class RootObjectConverter : CustomPropertiesConverter<RootObject>
{
const string ValuesName = "values";
protected override IEnumerable<string> CustomProperties
{
get { return new[] { ValuesName }; }
}
protected override void DeserializeCustomProperties(Dictionary<string, object> customDictionary, RootObject obj, JavaScriptSerializer serializer)
{
object itemCost;
if (customDictionary.TryGetValue(ValuesName, out itemCost) && itemCost != null)
obj.Values = serializer.FromSingleOrArray<float>(itemCost).ToArray();
}
protected override void SerializeCustomProperties(RootObject obj, Dictionary<string, object> dict, JavaScriptSerializer serializer)
{
obj.Values.ToSingleOrArray(dict, ValuesName);
}
}
public abstract class CustomPropertiesConverter<T> : JavaScriptConverter
{
protected abstract IEnumerable<string> CustomProperties { get; }
protected abstract void DeserializeCustomProperties(Dictionary<string, object> customDictionary, T obj, JavaScriptSerializer serializer);
protected abstract void SerializeCustomProperties(T obj, Dictionary<string, object> dict, JavaScriptSerializer serializer);
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
// Detach custom properties
var customDictionary = new Dictionary<string, object>();
foreach (var key in CustomProperties)
{
object value;
if (dictionary.TryRemoveInvariant(key, out value))
customDictionary.Add(key, value);
}
// Deserialize and populate all members other than "values"
var obj = new JavaScriptSerializer().ConvertToType<T>(dictionary);
// Populate custom properties
DeserializeCustomProperties(customDictionary, obj, serializer);
return obj;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
// Generate a default serialization. Is there an easier way to do this?
var defaultSerializer = new JavaScriptSerializer();
var dict = defaultSerializer.Deserialize<Dictionary<string, object>>(defaultSerializer.Serialize(obj));
// Remove default serializations of custom properties, if present
foreach (var key in CustomProperties)
{
dict.RemoveInvariant(key);
}
// Add custom properties
SerializeCustomProperties((T)obj, dict, serializer);
return dict;
}
public override IEnumerable<Type> SupportedTypes
{
get { return new[] { typeof(T) }; }
}
}
public static class JavaScriptSerializerObjectExtensions
{
public static void ReplaceInvariant<T>(this IDictionary<string, T> dictionary, string key, T value)
{
RemoveInvariant(dictionary, key);
dictionary.Add(key, value);
}
public static bool TryRemoveInvariant<T>(this IDictionary<string, T> dictionary, string key, out T value)
{
if (dictionary == null)
throw new ArgumentNullException();
var keys = dictionary.Keys.Where(k => string.Equals(k, key, StringComparison.OrdinalIgnoreCase)).ToArray();
if (keys.Length == 0)
{
value = default(T);
return false;
}
else if (keys.Length == 1)
{
value = dictionary[keys[0]];
dictionary.Remove(keys[0]);
return true;
}
else
{
throw new ArgumentException(string.Format("Duplicate keys found: {0}", String.Join(",", keys)));
}
}
public static void RemoveInvariant<T>(this IDictionary<string, T> dictionary, string key)
{
if (dictionary == null)
throw new ArgumentNullException();
foreach (var actualKey in dictionary.Keys.Where(k => string.Equals(k, key, StringComparison.OrdinalIgnoreCase)).ToArray())
dictionary.Remove(actualKey);
}
public static void ToSingleOrArray<T>(this ICollection<T> list, IDictionary<string, object> dictionary, string key)
{
if (dictionary == null)
throw new ArgumentNullException();
if (list == null || list.Count == 0)
dictionary.RemoveInvariant(key);
else if (list.Count == 1)
dictionary.ReplaceInvariant(key, list.First());
else
dictionary.ReplaceInvariant(key, list.ToArray());
}
public static List<T> FromSingleOrArray<T>(this JavaScriptSerializer serializer, object value)
{
if (value == null)
return null;
if (value.IsJsonArray())
{
return value.AsJsonArray().Select(i => serializer.ConvertToType<T>(i)).ToList();
}
else
{
return new List<T> { serializer.ConvertToType<T>(value) };
}
}
public static bool IsJsonArray(this object obj)
{
if (obj is string || obj is IDictionary)
return false;
return obj is IEnumerable;
}
public static IEnumerable<object> AsJsonArray(this object obj)
{
return (obj as IEnumerable).Cast<object>();
}
}
Then use it like:
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new[] { new RootObjectConverter() });
var root = serializer.Deserialize<RootObject>(json);
object with additional properties from json-like notation string should created.
Method will called from Razor view to pass colmodel to jqgrid
as json object like
#Html.Raw( Json.Encode( Model.GetColModel()))
Method should have signature like
object GetColModel(string colName, int colWidth, string additonalProperties)
For example,
GetColModel("customer", 17, "address=\"Toronto\", index=1555" )
should return object
new { colName="customer", colwidth=17, address="Toronto", index=1555 }
There may be nested properties like in JSON, eq.
GetColModel("customer", 17, "formatoptions= new { formatter=\"number\", editable=true } " )
should return object
new { colName="customer", colwidth=17, formatoptions=new {
formatter="number",
editable=true
}
}
I tried method
public object GetColModel(string colName, int colWidth, string additonalProperties)
{
return new
{
name = colName,
width = colWidth,
&addtitionalProperties
};
}
but this fails since macros are not supported in C#
How to create such method or other way to add properties from database to json strung in Razor view ?
It is called from ASP.NET/Mono C# MVC 4 viewmodel.
Razor views and RazorEngine are used.
There is nothing built in to do this, but you parse your string using string (string.Split will let you split on ',' but if you might have those in your text you will have to build a real parser, or switch you string format to something like CSV where you can find lots of parsers out there. You might be able to find a property parser for simple syntax. Or you push your additional properties string as json and use Json.net to parse.
Once you have your string parsed into a key/value structure, then you can use ExpandoObject to populate your final object and return that.
https://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject(v=vs.110).aspx
Here is a naive implementation of a true json based solution.
You can invoke it using:
dynamic d = Model.GetColModel("customer", 17, " { formatoptions : { formatter : \"number\", editable :true }, stam :2}");
Implementation:
static class ModelExtension
{
public static dynamic GetColModel(this Model model, string colName, int colWidth, string additonalProperties) {
dynamic expando = new ExpandoObject();
var json = JsonConvert.DeserializeObject<JObject>(additonalProperties);
expando.name = colName;
expando.width = colWidth;
return new FromPropertiesDynamicObjectCreator(expando, json);
}
private class FromPropertiesDynamicObjectCreator : DynamicObject
{
private readonly dynamic expando = null;
public FromPropertiesDynamicObjectCreator(IDictionary<string, object> expando, JObject props = null) {
this.expando = expando;
if (props != null) {
((dynamic)this).props = props;
}
}
public override bool TrySetMember(SetMemberBinder binder, object value) {
if (binder.Name.Equals("props")) {
var jsonObj = value as JObject;
JToken current = jsonObj.First;
var dictionary = expando as IDictionary<string, object>;
RecurseJson(current, dictionary);
return true;
}
return false;
}
private void RecurseJson(JToken current, IDictionary<string, object> dictionary) {
JToken value;
Dictionary<string, object> newDictionary;
while (current != null) {
var children = current.Children().ToList();
foreach (var child in children) {
switch (child.Type) {
case JTokenType.Object:
case JTokenType.Array:
newDictionary = new Dictionary<string, object>();
dictionary[child.Path] = newDictionary;
RecurseJson(child, newDictionary);
break;
case JTokenType.Property:
var prop = ((JProperty)child);
value = prop.Value;
if (value.HasValues) {
newDictionary = new Dictionary<string, object>();
dictionary[prop.Name] = newDictionary;
RecurseJson(child, newDictionary);
break;
}
dictionary[prop.Name] = ((dynamic)value).Value;
break;
default:
var val = ((dynamic)child).Value;
if (val is JToken) {
dictionary[child.Path] = val.Value;
}
else {
dictionary[child.Path] = val;
}
break;
}
}
current = current.Next;
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result) {
object value;
var dictionary = expando as IDictionary<string, object>;
if (dictionary.TryGetValue(binder.Name, out value)) {
var innerDictionary = value as IDictionary<string, object>;
if (innerDictionary != null) {
result = new FromPropertiesDynamicObjectCreator(innerDictionary);
}
else {
result = value;
}
return true;
}
result = null;
return true;
}
}
}
I'm trying to use JsonPath for .NET (http://code.google.com/p/jsonpath/downloads/list) and I'm having trouble finding an example of how to parse a Json string and a JsonPath string and get a result.
Has anyone used this?
The problem you are experiencing is that the C# version of JsonPath does not include a Json parser so you have to use it with another Json framework that handles serialization and deserialization.
The way JsonPath works is to use an interface called IJsonPathValueSystem to traverse parsed Json objects. JsonPath comes with a built-in BasicValueSystem that uses the IDictionary interface to represent Json objects and the IList interface to represent Json arrays.
You can create your own BasicValueSystem-compatible Json objects by constructing them using C# collection initializers but this is not of much use when your Json is coming in in the form of strings from a remote server, for example.
So if only you could take a Json string and parse it into a nested structure of IDictionary objects, IList arrays, and primitive values, you could then use JsonPath to filter it! As luck would have it, we can use Json.NET which has good serialization and deserialization capabilities to do that part of the job.
Unfortunately, Json.NET does not deserialize Json strings into a format compatible with the BasicValueSystem. So the first task for using JsonPath with Json.NET is to write a JsonNetValueSystem that implements IJsonPathValueSystem and that understands the JObject objects, JArray arrays, and JValue values that JObject.Parse produces.
So download both JsonPath and Json.NET and put them into a C# project. Then add this class to that project:
public sealed class JsonNetValueSystem : IJsonPathValueSystem
{
public bool HasMember(object value, string member)
{
if (value is JObject)
return (value as JObject).Properties().Any(property => property.Name == member);
if (value is JArray)
{
int index = ParseInt(member, -1);
return index >= 0 && index < (value as JArray).Count;
}
return false;
}
public object GetMemberValue(object value, string member)
{
if (value is JObject)
{
var memberValue = (value as JObject)[member];
return memberValue;
}
if (value is JArray)
{
int index = ParseInt(member, -1);
return (value as JArray)[index];
}
return null;
}
public IEnumerable GetMembers(object value)
{
var jobject = value as JObject;
return jobject.Properties().Select(property => property.Name);
}
public bool IsObject(object value)
{
return value is JObject;
}
public bool IsArray(object value)
{
return value is JArray;
}
public bool IsPrimitive(object value)
{
if (value == null)
throw new ArgumentNullException("value");
return value is JObject || value is JArray ? false : true;
}
private int ParseInt(string s, int defaultValue)
{
int result;
return int.TryParse(s, out result) ? result : defaultValue;
}
}
Now with all three of these pieces we can write a sample JsonPath program:
class Program
{
static void Main(string[] args)
{
var input = #"
{ ""store"": {
""book"": [
{ ""category"": ""reference"",
""author"": ""Nigel Rees"",
""title"": ""Sayings of the Century"",
""price"": 8.95
},
{ ""category"": ""fiction"",
""author"": ""Evelyn Waugh"",
""title"": ""Sword of Honour"",
""price"": 12.99
},
{ ""category"": ""fiction"",
""author"": ""Herman Melville"",
""title"": ""Moby Dick"",
""isbn"": ""0-553-21311-3"",
""price"": 8.99
},
{ ""category"": ""fiction"",
""author"": ""J. R. R. Tolkien"",
""title"": ""The Lord of the Rings"",
""isbn"": ""0-395-19395-8"",
""price"": 22.99
}
],
""bicycle"": {
""color"": ""red"",
""price"": 19.95
}
}
}
";
var json = JObject.Parse(input);
var context = new JsonPathContext { ValueSystem = new JsonNetValueSystem() };
var values = context.SelectNodes(json, "$.store.book[*].author").Select(node => node.Value);
Console.WriteLine(JsonConvert.SerializeObject(values));
Console.ReadKey();
}
}
which produces this output:
["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]
This example is based on the Javascript sample at the JsonPath site:
Javascript Usage and Example
For those that don't like LINQ (.NET 2.0):
namespace JsonPath
{
public sealed class JsonNetValueSystem : IJsonPathValueSystem
{
public bool HasMember(object value, string member)
{
if (value is Newtonsoft.Json.Linq.JObject)
{
// return (value as JObject).Properties().Any(property => property.Name == member);
foreach (Newtonsoft.Json.Linq.JProperty property in (value as Newtonsoft.Json.Linq.JObject).Properties())
{
if (property.Name == member)
return true;
}
return false;
}
if (value is Newtonsoft.Json.Linq.JArray)
{
int index = ParseInt(member, -1);
return index >= 0 && index < (value as Newtonsoft.Json.Linq.JArray).Count;
}
return false;
}
public object GetMemberValue(object value, string member)
{
if (value is Newtonsoft.Json.Linq.JObject)
{
var memberValue = (value as Newtonsoft.Json.Linq.JObject)[member];
return memberValue;
}
if (value is Newtonsoft.Json.Linq.JArray)
{
int index = ParseInt(member, -1);
return (value as Newtonsoft.Json.Linq.JArray)[index];
}
return null;
}
public System.Collections.IEnumerable GetMembers(object value)
{
System.Collections.Generic.List<string> ls = new System.Collections.Generic.List<string>();
var jobject = value as Newtonsoft.Json.Linq.JObject;
/// return jobject.Properties().Select(property => property.Name);
foreach (Newtonsoft.Json.Linq.JProperty property in jobject.Properties())
{
ls.Add(property.Name);
}
return ls;
}
public bool IsObject(object value)
{
return value is Newtonsoft.Json.Linq.JObject;
}
public bool IsArray(object value)
{
return value is Newtonsoft.Json.Linq.JArray;
}
public bool IsPrimitive(object value)
{
if (value == null)
throw new System.ArgumentNullException("value");
return value is Newtonsoft.Json.Linq.JObject || value is Newtonsoft.Json.Linq.JArray ? false : true;
}
private int ParseInt(string s, int defaultValue)
{
int result;
return int.TryParse(s, out result) ? result : defaultValue;
}
}
}
Usage:
object obj = Newtonsoft.Json.JsonConvert.DeserializeObject(input);
JsonPath.JsonPathContext context = new JsonPath.JsonPathContext { ValueSystem = new JsonPath.JsonNetValueSystem() };
foreach (JsonPath.JsonPathNode node in context.SelectNodes(obj, "$.store.book[*].author"))
{
Console.WriteLine(node.Value);
}
using Newtonsoft.Json.Linq yo can use function SelectToken and try out yous JsonPath
Check documentation https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_Linq_JObject.htm
Object jsonObj = JObject.Parse(stringResult);
JToken pathResult = jsonObj.SelectToken("results[0].example");
return pathResult.ToString();
I found that the internal JavaScriptSerializer() can work just fine as long as filtering is not required.
Using the dataset and examples from JsonPath Dataset + Examples
My source for the JsonPath implementation was GitHub
public static string SelectFromJson(string inputJsonData, string inputJsonPath)
{
// Use the serializer to deserialize the JSON but also to create the snippets
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
serializer.MaxJsonLength = Int32.MaxValue;
dynamic dynJson = serializer.Deserialize<object>(inputJsonData);
var jsonPath = new JsonPath.JsonPathContext();
var jsonResults = jsonPath.Select(dynJson, inputJsonPath);
var valueList = new List<string>();
foreach (var node in jsonResults)
{
if (node is string)
{
valueList.Add(node);
}
else
{
// If the object is too complex then return a list of JSON snippets
valueList.Add(serializer.Serialize(node));
}
}
return String.Join("\n", valueList);
}
The function is used as follows.
var result = SelectFromJson(#"
{ ""store"": {
""book"": [
{ ""category"": ""fiction"",
""author"": ""J. R. R. Tolkien"",
""title"": ""The Lord of the Rings"",
""isbn"": ""0-395-19395-8"",
""price"": 22.99
}
]
}
}", "$.store.book[*].author");