Currently my Dictionary<string, string> is serialized as:
{
"Uri" : "/site/Default.aspx",
"time-taken" : "232"
}
I would like Json.net to serialize it as
{
"Uri" : "/site/Default.aspx",
"time-taken" : 232
}
What would be the easiest way to achieve this with Json.net? I don't want to make a new class with the correct types instead of the Dictionary since the keys are many and may change. I know the keys that will be int.
I think I would just make a helper method that copied the data from a dictionary to a JObject like this:
public static class JsonHelper
{
public static string SerializeDictionary(Dictionary<string, string> dict, IEnumerable<string> intKeys)
{
JObject obj = new JObject();
foreach (var kvp in dict)
{
int intValue;
if (intKeys.Contains(kvp.Key) && int.TryParse(kvp.Value, out intValue))
obj.Add(kvp.Key, intValue);
else
obj.Add(kvp.Key, kvp.Value);
}
return obj.ToString(Formatting.Indented);
}
}
Then use it like this:
var dict = new Dictionary<string, string>();
dict.Add("AnInt", "123");
dict.Add("AString", "abc");
dict.Add("AnotherInt", "456");
dict.Add("KeepThisAsString", "789");
dict.Add("NotAnInt", "xyz");
var intKeys = new string[] { "AnInt", "AnotherInt" };
string json = JsonHelper.SerializeDictionary(dict, intKeys);
Console.WriteLine(json);
Output:
{
"AnInt": 123,
"AString": "abc",
"AnotherInt": 456,
"KeepThisAsString": "789",
"NotAnInt": "xyz"
}
Fiddle: https://dotnetfiddle.net/xdnnb0
Related
I want to write mapping function that takes in a string of json payload and another string of mappings and returns a string of the mapped data. For example a payload of:
{
"Name" : "Hello",
"This" : {
"That" : {
"TheOther" : "There"
}
}
}
And a map of:
{
"Test_Name" : "Name",
"Test_Value" : "This.That.TheOther"
}
Should return:
{
"Test_Name" : "Hello",
"Test_Value" : "There"
}
Well, you would as comments suggested would have to convert the Json in a usable structure, a common library used for that would be NewtonSoft.Json which is available on nuget, and you could then parse your input like in the following way
var sourceObject = JsonConvert.DeserializeObject<JObject>( jsonContent );
var sourceMap = JsonConvert.DeserializeObject<IDictionary<string, string>>( jsonMap );
You would then have the object as a JObject, and then you could parse it in the following way
private static IDictionary<string,object> MapData( JObject source, IDictionary<string,string> map ) {
var result = new Dictionary<string, object>();
foreach (var kvp in map) {
result.Add( kvp.Key, source.SelectToken( kvp.Value ).Value<object>() );
}
return result;
}
Which would return you a dictionary with a key and the value for what you have found, which could then be used further on in your program.
The running version of this program can be found here, but I also provide you with the code in case you just want to copy paste it
using System;
using System.Linq;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public class Program
{
private static IDictionary<string,object> MapData( JObject source, IDictionary<string,string> map ) {
var result = new Dictionary<string, object>();
foreach (var kvp in map) {
result.Add( kvp.Key, source.SelectToken( kvp.Value ).Value<object>() );
}
return result;
}
public static void Main()
{
string jsonContent = #"{
""Name"" : ""Hello"",
""This"" : {
""That"" : {
""TheOther"" : ""There""
}
}
}";
string jsonMap = #"{
""Test_Name"" : ""Name"",
""Test_Value"": ""This.That.TheOther""
}";
var sourceObject = JsonConvert.DeserializeObject<JObject>( jsonContent );
var sourceMap = JsonConvert.DeserializeObject<IDictionary<string, string>>( jsonMap );
var result = MapData( sourceObject, sourceMap );
foreach (var kvp in result) {
Console.WriteLine( "{0}: {1}", kvp.Key, kvp.Value );
}
}
}
It would be easier if you keep the JPaths in the mapping JSON.
You can load orignal JSON in jobject1.
You can load mapping JSON to jobject2.
The the loop like below should help you to get the reasult
Assumption - i am assuming that your map JSON will contain map of only one object and your original JSON would contain array of elements.
for each object in jobject1
for each object in jobject2
for each property in object
get the value field (which would be JPath referring to some location in jobject1).
set the value field to value of JPath
end for
end for
end for
I guessed at the json structure for finding the first name. But this is similar to how I would try and do it. If the structure is a little different let me know and I can help.
string json = "{data:[{'Name':'Hello','This':{'That':{'TheOther':'There'}}},{'Name':'Hello2','This':{'That':{'TheOther':'There2'}}}]}";
var jsonArray = JToken.Parse(json);
var selectedObject = jsonArray.SelectToken("$..[?(#.Name== 'Hello')]");
var result = selectedObject.ToString();
var value = JObject.Parse(result);
var selectedValue = value.SelectToken("This.That.TheOther");
var final = selectedValue.ToString();
It is the MVC 5 application consuming web service. Web service having the method to return the JSON data of string in following format.
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public string GetCompanyData()
{
string jsonData = "[{\"1\":\"Message-Type\"},{\"28\":\"KEM\",\"3\":\"COMPANY1\",\"6\":\"218\",\"21\":\"6.8\",\"14\":\"33543\",\"16\":\"7188572.3\"},";
jsonData +="{\"28\":\"KEM\",\"3\":\"COMPANY2\",\"6\":\"224.5\",\"21\":\"4.5\",\"14\":\"19058\",\"16\":\"4246936\"},";
jsonData +="{\"28\":\"KEM\",\"3\":\"COMPANY3\",\"6\":\"79.9\",\"21\":\"3.4\",\"14\":\"81418\",\"16\":\"6320237.5\"},";
jsonData +="{\"28\":\"KEM\",\"3\":\"COMPANY4\",\"6\":\"87\",\"21\":\"2.5\",\"14\":\"42277\",\"16\":\"3654459\"},";
jsonData +="{\"28\":\"KEM\",\"3\":\"COMPANY5\",\"6\":\"103\",\"21\":\"2.3\",\"14\":\"1735\",\"16\":\"177450.4\"},";
jsonData +="{\"28\":\"KEM\",\"3\":\"COMPANY6\",\"6\":\"108.1\",\"21\":\"2.1\",\"14\":\"269165\",\"16\":\"29039148.4\"},";
jsonData +="{\"28\":\"KEM\",\"3\":\"COMPANY7\",\"6\":\"95.9\",\"21\":\"1.2\",\"14\":\"313\",\"16\":\"29479.7\"},";
jsonData +="{\"28\":\"KEM\",\"3\":\"COMPANY8\",\"6\":\"51.1\",\"21\":\"1\",\"14\":\"117208\",\"16\":\"5954460.6\"},";
jsonData +="{\"28\":\"KEM\",\"3\":\"COMPANY9\",\"6\":\"73.6\",\"21\":\"0.9\",\"14\":\"161593\",\"16\":\"11856197.6\"},";
jsonData +="{\"28\":\"KEM\",\"3\":\"COMPANY10\",\"6\":\"40.1\",\"21\":\"0.55\",\"14\":\"220241\",\"16\":\"8782243.3\"}]";
return jsonData;
}
Trying to convert this into the object list.
In controller :
JavaScriptSerializer json_serializer = new JavaScriptSerializer();
var companyDataList = json_serializer.Deserialize<List<object>>(svc.GetCompanyData())
This is working fine. But there is iteration in JSON where, need to be perform the unboxing to custom object. Since keys are integer based unable to read data from JSON.
But generating JSON is having int as keys, so unable to read specific data.
How to read such JSON data.
Edited : Tried with Newtonsoft
object[] objectArray = JsonConvert.DeserializeObject<object[]>(JsonConvert.SerializeObject(companyDataList ));
But underlying list of data is resulting in immediate window like (for first ):
objIndex
{
"28": "KEM",
"3": "COMPANY1",
"6": "218",
"21": "6.8",
"14": "33543",
"16": "7188572.3"
}
base: {
"28": "KEM",
"3": "COMPANY1",
"6": "218",
"21": "6.8",
"14": "33543",
"16": "7188572.3"
}
Type: Object
ANSWER:
Added following class to extend :
public static class Extensions
{
public static T ToObject<T>(this IDictionary<string, object> source, Dictionary<string, string> sourceDictionary)
where T : class, new()
{
T someObject = new T();
Type someObjectType = someObject.GetType();
foreach (KeyValuePair<string, object> item in source)
{
if (sourceDictionary.Keys.Contains(item.Key) && (someObjectType.GetProperty(sourceDictionary[item.Key])!=null))
someObjectType.GetProperty(sourceDictionary[item.Key]).SetValue(someObject, item.Value, null);
}
return someObject;
}
public static IDictionary<string, object> AsDictionary(this object source, BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)
{
return source.GetType().GetProperties(bindingAttr).ToDictionary
(
propInfo => propInfo.Name,
propInfo => propInfo.GetValue(source, null)
);
}
}
In Class method utilized this as :
var tempObjArray = json_serializer.Deserialize<object[]>(svc.GetTopByVolume());
List<Symbol> topByVolumeList = new List<Symbol>();
foreach (object tmpObject in tempObjArray)
{
Dictionary<string, object> diTopByVolumes = (Dictionary<string, object>)tmpObject;
Symbol someObject = diTopByVolumes.ToObject<Symbol>(StaticDictionary.TopByVolumeDictionary);
topByVolumeList.Add(someObject);
}
also in Global.asax in application start event specified added :
public static Dictionary<string, string> TopByVolumeDictionary = new Dictionary<string, string>();
TopByVolumeDictionary.Add("3", "SYMBOLID");
TopByVolumeDictionary.Add("6", "Property1");
TopByVolumeDictionary.Add("14", "Property2");
TopByVolumeDictionary.Add("16", "Property3");
TopByVolumeDictionary.Add("21", "Property4");
To expand on my comments a little,
You might be better off building a simple class or object that reflects what your JSON is supposed to look like and then making a List<companyObject> the you can convert to a JSON string.
A quick example that might not reflect the structure you want would be something like:
A Class:
public class Company
{
public Dictionary<string, string> companyObject =
new Dictionary<string, string>();
public Dictionary<string, string>
Add(string twentyEight, string three, string six,
string twentOne, string fourteen, string sixteen)
{
companyObject.Add("28", twentyEight);
companyObject.Add("3", three);
companyObject.Add("6", six);
companyObject.Add("21", twentOne);
companyObject.Add("14", fourteen);
companyObject.Add("16", sixteen);
return companyObject;
}
}
And then a little code (not elegant)
List<Company> companyList = new List<Company>();
Company c = new Company();
c.Add("KEM", "COMPANY1", "218", "6.8", "33543", "7188572.3");
companyList.Add(c);
string newJson = Newtonsoft.Json.JsonConvert.SerializeObject(companyList);
would create a JSON that looks like:
[{"companyObject":{"28":"KEM","3":"COMPANY1","6":"218","21":"6.8",
"14":"33543","16":"7188572.3"}}]
A list of two items would look like:
[{"companyObject":{"28":"KEM","3":"COMPANY1","6":"218","21":"6.8",
"14":"33543","16":"7188572.3"}},
{"companyObject":{"28":"KEM","3":"COMPANY2","6":"219","21":"7.2",
"14":"35200","16":"7188111.7"}}]
I'd tweak the class depending on what you really want, but as a quick rough concept I think it is a better approach.
I can't bind JSON to Dictionary<string,string> in Nancy.
This route:
Get["testGet"] = _ =>
{
var dictionary = new Dictionary<string, string>
{
{"hello", "world"},
{"foo", "bar"}
};
return Response.AsJson(dictionary);
};
returns the following JSON, as expected:
{
"hello": "world",
"foo": "bar"
}
When I try and post this exact JSON back to this route:
Post["testPost"] = _ =>
{
var data = this.Bind<Dictionary<string, string>>();
return null;
};
I get the exception:
The value "[Hello, world]" is not of type "System.String" and cannot
be used in this generic collection.
Is it possible to bind to Dictionary<string,string> using Nancys default model binding and, if so, what am I doing wrong here?
Nancy doesn't have a built-in converter for dictionaries. Because of this you'd need to use BindTo<T>() like so
var data = this.BindTo(new Dictionary<string, string>());
which will use the CollectionConverter. The issue with doing it like this is it will only add string values, so if you send
{
"hello": "world",
"foo": 123
}
your result will only contain the key hello.
If you want to capture all the values as strings, even if they aren't supplied as such, then you'll need to use a custom IModelBinder.
This will convert all the values to strings and return a Dictionary<string, string>.
public class StringDictionaryBinder : IModelBinder
{
public object Bind(NancyContext context, Type modelType, object instance, BindingConfig configuration, params string[] blackList)
{
var result = (instance as Dictionary<string, string>) ?? new Dictionary<string, string>();
IDictionary<string, object> formData = (DynamicDictionary) context.Request.Form;
foreach (var item in formData)
{
var itemValue = Convert.ChangeType(item.Value, typeof (string)) as string;
result.Add(item.Key, itemValue);
}
return result;
}
public bool CanBind(Type modelType)
{
// http://stackoverflow.com/a/16956978/39605
if (modelType.IsGenericType && modelType.GetGenericTypeDefinition() == typeof (Dictionary<,>))
{
if (modelType.GetGenericArguments()[0] == typeof (string) &&
modelType.GetGenericArguments()[1] == typeof (string))
{
return true;
}
}
return false;
}
}
Nancy will auto register this for you and you can bind your models as you normally would.
var data1 = this.Bind<Dictionary<string, string>>();
var data2 = this.BindTo(new Dictionary<string, string>());
I have a little test class like so :
public class Command
{
public dynamic MyData { get; set; }
}
As the dynamic MyData I want to use ExpandoObject, so I can do:
Command cmd = new Command();
cmd.MyData = new ExpandoObject();
cmd.MyData.SomeStuff = 4;
cmd.MyData.SomeOtherStuff = "hi";
I am trying to serialize to/deserialize from json. To do this I am using JavaScriptSerializer.
I want an example object above to serialize to:
{
MyData : {
SomeStuff : 4,
SomeOtherStuff : "hi"
}
}
To do this I need a JavaScriptConverter (taken from this website):
public class ExpandoJsonConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
return dictionary.ToExpando();
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
var result = new Dictionary<string, object>();
var dictionary = obj as IDictionary<string, object>;
foreach (var item in dictionary)
result.Add(item.Key, item.Value);
return result;
}
public override IEnumerable<Type> SupportedTypes
{
get
{
return new ReadOnlyCollection<Type>(new Type[] { typeof(ExpandoObject) });
}
}
}
public static class IDictionaryExtensions {
/// <summary>
/// Extension method that turns a dictionary of string and object to an ExpandoObject
/// Snagged from http://theburningmonk.com/2011/05/idictionarystring-object-to-expandoobject-extension-method/
/// </summary>
public static ExpandoObject ToExpando(this IDictionary<string, object> dictionary) {
var expando = new ExpandoObject();
var expandoDic = (IDictionary<string, object>)expando;
// go through the items in the dictionary and copy over the key value pairs)
foreach (var kvp in dictionary) {
// if the value can also be turned into an ExpandoObject, then do it!
if (kvp.Value is IDictionary<string, object>) {
var expandoValue = ((IDictionary<string, object>)kvp.Value).ToExpando();
expandoDic.Add(kvp.Key, expandoValue);
}
else if (kvp.Value is ICollection) {
// iterate through the collection and convert any strin-object dictionaries
// along the way into expando objects
var itemList = new List<object>();
foreach (var item in (ICollection)kvp.Value) {
if (item is IDictionary<string, object>) {
var expandoItem = ((IDictionary<string, object>)item).ToExpando();
itemList.Add(expandoItem);
}
else {
itemList.Add(item);
}
}
expandoDic.Add(kvp.Key, itemList);
}
else {
expandoDic.Add(kvp);
}
}
return expando;
}
}
Now this works neat for serializing, but there is a following problem with deserializing:
since MyData is a dynamic object, and the ExpandoJsonConverter expects ExpandoObject, the data deserialized to MyData is of type IDictionary<string, object>.
if I change dynamic MyData to be ExpandoObject MyData, I won't be able to say cmd.MyData.SomeStuff = 4;, the compiler will tell me that "ExpandoObject does not have property named SomeStuff".
finally, I could add dynamic to the list of supported types of ExpandoJsonConverter, byt wait, you cant do typeof(dynamic).
Is anyone aware of a neat workaround? I would really like this functionality, but I can't use 3rd party serialization libraries like Newtonsoft. Thanks.
Deserialize to ExpandoObject but declare the variable dynamic, i.e. what you need is dynamic d = js.Deserialize<ExpandoObject>(json):
string json = #"{
MyData : {
SomeStuff : 4,
SomeOtherStuff : ""hi""
}
}";
var js = new JavaScriptSerializer();
js.RegisterConverters(new[] { new ExpandoJsonConverter() });
dynamic d = js.Deserialize<ExpandoObject>(json);
Console.WriteLine(d.MyData.SomeOtherStuff);
Output:
hi
If you need a Command object, just construct one manually and inject the dynamic object returned from the serializer:
var cmd = new Command { MyData = d };
Console.WriteLine(cmd.MyData.SomeStuff);
public class MyClass
{
public MyClass()
{
myDictionary = new Dictionary<string, string>();
myArray = new List<int>();
}
private Dictionary<string, string> myDictionary;
public Dictionary<string, string> MyDictionary
{
get { return myDictionary; }
}
private List<int> myArray;
public List<int> MyArray
{
get { return myArray; }
}
}
static void Main(string[] args)
{
var model = new MyClass();
Type t = model.GetType();
System.Reflection.PropertyInfo[] properties = t.GetProperties();
//Add items to MyArray and MyDictionary in this model According to the properties using reflection
}
I want to add items to MyArray and MyDictionary in this model According to the properties using reflection.
Thank you for your help !
var dictProp = properties.Single(t => t.Name = "MyDictionary");
var myDict = (Dictionary<string,string>)dictProp.GetValue(model, null);
myDict.Add("MyKey", "MyValue");
To add an Item to a Generic.List<T> you use the Add method
Example:
MyArray.Add(1);
For Generic.Dictonary<T> you also use the Add method, but supply 2 values, Key and Value
MyDictionary.Add("MyKey", "MyValue");
So you can just loop though your PropertyInfo[] and add whatever you need to your List<T> or Dictionary<T>
foreach(var prop in properties )
{
MyArray.Add(a number from somewhere);
MyDictionary("some key", "some value");
}