Two fields in a class with the same Json PropertyName - c#

What would be the best approach for having a class with two distinct fields (with distinct types) but same PropertyName.
It will always be the case that whenever one of the fields has a value, the other one will be null. I know I could create
two different classes, each of them with only one field. Is there a better alternative than creating two different classes?
Here's an example of what I'm trying to accomplish:
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class Foo
{
private Foo(int x, bool asAList = false)
{
if (asAList)
{
Baz = new List<int> { x };
}
else
{
Bar = x;
}
}
public static JObject Get(int x, bool asAList = false)
{
Foo foo = new Foo(x, asAList);
return JObject.FromObject(foo);
}
[JsonProperty(PropertyName = "qwerty", NullValueHandling = NullValueHandling.Ignore)]
public int? Bar { get; set; } = null;
[JsonProperty(PropertyName = "qwerty", NullValueHandling = NullValueHandling.Ignore)]
public List<int> Baz { get; set; } = null;
}
And I'd like to be able to do this:
JObject a = Foo.Get(1);
JObject b = Foo.Get(2, true);

You could have one private JToken JsonProperty that is being used for serializing/deserializing to either of the two public facing properties. On a set operation, that would then determine based on the JToken type if it's a JArray or not and then based on that determine which of the other properties to set. On a get operation, it would use the property that isn't null and convert that to the JToken. In order to deserialize you'll want a constructor that can be used the [JsonConstructor] can be added. Since you don't want to serialize/deserialize the other properties directly, the [JsonProperty] attribute can be removed because of the MemberSerialization.OptIn.
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class Foo
{
[JsonConstructor]
private Foo()
{ }
private Foo(int x, bool asAList = false)
{
if (asAList)
Baz = new List<int> { x };
else
Bar = x;
}
public static JObject Get(int x, bool asAList = false)
{
Foo foo = new Foo(x, asAList);
return JObject.FromObject(foo);
}
[JsonProperty(PropertyName = "qwerty", NullValueHandling = NullValueHandling.Ignore)]
private JToken Qwerty
{
get
{
return Bar.HasValue ? JToken.FromObject(Bar) : Baz != null ? JToken.FromObject(Baz) : null;
}
set
{
if (value != null && value.Type == JTokenType.Array)
Baz = value?.ToObject<List<int>>();
else
Bar = value?.ToObject<int?>();
}
}
public int? Bar { get; set; }
public List<int> Baz { get; set; }
}

Related

Inheritance from Jobject Newtonsoft

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);

C# How to access a class member by Binding

I want to access a class member by binding, but without any UI nor XAML code.
class Foo {
public int Value { get; set; }
}
class Bar {
public Foo foo { get; set; }
}
Bar bar = new Bar() {
foo = new Foo() {
Value = 2
}
}
Binding b = new System.Windows.Data.Binding("foo.Value");
b.Source = bar;
// Now I want a method which returns bar.foo.Value, would be like that:
int value = b.GET_VALUE(); // this method would return 2
Is there such a method ?
I found the answer, thanks to:
How to get class members values having string of their names?
No need of Binding class:
public static class Extensions
{
public static object GetPropertyValue(this object Obj, string PropertyName)
{
object Value = Obj;
foreach (string Property in PropertyName.Split('.'))
{
if (Value == null)
break;
Value = Value.GetType().GetProperty(Property).GetValue(Value, null);
}
return Value;
}
}
Usage:
class Foo {
public int Value { get; set; }
}
class Bar {
public Foo foo { get; set; }
}
Bar bar = new Bar() {
foo = new Foo() {
Value = 2
}
}
bar.GetPropertyValue("foo.Value"); // 2
bar.foo = null;
bar.GetPropertyValue("foo.Value"); // null

How to auto-ignore all properties with no corresponding constructor parameter when serializing

Is there some non-attribute-based method of ignoring all properties that don't have a corresponding constructor parameter when serializing? For example, when serializing this class, property Combo should be ignored. A round-trip serialization/deserialization of an instance of MyClass doesn't require Combo to be serialized. Ideally I could use some out-of-the-box setting.
public class MyClass
{
public MyClass(int myInt, string myString)
{
this.MyInt = myInt;
this.MyString = myString;
}
public int MyInt { get; }
public string MyString { get; }
public string Combo => this.MyInt + this.MyString;
}
You can do this with a custom IContractResolver:
public class ConstructorPropertiesOnlyContractResolver : DefaultContractResolver
{
readonly bool serializeAllWritableProperties;
public ConstructorPropertiesOnlyContractResolver(bool serializeAllWritableProperties)
: base()
{
this.serializeAllWritableProperties = serializeAllWritableProperties;
}
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
if (contract.CreatorParameters.Count > 0)
{
foreach (var property in contract.Properties)
{
if (contract.CreatorParameters.GetClosestMatchProperty(property.PropertyName) == null)
{
if (!serializeAllWritableProperties || !property.Writable)
property.Readable = false;
}
}
}
return contract;
}
}
Then use it like:
var settings = new JsonSerializerSettings { ContractResolver = new ConstructorPropertiesOnlyContractResolver(false) };
var json = JsonConvert.SerializeObject(myClass, Formatting.Indented, settings );
Pass true for serializeAllWritableProperties if you also want to serialize read/write properties that are not included in the constructor parameter list, e.g. AnUnrelatedReadWriteProperty in:
public class MyClass
{
public MyClass(int myInt, string myString)
{
this.MyInt = myInt;
this.MyString = myString;
}
public int MyInt { get; private set; }
public string MyString { get; private set; }
public string Combo { get { return this.MyInt + this.MyString; } }
public string AnUnrelatedReadWriteProperty { get; set; }
}
Note you may want to cache your contract resolver for best performance.

How to serialize static properties in JSON.NET without adding [JsonProperty] attribute

Is it possible to serialize static properties with JSON.NET without adding [JsonProperty] attribute to each property.
Example class:
public class Settings
{
public static int IntSetting { get; set; }
public static string StrSetting { get; set; }
static Settings()
{
IntSetting = 5;
StrSetting = "Test str";
}
}
Expected result:
{
"IntSetting": 5,
"StrSetting": "Test str"
}
Default behavior skips static properties:
var x = JsonConvert.SerializeObject(new Settings(), Formatting.Indented);
You can do this with a custom contract resolver. Specifically you need to subclass DefaultContractResolver and override the GetSerializableMembers function:
public class StaticPropertyContractResolver : DefaultContractResolver
{
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
var baseMembers = base.GetSerializableMembers(objectType);
PropertyInfo[] staticMembers =
objectType.GetProperties(BindingFlags.Static | BindingFlags.Public);
baseMembers.AddRange(staticMembers);
return baseMembers;
}
}
Here all we're doing is calling the base implementation of GetSerializableMembers, then adding public static properties to our list of members to serialize.
To use it you can create a new JsonSerializerSettings object and set the ContractResolver to an instance of the StaticPropertyContractResolver:
var serializerSettings = new JsonSerializerSettings();
serializerSettings.ContractResolver = new StaticPropertyContractResolver();
Now, pass those settings to JsonConvert.SerializeObject and everything should work:
string json = JsonConvert.SerializeObject(new Settings(), serializerSettings);
Output:
{
"IntSetting": 5,
"StrSetting": "Test str"
}
Example: https://dotnetfiddle.net/pswTJW
A more complicated way to solve this:
Solution 1:
public class Settings
{
int intsetting { get; set; } /*= 0;*/ // commented only allowed in C# 6+
string strsetting { get; set; } /*= "";*/
public int IntSetting { get { return intsetting; } set { intsetting = value; } }
public string StrSetting { get { return strsetting; } set { strsetting = value; } }
static Settings()
{
IntSetting = 5;
StrSetting = "Test str";
}
}
Solution 2: (less complicated)
public class Settings
{
[JsonProperty]
public static int IntSetting { get; set; }
[JsonProperty]
public static string StrSetting { get; set; }
static Settings()
{
IntSetting = 5;
StrSetting = "Test str";
}
}
Adding the [JsonProperty] to all variables would be the easyest way of solving this, but when you don't want to use it Solution 1 would fit best for you.

How to differentiate between "true" and boolean true in YamlDotNet

I have a class with a dynamic member. When the value is a boolean, it's serialized as true / false which deserializes to a string. Is this an issue with YamlDotNet, or if not, how can I force it to serialize as true rather than "true"?
This is a class I'm trying to serialize
public class Field : IField
{
public string MachineName { get; set; }
public string DisplayName { get; set; }
public FieldFormatterEnum FormatterType { get; set; }
public dynamic Value { get; set; }
public dynamic DefaultValue { get; set; }
public dynamic FormattedValue { get; set; }
...
}
The typed deserializer method:
public T DeserializeObject<T>(string input)
{
T o;
using (var tr = new StringReader(input))
{
o = new Deserializer().Deserialize<T>(tr);
}
return o;
}
Field is in a List in another class which is being serialized / deserialized.
Update: After looking at the serialized output, YamlDotNet outputs the serialized boolean as true and not "true". So.. I'm guessing it's a problem with the deserialization code in YamlDotNet?
For serialization, you could create a getter-only property as a bool that has the value you want:
public bool ValueAsBool
{
get
{
bool outValue = false;
if(Value is string)
{
bool.TryParse((string)Value, out outValue);
return outValue;
}
return outValue;
}
}
Ultimately, when working with dynamics you will be throwing some type information away, and you shouldn't expect to be able to recover all of it properly.
I've just set up a simple test like:
class Foo
{
public dynamic V { get; set; }
}
var f1 = new Foo(){V = 5};
var tw2 = new StringWriter();
new Serializer().Serialize(tw2, f1);
var tr2 = new StringReader(tw2.ToString());
var f_1 = new Deserializer().Deserialize<Foo>(tr2);
... and V is deserialized as "5". The default type to deserialize to is string

Categories