I'm trying to deserialize this JSON into an object but I can't reach the solution.
Json format:
{"option1":
{"field1":"true","field2":"false"},
"option2":
{"field1":"true","field2":"false"}}
I Have the following object:
[Serializable]
public class genericFieldOptions
{
public string option { get; set; }
public string field { get; set; }
public bool value{ get; set; }
}
And then the "deserializer":
public class genericFieldsConverter : JavaScriptConverter
{
public override IEnumerable<Type> SupportedTypes
{
get
{
return new[] { typeof(genericFieldOptions) };
}
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
List<genericFieldOptions> p = new List<genericFieldOptions>();
foreach (var entry in dictionary.Keys)
{
try
{
Dictionary<string, Boolean> test = (Dictionary<string, Boolean>)dictionary[entry];//error
p.Add(new genericFieldOptions { key = entry, field1=test["field1"],field2=test["field2"] });
}
catch { }
}
return p;
}
The call:
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new JavaScriptConverter[] { new genericFieldsConverter() });
var example= serializer.Deserialize<List<genericFieldOptions>>(json);
How can I access the IDictionary<string, object> as Dictionary<string, Dictionary<string,boolean>> ? Or is it just impossible?
What am I doing wrong? Is there another easy way to do this?
The easy way is to correctly make objects that represent the serialized values. So for:
{"option1":
{"field1":"true","field2":"false"},
"option2":
{"field1":"true","field2":"false"}}
I would make:
public class Options
{
public Option Option1 { get; set; }
public Option Option2 { get; set; }
}
public class Option
{
public bool Field1 { get; set; }
public bool Field2 { get; set; }
}
For beginners, one of the easier ways is to use http://json2csharp.com/.
As mentioned, you can use Json.NET. If you don't want to create classes to deserialise to, you can use dictionaries as you have tried, or you could use a dynamic.
const string json = "{\"option1\":{\"field1\":true,\"field2\":false}," +
"\"option2\":{\"field1\":true,\"field2\":false}}";
var result1 = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, bool>>>(json);
Console.WriteLine(result1["option2"]["field1"]);
dynamic result2 = JsonConvert.DeserializeObject(json);
Console.WriteLine(result2.option2.field1);
Given that you have chosen to use javascriptserializer, firstly you need to do your conversion at the level of the List<genericFieldOptions> not the genericFieldOptions, because the serializer cannot convert a JSON object to a List<T> automatically.
Secondly, rather than casting the nested dictionaries to Dictionary<string, Boolean>, you need to cast to IDictionary<string, object> and then deserialize each value to a bool using JavaScriptSerializer.ConvertToType<bool>. Thus:
public class genericFieldsListConverter : JavaScriptConverter
{
public override IEnumerable<Type> SupportedTypes
{
get
{
return new[] { typeof(List<genericFieldOptions>) };
}
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
var query = from entry in dictionary
let subDictionary = entry.Value.AsJsonObject()
where subDictionary != null
from subEntry in subDictionary
select new genericFieldOptions { option = entry.Key, field = subEntry.Key, value = serializer.ConvertToType<bool>(subEntry.Value) };
return query.ToList();
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
var list = (IList<genericFieldOptions>)obj;
if (list == null)
return null;
return list
.GroupBy(o => o.option)
.ToDictionary(g => g.Key, g => (object)g.ToDictionary(o => o.field, o => serializer.Serialize(o.value)));
}
}
public static class JavaScriptSerializerObjectExtensions
{
public static bool IsJsonObject(this object obj)
{
return obj is IDictionary<string, object>;
}
public static IDictionary<string, object> AsJsonObject(this object obj)
{
return obj as IDictionary<string, object>;
}
}
Related
How to I determine if a Type is an ExpandoObject vs a Dynamic object?
This is returning true for both:
public static bool IsDynamicObject(Type type)
{
return typeof(IDynamicMetaObjectProvider).IsAssignableFrom(type);
}
Example Code for Dynamic Object:
public class Entity
{
public Guid Id { get; set; }
public String Name { get; set; }
}
Delta<Entity> x = new Delta<Entity>();
dynamic dynamicX = x;
dynamicX.Name = nameof(Entity);
dynamicX.Id = typeof(Entity).GUID;
Example Code for Expando Object:
dynamic childX = new ExpandoObject();
childX.A = 1;
The ExpandoObject can be casted to a dictionary to get the member names and values
public static bool IsExpandoObject(object objectValue)
{
if (objectValue == null)
return false;
if (IsDynamicObject(objectValue.GetType()))
{
IDictionary<string, object> expandoPropertyValues = objectValue as IDictionary<string, object>;
return expandoPropertyValues != null;
}
return false;
}
public static bool IsDynamicObject(Type type)
{
return typeof(IDynamicMetaObjectProvider).IsAssignableFrom(type);
}
I have a JSON file formatted like so, which is generated by a tool that I cannot edit:
{
"thing_name1": {
"property1": 0,
"property2": "sure"
},
"thing_name2": {
"property1": 34,
"property2": "absolutely"
}
}
The class I'm trying to deserialize to is as such:
[DataContract]
public class Thing
{
[DataMember]
public String ThingName;
[DataMember(Name="property1")]
public int Property1;
[DataMember(Name="property2")]
public String Property2;
}
I need to put the values of "thing_name1" and "thing_name2" into the ThingName data member of their respective deserialized objects, but haven't been able to find an easy way to do this without writing a custom (de)serializer. Or writing a quick Python script to write another file, but that wouldn't be very space efficient.
Yes, this is possible, but you do need some custom code to do it.
It's a little ugly, but you can create a custom IDataContractSurrogate class to deserialize the JSON into a Dictionary<string, Dictionary<string, object>>, and then copy the values from the nested dictionary structure into a List<Thing>. Here's the code you would need for the surrogate:
class MyDataContractSurrogate : IDataContractSurrogate
{
public Type GetDataContractType(Type type)
{
if (type == typeof(List<Thing>))
{
return typeof(Dictionary<string, Dictionary<string, object>>);
}
return type;
}
public object GetDeserializedObject(object obj, Type targetType)
{
if (obj.GetType() == typeof(Dictionary<string, Dictionary<string, object>>) &&
targetType == typeof(List<Thing>))
{
List<Thing> list = new List<Thing>();
foreach (var kvp in (Dictionary<string, Dictionary<string, object>>)obj)
{
Thing thing = new Thing { ThingName = kvp.Key };
Dictionary<string, object> propsDict = kvp.Value;
foreach (PropertyInfo prop in GetDataMemberProperties(typeof(Thing)))
{
DataMemberAttribute att = prop.GetCustomAttribute<DataMemberAttribute>();
object value;
if (propsDict.TryGetValue(att.Name, out value))
{
prop.SetValue(thing, value);
}
}
list.Add(thing);
}
return list;
}
return obj;
}
public object GetObjectToSerialize(object obj, Type targetType)
{
if (obj.GetType() == typeof(List<Thing>) &&
targetType == typeof(Dictionary<string, Dictionary<string, object>>))
{
var thingsDict = new Dictionary<string, Dictionary<string, object>>();
foreach (Thing thing in (List<Thing>)obj)
{
var propsDict = new Dictionary<string, object>();
foreach (PropertyInfo prop in GetDataMemberProperties(typeof(Thing)))
{
DataMemberAttribute att = prop.GetCustomAttribute<DataMemberAttribute>();
propsDict.Add(att.Name, prop.GetValue(thing));
}
thingsDict.Add(thing.ThingName, propsDict);
}
return thingsDict;
}
return obj;
}
private IEnumerable<PropertyInfo> GetDataMemberProperties(Type type)
{
return type.GetProperties().Where(p => p.CanRead && p.CanWrite && p.GetCustomAttribute<DataMemberAttribute>() != null);
}
// ------- The rest of these methods are not needed -------
public object GetCustomDataToExport(Type clrType, Type dataContractType)
{
throw new NotImplementedException();
}
public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
{
throw new NotImplementedException();
}
public void GetKnownCustomDataTypes(System.Collections.ObjectModel.Collection<Type> customDataTypes)
{
throw new NotImplementedException();
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
{
throw new NotImplementedException();
}
public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
{
throw new NotImplementedException();
}
}
To use the surrogate, you'll need to create an instance of DataContractJsonSerializerSettings and pass it to the DataContractJsonSerializer with the following properties set. Note that since we require the UseSimpleDictionaryFormat setting, this solution will only work with .Net 4.5 or later.
var settings = new DataContractJsonSerializerSettings();
settings.DataContractSurrogate = new MyDataContractSurrogate();
settings.KnownTypes = new List<Type> { typeof(Dictionary<string, Dictionary<string, object>>) };
settings.UseSimpleDictionaryFormat = true;
Note that in your Thing class you should NOT mark the ThingName member with a [DataMember] attribute, since it is handled specially in the surrogate. Also, I made the assumption that your class members are actually properties (with { get; set; }) and not fields like you wrote in your question. If that assumption is incorrect, you'll need to change all the references to PropertyInfo in the surrogate code to use FieldInfo instead; otherwise the surrogate will not work.
[DataContract]
public class Thing
{
// Don't mark this property with [DataMember]
public string ThingName { get; set; }
[DataMember(Name = "property1")]
public int Property1 { get; set; }
[DataMember(Name = "property2")]
public string Property2 { get; set; }
}
Here is a round-trip demo:
public class Program
{
public static void Main(string[] args)
{
string json = #"
{
""thing_name1"": {
""property1"": 0,
""property2"": ""sure""
},
""thing_name2"": {
""property1"": 34,
""property2"": ""absolutely""
}
}";
var settings = new DataContractJsonSerializerSettings();
settings.DataContractSurrogate = new MyDataContractSurrogate();
settings.KnownTypes = new List<Type> { typeof(Dictionary<string, Dictionary<string, object>>) };
settings.UseSimpleDictionaryFormat = true;
List<Thing> things = Deserialize<List<Thing>>(json, settings);
foreach (Thing thing in things)
{
Console.WriteLine("ThingName: " + thing.ThingName);
Console.WriteLine("Property1: " + thing.Property1);
Console.WriteLine("Property2: " + thing.Property2);
Console.WriteLine();
}
json = Serialize(things, settings);
Console.WriteLine(json);
}
public static T Deserialize<T>(string json, DataContractJsonSerializerSettings settings)
{
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
var ser = new DataContractJsonSerializer(typeof(T), settings);
return (T)ser.ReadObject(ms);
}
}
public static string Serialize(object obj, DataContractJsonSerializerSettings settings)
{
using (MemoryStream ms = new MemoryStream())
{
var ser = new DataContractJsonSerializer(obj.GetType(), settings);
ser.WriteObject(ms, obj);
return Encoding.UTF8.GetString(ms.ToArray());
}
}
}
Output:
ThingName: thing_name1
Property1: 0
Property2: sure
ThingName: thing_name2
Property1: 34
Property2: absolutely
{"thing_name1":{"property1":0,"property2":"sure"},"thing_name2":{"property1":34,"property2":"absolutely"}}
I am having a problem trying to deserialize a pretty complex nested dictionary type with interface values using Json.net. The code is located here "https://dotnetfiddle.net/JSoAug", and the types in question are:
public class TypeConverter<T, TSerialized> : CustomCreationConverter<T>
where TSerialized : T, new()
{
public override T Create(Type objectType)
{
return new TSerialized();
}
}
public interface IValue
{
Dictionary<string, IValue> SomeValues { get; set; }
}
public class Value : IValue
{
[JsonProperty(ItemConverterType = typeof(TypeConverter<IValue, Value>))]
public Dictionary<string, IValue> SomeValues { get; set; }
}
public interface ISomeAtrributes
{
Dictionary<string, object> Attributes { get; set; }
}
public interface IDataItem : ISomeAtrributes
{
IValue Value { get; set; }
}
public class DataItem : IDataItem
{
[JsonProperty(ItemConverterType = typeof(TypeConverter<IValue, Value>))]
public IValue Value { get; set; }
public Dictionary<string, object> Attributes { get; set; }
}
public interface IBlobItem
{
TypeXDictionary<IEnumerable<IDataItem>> TypeXDataDictionary { get; set; }
}
public class BlobItem : IBlobItem
{
public BlobItem()
{
TypeXDataDictionary = new TypeXDictionary<IEnumerable<IDataItem>>();
}
[JsonProperty(ItemConverterType = typeof(TypeConverter<IEnumerable<IDataItem>, List<DataItem>>))]
public TypeXDictionary<IEnumerable<IDataItem>> TypeXDataDictionary { get; set; }
}
public class TypeYDictionary<T> : Dictionary<string, T>
{
}
public class TypeXDictionary<T> : Dictionary<string, TypeYDictionary<T>>
{
}
I have several nested levels of collections or dictionaries containing interface objects (with BlobItem as the root), and at each level I use a subclass of CustomCreationConverter<T> to deserialize the interfaces as known concrete types. However, in this case, when I attempt to do so as follows:
var blobItem = new BlobItem();
var dataItemDic = new TypeYDictionary<IEnumerable<IDataItem>>();
var objDic = new Dictionary<string, object> {{"key", "object"}};
dataItemDic.Add("dataItemKey", new List<DataItem>() { new DataItem() { Attributes = objDic } });
blobItem.TypeXDataDictionary.Add("typeXKey", dataItemDic );
var ser = JsonConvert.SerializeObject(blobItem);
var deSerialized = JsonConvert.DeserializeObject<BlobItem>(ser);
I receive an exception:
Run-time exception (line 19): Cannot populate JSON object onto type 'System.Collections.Generic.List`1[JsonSerialization.DataItem]'. Path 'TypeXDataDictionary.typeXKey.dataItemKey', line 1, position 50.
Stack Trace:
[Newtonsoft.Json.JsonSerializationException: Cannot populate JSON object onto type 'System.Collections.Generic.List`1[JsonSerialization.DataItem]'. Path 'TypeXDataDictionary.typeXKey.dataItemKey', line 1, position 50.]
at JsonSerialization.Program.Main(String[] args): line 19
Why is the CustomCreationConverter<T> not working?
The problem is with your BlobItem type:
public class BlobItem : IBlobItem
{
public BlobItem()
{
TypeXDataDictionary = new TypeXDictionary<IEnumerable<IDataItem>>();
}
[JsonProperty(ItemConverterType = typeof(TypeConverter<IEnumerable<IDataItem>, List<DataItem>>))]
public TypeXDictionary<IEnumerable<IDataItem>> TypeXDataDictionary { get; set; }
}
For TypeXDataDictionary you specify an ItemConverterType = typeof(TypeConverter<IEnumerable<IDataItem>, List<DataItem>>) to indicate how to deserialize the values of the TypeXDataDictionary. However, this dictionary is actually a dictionary of dictionaries:
public class TypeXDictionary<T> : Dictionary<string, TypeYDictionary<T>>
{
}
public class TypeYDictionary<T> : Dictionary<string, T>
{
}
Thus its values are not of type IEnumerable<IDataItem>, they are of type Dictionary<string, IEnumerable<IDataItem>> and the converter will not work. What you need is a converter for the items of the items of the TypeXDictionary, which can be defined as follows:
public class DictionaryValueTypeConverter<TDictionary, TKey, TValue, TValueSerialized> : JsonConverter
where TDictionary : class, IDictionary<TKey, TValue>, new()
where TValueSerialized : TValue
{
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var surrogate = serializer.Deserialize<Dictionary<TKey, TValueSerialized>>(reader);
if (surrogate == null)
return null;
var dictionary = existingValue as TDictionary ?? new TDictionary();
foreach (var pair in surrogate)
dictionary[pair.Key] = pair.Value;
return dictionary;
}
}
Then applied to BlobItem as follows:
public class BlobItem : IBlobItem
{
public BlobItem()
{
TypeXDataDictionary = new TypeXDictionary<IEnumerable<IDataItem>>();
}
[JsonProperty(ItemConverterType = typeof(DictionaryValueTypeConverter<TypeYDictionary<IEnumerable<IDataItem>>, string, IEnumerable<IDataItem>, List<DataItem>>))]
public TypeXDictionary<IEnumerable<IDataItem>> TypeXDataDictionary { get; set; }
}
Sample fiddle.
Due to a schema change I need to control the deserialization of property b depending on the API version or client version from the controller level.
public class MyModel
{
public string a { get; set; }
// old: public string b { get; set; }
public string[] b { get; set; }
}
I'm looking to implement a custom converter to write string[] as a single string if the version is old.
When constructing the response, I'm deserializing a parent model and want to use the custom converter only on this one property.
public class ParentModel
{
public MyModel myModel { get; set; }
}
This means an attribute for b wouldn't work. How can I implant such a custom converter on demand for just one property (custom converters switch on by a type, not a property name)?
Check bellow Code
public class ParentModelJSONConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
throw new ApplicationException("Serializable only");
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
Dictionary<string, object> result = new Dictionary<string, object>();
ParentModel myobj = obj as ParentModel;
if (myobj != null)
{
// Add your conditions
result.Add("MyKeyName", myobj.myModel);
}
return result;
}
public override IEnumerable<Type> SupportedTypes
{
get { return new Type[] { typeof(ParentModel) }; }
}
}
To use above code
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new JavaScriptConverter[] { new ParentModelJSONConverter() });
String json = serializer.Serialize(objParentModel);
Ended up doing the following manual conversion. Since it's a schema change, the old model can be easily hardcoded:
public class CustomConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var o = (MyModel)value;
writer.WriteStartObject();
writer.WritePropertyName("a");
writer.WriteValue(o.a);
writer.WritePropertyName("b");
writer.WriteValue(o.b[0]);
writer.WriteEndObject();
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(MyModel);
}
//...
}
}
Ok, so I have this basic class setup...
public class Location
{
public string Name{ get; set; }
private LocationList _LocationList = new LocationList();
public LocationList Locations{ get{ return _LocationList; } }
}
public class LocationList : List<Location>{}
public class ViewModel
{
private LocationList _LocationList = new LocationList();
public LocationList Locations{ get{ return _LocationList; } }
}
which I want to use with the Newtonsoft JSON serializer. However, the serializer doesn't insert the items into the existing collection behind the read-only property accessor, but rather tries to assign an entirely new List to the property, which of course it can't since there isn't a setter.
Now I could just switch to this...
public class Location
{
public string Name{ get; set; }
public LocationList Locations{ get; set; }
}
public class LocationList : List<Location>{}
public class ViewModel
{
public LocationList RootLocations{ get; set; }
}
But now the list property isn't read-only and can be set to null. Even if we make the setter private, JSON deserialization can still set it to null.
What I want is a way to tell the serializer 'Take the items you have in your list and insert them into this already-existing list' rather than saying 'Replace my list with yours altogether'.
So can this be done, or am I going to have to write my own JSON serialization converter and plug that in?
M
I don't know about JSON.NET, but if you use JavaScriptSerializer you can provide a custom serializer, but still use the inbuilt parsing / formatting etc:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Web.Script.Serialization;
class Program
{
static void Main(string[] args)
{
JavaScriptSerializer ser = new JavaScriptSerializer();
ser.RegisterConverters(new[] { new ViewModelConverter() });
var model = new ViewModel { Locations = { new Location { Name = "abc",
Locations = { new Location { Name = "def"}}} } };
var json = ser.Serialize(model);
var clone = (ViewModel)ser.Deserialize(json, typeof(ViewModel));
}
}
public class ViewModelConverter : JavaScriptConverter
{
public override IEnumerable<Type> SupportedTypes
{
get { return new[] { typeof(Location), typeof(ViewModel) }; }
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
if (obj is ViewModel)
{
return new Dictionary<string, object> { { "locations", ((ViewModel)obj).Locations } };
}
if (obj is Location)
{
return new Dictionary<string, object> {
{"name", ((Location)obj).Name},
{ "locations", ((Location)obj).Locations }
};
}
throw new NotSupportedException();
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
if (type == typeof(ViewModel))
{
var model = new ViewModel();
ReadLocations(model.Locations, dictionary, serializer);
return model;
}
if (type == typeof(Location))
{
var loc = new Location();
if (dictionary.ContainsKey("name"))
{
loc.Name = (string)dictionary["name"];
}
ReadLocations(loc.Locations, dictionary, serializer);
return loc;
}
throw new NotSupportedException();
}
static void ReadLocations(LocationList locations, IDictionary<string, object> dictionary, JavaScriptSerializer serializer)
{
if (dictionary.ContainsKey("locations"))
{
foreach (object item in (IList)dictionary["locations"])
{
locations.Add((Location)serializer.ConvertToType<Location>(item));
}
}
}
}
public class Location
{
public string Name { get; set; }
private LocationList _LocationList = new LocationList();
public LocationList Locations { get { return _LocationList; } }
}
public class LocationList : List<Location> { }
public class ViewModel
{
private LocationList _LocationList = new LocationList();
public LocationList Locations { get { return _LocationList; } }
}