This question already has answers here:
Serialize/Deserialize dynamic property name using JSON.NET
(4 answers)
Closed 2 years ago.
I need to have a dynamic property-name for the serialization.
public class Home
{
public virtual int Id { get; set; } // value: 2
public virtual string propertyName { get; set; } // value: administration
public virtual string Text { get; set; } // value: text1
}
should serialize to:
{
"Id": 2,
"administration": "text1"
}
Is there any way to serialize that? Which is the best way to deserialize it?
According to this post on how to Dynamically rename or ignore properties without changing the serialized class by Rico Suter, you can add a class which extends DefaultContractResolver named PropertyRenameAndIgnoreSerializerContractResolver.
So the model would look like:
public class Home
{
[JsonProperty("firstName")]
public int Id { get; set; } // value: 2
//public Dictionary<string,string> dictionary { get; set; }
[JsonProperty("propertyName")]
public string propertyName { get; set; } // value: administration
[JsonIgnore]
public string Text { get; set; } // value: text1
}
And serialization would look like this:
var home = new Home();
home.Id = 2;
home.propertyName = "text1";
var jsonResolver = new PropertyRenameAndIgnoreSerializerContractResolver();
jsonResolver.RenameProperty(typeof(Home), "propertyName", "administration");
var serializerSettings = new JsonSerializerSettings();
serializerSettings.ContractResolver = jsonResolver;
var json = JsonConvert.SerializeObject(home, serializerSettings);
Which give the desire output.
Add this class PropertyRenameAndIgnoreSerializerContractResolver.cs:
public class PropertyRenameAndIgnoreSerializerContractResolver : DefaultContractResolver
{
private readonly Dictionary<Type, HashSet<string>> _ignores;
private readonly Dictionary<Type, Dictionary<string, string>> _renames;
public PropertyRenameAndIgnoreSerializerContractResolver()
{
_ignores = new Dictionary<Type, HashSet<string>>();
_renames = new Dictionary<Type, Dictionary<string, string>>();
}
public void IgnoreProperty(Type type, params string[] jsonPropertyNames)
{
if (!_ignores.ContainsKey(type))
_ignores[type] = new HashSet<string>();
foreach (var prop in jsonPropertyNames)
_ignores[type].Add(prop);
}
public void RenameProperty(Type type, string propertyName, string newJsonPropertyName)
{
if (!_renames.ContainsKey(type))
_renames[type] = new Dictionary<string, string>();
_renames[type][propertyName] = newJsonPropertyName;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (IsIgnored(property.DeclaringType, property.PropertyName))
property.ShouldSerialize = i => false;
if (IsRenamed(property.DeclaringType, property.PropertyName, out var newJsonPropertyName))
property.PropertyName = newJsonPropertyName;
return property;
}
private bool IsIgnored(Type type, string jsonPropertyName)
{
if (!_ignores.ContainsKey(type))
return false;
return _ignores[type].Contains(jsonPropertyName);
}
private bool IsRenamed(Type type, string jsonPropertyName, out string newJsonPropertyName)
{
Dictionary<string, string> renames;
if (!_renames.TryGetValue(type, out renames) || !renames.TryGetValue(jsonPropertyName, out newJsonPropertyName))
{
newJsonPropertyName = null;
return false;
}
return true;
}
}
Add a ToJObject method that returns a JObject.
public JObject ToJObject()
{
JObject jObject = new JObject()
{
{ "Id", Id },
{ propertyName, Text }
}
return jObject;
}
Then for Deserializing i would probably create a factory method something like this:
public static Home CreateFromJObject(JObject obj)
{
Home h = new Home();
foreach (var a in obj)
{
if (a.Key == "ID")
{
h.Id = a.Value.Value<int>();
}
else
{
h.propertyName = a.Key;
h.Text = a.Value.Value<string>();
}
}
return h;
}
Ofcause if you have multiple other values in there i would either change it to a switch or make sure that only the needed JObject is passed in there.
Inheritance from Jobject(Newtonsoft) the existents properties from class not serialized.
Why were the Id and Name properties not serialized?
public class Test : JObject
{
public int Id { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
var test = new Test();
test["new_pro"] = 123456;
test.Id = 1;
test.Name = "Dog";
var r = Newtonsoft.Json.JsonConvert.SerializeObject(test);
// Result = { "new_pro":123456}
}
}
Any idea?
Whatever is the reason you want to do that - the reason is simple: JObject implements IDictionary and this case is treated in a special way by Json.NET. If your class implements IDictionary - Json.NET will not look at properties of your class but instead will look for keys and values in the dictionary. So to fix your case you can do this:
public class Test : JObject
{
public int Id
{
get { return (int) this["id"]; }
set { this["id"] = value; }
}
public string Name
{
get { return (string) this["name"]; }
set { this["name"] = value; }
}
}
If you just want to have both dynamic and static properties on your object - there is no need to inherit from JObject. Instead, use JsonExtensionData attribute:
public class Test {
public int Id { get; set; }
public string Name { get; set; }
[JsonExtensionData]
public Dictionary<string, JToken> AdditionalProperties { get; set; } = new Dictionary<string, JToken>();
}
var test = new Test();
test.AdditionalProperties["new_pro"] = 123456;
test.Id = 1;
test.Name = "Dog";
var r = Newtonsoft.Json.JsonConvert.SerializeObject(test);
I have a class Properties and in that I have defined a dictionary like this :
public class Properties
{
public IDictionary<string, string> ExtendedProperties
{
get;
set;
}
}
In the dictionary, there will be 3 keys always present say Name,Number and Age with the option of adding more KeyValuePairs during runtime.
I want to have the 3 keys described above to be present in the dictionary by default while initializing it in my code so that I can use it directly like this:
Properties objProps = new Properties();
objProps.ExtendedProperties["Name"] = "SomeName";
I know I can achieve this in my code by adding the KeyValuePair to the dictionary but I was hoping to have it setup directly in the class using get-set to include the 3 keys. I could not find any solution that does that in the class itself. I looked into this Creating dictionaries with predefined keys but did not find it satisfactory.
how can I achieve this?
Since C# 6 you can do something like:
using System;
using System.Collections.Generic;
public class Properties
{
public IDictionary<string, string> ExtendedProperties { get; set; }
public Properties(string name, string number, string age)
{
this.ExtendedProperties = new Dictionary<string, string>()
{
["Name"] = name,
["Number"] = number,
["Age"] = age
};
}
}
As you can see you need to define it in the constructor.
There is also some cool function you might want to use:
public int this[int param]
{
get { return array[param]; }
set { array[param] = value; }
}
Documentation
If you add something like this, you can do new Properties()["Name"]
Example with your code:
using System;
using System.Collections.Generic;
public class Properties
{
private IDictionary<string, string> extendedProperties;
public string this[string key]
{
get { return extendedProperties[key]; }
set { extendedProperties[key] = value; }
}
public Properties()
{
this.extendedProperties = new Dictionary<string, string>()
{
["Name"] = "something",
["Number"] = "something",
["Age"] = "something"
};
}
}
Like this:
public class Properties
{
public IDictionary<string, string> ExtendedProperties
{
get;
set;
}
public Properties()
{
this.ExtendedProperties = new Dictionary<string, string>()
{
{ "Name", String.Empty },
{ "Number", String.Empty },
{ "Age", String.Empty },
};
}
}
You might want to have a look at some documentation: https://msdn.microsoft.com/en-us/library/bb531208.aspx
How about adding the 3 entries in the constructor?
using System;
using System.Collections.Generic;
namespace My.Namespace
{
public class Properties
{
public IDictionary<string, string> ExtendedProperties { get; set; }
public Properties()
{
ExtendedProperties = new Dictionary<string, string>
{
["Name"] = String.Empty,
["Number"] = String.Empty,
["Age"] = String.Empty
};
}
}
}
You can do this.
public class Properties
{
public IDictionary<string, string> ExtendedProperties
{
get;
set;
}
public Properties(string [] fields)
{
ExtendedProperties = new Dictionary<string, string> ();
foreach(var s in fields)
{
ExtendedProperties.Add(s,string.Empty);
}
}
}
Usage :
Properties p = new Properties(new [] {"Name","Number", "Age"});
Working fiddler code
I would go with implementing IDictionary<string, string> because it is more safe and easier to extend with other keys: (long class to follow)
class Properties : IDictionary<string, string>
{
private Dictionary<string, string> _staticProps;
private Dictionary<string, string> _otherProps;
public Properties()
{
_staticProps = new Dictionary<string, string>
{
{"Name", "" },
{"Number", "" },
{"Age", "" }
};
_otherProps = new Dictionary<string, string>();
}
public ICollection<string> Keys
{
get
{
return (ICollection<String>)_otherProps.Keys.Concat(_staticProps.Keys);
}
}
public ICollection<string> Values
{
get
{
return (ICollection<String>)_otherProps.Values.Concat(_staticProps.Values);
}
}
public int Count
{
get
{
return _otherProps.Count + _staticProps.Count;
}
}
public bool IsReadOnly
{
get
{
throw new NotImplementedException();
}
}
public string this[string key]
{
get
{
if (_otherProps.ContainsKey(key))
{
return _otherProps[key];
}
if(_staticProps.ContainsKey(key))
{
return _staticProps[key];
}
throw new KeyNotFoundException(key);
}
set
{
if (_otherProps.ContainsKey(key) || _staticProps.ContainsKey(key))
{
throw new ArgumentException("key exists: " + key);
}
_otherProps[key] = value;
}
}
public bool ContainsKey(string key)
{
return _otherProps.ContainsKey(key) || _staticProps.ContainsKey(key);
}
public void Add(string key, string value)
{
_otherProps.Add(key, value);
}
public bool Remove(string key)
{
if (_staticProps.ContainsKey(key))
{
throw new ArgumentException("key is static, cannot be removed: " + key);
}
return _otherProps.Remove(key);
}
public bool TryGetValue(string key, out string value)
{
return _otherProps.TryGetValue(key, out value) || _staticProps.TryGetValue(key, out value);
}
public void Add(KeyValuePair<string, string> item)
{
if (_staticProps.ContainsKey(item.Key))
{
throw new ArgumentException("key exist an is static: " + item.Key);
}
_otherProps.Add(item.Key, item.Value);
}
public void Clear()
{
_otherProps.Clear();
foreach (var key in _staticProps.Keys)
{
_staticProps[key] = string.Empty;
}
}
public bool Contains(KeyValuePair<string, string> item)
{
return _otherProps.Contains(item) || _staticProps.Contains(item);
}
public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex)
{
// define yourself how you want to handle arrayIndex between the two dictionaries
}
public bool Remove(KeyValuePair<string, string> item)
{
if (_staticProps.ContainsKey(item.Key))
{
throw new ArgumentException("key is static, cannot be removed: " + item.Key);
}
return _otherProps.Remove(item.Key);
}
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return _otherProps.Concat(_staticProps).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _otherProps.Concat(_staticProps).GetEnumerator();
}
}
I would encapsulate this logic in property itself:
public class Properties
{
private IDictionary<string, string> _extendedProperties;
public IDictionary<string, string> ExtendedProperties
{
get
{
return
_extendedProperties == null ?
new Dictionary<string, string>() { { "Name", "" }, { "Number", "" }, { "Age", "" } } :
_extendedProperties;
}
set
{
_extendedProperties = value;
//here you can also check if value misses those key to add them to _extendedProperties
}
}
}
How do you set a value of a nested object? Here are my classes:
public class MyPage<MyData> {
public MyData Content {get;set;}
}
public class MyData {
public IDictionary<string, string> Text { get;set; }
}
I have an expression represented as a string as follows
var expressionString = "Content[\"Main\"]";
The value to set
var value = "hello world!";
The object to set
var page = new MyPage<MyData>();
Please note that Content is null and Text is null. I would like to create the while setting Text["Main"] = "hello world!";
Thanks.
Your code can work, you have to initialize everything to get it to work. To use those classes you need to do the following:
MyPage<MyData> myPage = new MyPage<MyData>();
myPage.Content = new MyData();
myPage.Content.Text = new Dictionary<string, string>();
myPage.Content.Text["foo"] = "bar";
myPage.Content.Text["fizz"] = "buzz";
foreach (var item in myPage.Content.Text)
{
Console.WriteLine(item.Key + item.Value);
}
What you probably want to do is change your classes to do the following. This way all of your objects are already initialized when you create your MyPage class. Also I don't see a need for adding to MyPage since you are limiting it to anyway.
public class MyPage
{
public MyPage()
{
Content = new MyData();
}
public MyData Content { get; set; }
}
public class MyData
{
public MyData()
{
Text = new Dictionary<string, string>();
}
public IDictionary<string, string> Text { get; private set; }
}
I'm playing around with Dictionaries and the new fancy 4.0 dynamic types inside a dictionary.
I have a Dictionary:
Dictionary<dynamic, dynamic> dynamicDic
And I populate it like this:
dynamicDic.Add("First", new Class1());
dynamicDic.Add("Second", new Class2());
For the sake of testing/practising Class1 and Class2 are quite simple:
public class Class1
{
public string Element { get; set; }
public List<Class2> Class2 { get; set; }
}
public class Class2
{
public string Property { get; set; }
public string Field;
}
I create two other classes that map class1 and class2 and they are virtually the same so ClassMap1 and ClassMap2. I'll just include CalssMap1 though:
public class ClassMap1: BaseClassMap1
{
public ClassMap1()
{
var r = new Class1();
Children = new Dictionary<string, dynamic>
{
{"Element", r.GetType().GetProperty("Element")},
{"Class1", r.GetType().GetProperty("Class1")}
};
Name = "Root";
ObjectType = typeof (Class1);
Parent = "RootElement";
HasParent = false;
HasChildren = true;
IsClass = r.GetType().IsClass;
}
}
And I create a base class: BaseClass1()
public class BaseClass1
{
private String _Name;
public String Name
{
get { return _Name; }
set { _Name = value; }
}
private Type _ObjectType;
public Type ObjectType
{
get { return _ObjectType; }
set { _ObjectType = value; }
}
private String _Parent;
public String Parent
{
get { return _Parent; }
set { _Parent = value; }
}
private Dictionary<string, dynamic> _Children;
public Dictionary<string, dynamic> Children
{
get { return _Children; }
set { _Children = value; }
}
private bool _HasParent;
public bool HasParent
{
get { return _HasParent; }
set { _HasParent = value; }
}
private bool _HasChildren;
public bool HasChildren
{
get { return _HasChildren; }
set { _HasChildren = value; }
}
private bool _IsClass;
public bool IsClass
{
get { return _IsClass; }
set { _IsClass = value; }
}
}
I populate the classes with data, not really important what data :)
Yet when I try to access the values through a Linq statement:
var a = _classObjects.SingleOrDefault(x => x.Key == node.Name).Value;
a only gives me:-
a.Equals(), a.GetType(), a.GetEnumerator() or a.ToString()
I would like to be able to have it do this instead (with intellisense)...
a.Children
a.Name
a.HasParent
etc...
Anyone got any ideas where I'm going wrong?
Oops got that completely wrong... Sorry :|
Edited above...
dynamic classes are all about run-time (NOT compile-time) discovery. How do you expect Intellisense to know what to do?
Using dynamic classes incurs a high performance overhead. I really suggest that you make it: Dictionary<string, dynamic> dynamicDic.
Or define a MyBaseClass and make it: Dictionary<string, MyBaseClass> myDic.