I have a class that has a default constructor and also an overloaded constructor that takes in a set of parameters. These parameters match to fields on the object and are assigned on construction. At this point i need the default constructor for other purposes so i would like to keep it if i can.
My Problem: If I remove the default constructor and pass in the JSON string, the object deserializes correctly and passes in the constructor parameters without any issues. I end up getting back the object populated the way I would expect. However, as soon as I add the default constructor into the object, when i call JsonConvert.DeserializeObject<Result>(jsontext) the properties are no longer populated.
At this point I have tried adding new JsonSerializerSettings(){CheckAdditionalContent = true} to the deserialization call. That did not do anything.
Another note: the constructor parameters do match the names of the fields exactly except that the parameters are start with a lowercase letter. I wouldn't think this would matter since, like i mentioned, the deserialization works fine with no default constructor.
Here is a sample of my constructors:
public Result() { }
public Result(int? code, string format, Dictionary<string, string> details = null)
{
Code = code ?? ERROR_CODE;
Format = format;
if (details == null)
Details = new Dictionary<string, string>();
else
Details = details;
}
Json.Net prefers to use the default (parameterless) constructor on an object if there is one. If there are multiple constructors and you want Json.Net to use a non-default one, then you can add the [JsonConstructor] attribute to the constructor that you want Json.Net to call.
[JsonConstructor]
public Result(int? code, string format, Dictionary<string, string> details = null)
{
...
}
It is important that the constructor parameter names match the corresponding property names of the JSON object (ignoring case) for this to work correctly. You do not necessarily have to have a constructor parameter for every property of the object, however. For those JSON object properties that are not covered by the constructor parameters, Json.Net will try to use the public property accessors (or properties/fields marked with [JsonProperty]) to populate the object after constructing it.
If you do not want to add attributes to your class or don't otherwise control the source code for the class you are trying to deserialize, then another alternative is to create a custom JsonConverter to instantiate and populate your object. For example:
class ResultConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Result));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// Load the JSON for the Result into a JObject
JObject jo = JObject.Load(reader);
// Read the properties which will be used as constructor parameters
int? code = (int?)jo["Code"];
string format = (string)jo["Format"];
// Construct the Result object using the non-default constructor
Result result = new Result(code, format);
// (If anything else needs to be populated on the result object, do that here)
// Return the result
return result;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then, add the converter to your serializer settings, and use the settings when you deserialize:
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new ResultConverter());
Result result = JsonConvert.DeserializeObject<Result>(jsontext, settings);
A bit late and not exactly suited here, but I'm gonna add my solution here, because my question had been closed as a duplicate of this one, and because this solution is completely different.
I needed a general way to instruct Json.NET to prefer the most specific constructor for a user defined struct type, so I can omit the JsonConstructor attributes which would add a dependency to the project where each such struct is defined.
I've reverse engineered a bit and implemented a custom contract resolver where I've overridden the CreateObjectContract method to add my custom creation logic.
public class CustomContractResolver : DefaultContractResolver {
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var c = base.CreateObjectContract(objectType);
if (!IsCustomStruct(objectType)) return c;
IList<ConstructorInfo> list = objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).OrderBy(e => e.GetParameters().Length).ToList();
var mostSpecific = list.LastOrDefault();
if (mostSpecific != null)
{
c.OverrideCreator = CreateParameterizedConstructor(mostSpecific);
c.CreatorParameters.AddRange(CreateConstructorParameters(mostSpecific, c.Properties));
}
return c;
}
protected virtual bool IsCustomStruct(Type objectType)
{
return objectType.IsValueType && !objectType.IsPrimitive && !objectType.IsEnum && !objectType.Namespace.IsNullOrEmpty() && !objectType.Namespace.StartsWith("System.");
}
private ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
{
method.ThrowIfNull("method");
var c = method as ConstructorInfo;
if (c != null)
return a => c.Invoke(a);
return a => method.Invoke(null, a);
}
}
I'm using it like this.
public struct Test {
public readonly int A;
public readonly string B;
public Test(int a, string b) {
A = a;
B = b;
}
}
var json = JsonConvert.SerializeObject(new Test(1, "Test"), new JsonSerializerSettings {
ContractResolver = new CustomContractResolver()
});
var t = JsonConvert.DeserializeObject<Test>(json);
t.A.ShouldEqual(1);
t.B.ShouldEqual("Test");
Based on some of the answers here, I have written a CustomConstructorResolver for use in a current project, and I thought it might help somebody else.
It supports the following resolution mechanisms, all configurable:
Select a single private constructor so you can define one private constructor without having to mark it with an attribute.
Select the most specific private constructor so you can have multiple overloads, still without having to use attributes.
Select the constructor marked with an attribute of a specific name - like the default resolver, but without a dependency on the Json.Net package because you need to reference Newtonsoft.Json.JsonConstructorAttribute.
public class CustomConstructorResolver : DefaultContractResolver
{
public string ConstructorAttributeName { get; set; } = "JsonConstructorAttribute";
public bool IgnoreAttributeConstructor { get; set; } = false;
public bool IgnoreSinglePrivateConstructor { get; set; } = false;
public bool IgnoreMostSpecificConstructor { get; set; } = false;
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
// Use default contract for non-object types.
if (objectType.IsPrimitive || objectType.IsEnum) return contract;
// Look for constructor with attribute first, then single private, then most specific.
var overrideConstructor =
(this.IgnoreAttributeConstructor ? null : GetAttributeConstructor(objectType))
?? (this.IgnoreSinglePrivateConstructor ? null : GetSinglePrivateConstructor(objectType))
?? (this.IgnoreMostSpecificConstructor ? null : GetMostSpecificConstructor(objectType));
// Set override constructor if found, otherwise use default contract.
if (overrideConstructor != null)
{
SetOverrideCreator(contract, overrideConstructor);
}
return contract;
}
private void SetOverrideCreator(JsonObjectContract contract, ConstructorInfo attributeConstructor)
{
contract.OverrideCreator = CreateParameterizedConstructor(attributeConstructor);
contract.CreatorParameters.Clear();
foreach (var constructorParameter in base.CreateConstructorParameters(attributeConstructor, contract.Properties))
{
contract.CreatorParameters.Add(constructorParameter);
}
}
private ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
{
var c = method as ConstructorInfo;
if (c != null)
return a => c.Invoke(a);
return a => method.Invoke(null, a);
}
protected virtual ConstructorInfo GetAttributeConstructor(Type objectType)
{
var constructors = objectType
.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(c => c.GetCustomAttributes().Any(a => a.GetType().Name == this.ConstructorAttributeName)).ToList();
if (constructors.Count == 1) return constructors[0];
if (constructors.Count > 1)
throw new JsonException($"Multiple constructors with a {this.ConstructorAttributeName}.");
return null;
}
protected virtual ConstructorInfo GetSinglePrivateConstructor(Type objectType)
{
var constructors = objectType
.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);
return constructors.Length == 1 ? constructors[0] : null;
}
protected virtual ConstructorInfo GetMostSpecificConstructor(Type objectType)
{
var constructors = objectType
.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.OrderBy(e => e.GetParameters().Length);
var mostSpecific = constructors.LastOrDefault();
return mostSpecific;
}
}
Here is the complete version with XML documentation as a gist:
https://gist.github.com/bjorn-jarisch/80f77f4b6bdce3b434b0f7a1d06baa95
Feedback appreciated.
The default behaviour of Newtonsoft.Json is going to find the public constructors. If your default constructor is only used in containing class or the same assembly, you can reduce the access level to protected or internal so that Newtonsoft.Json will pick your desired public constructor.
Admittedly, this solution is rather very limited to specific cases.
internal Result() { }
public Result(int? code, string format, Dictionary<string, string> details = null)
{
Code = code ?? ERROR_CODE;
Format = format;
if (details == null)
Details = new Dictionary<string, string>();
else
Details = details;
}
Based on the answer by Zoltan, I created a variation that lets you use a specific constructor based on its signature.
Usage
return new JsonSerializerSettings
{
ContractResolver = new DynamicObjectResolver(t =>
{
if (t == typeof(QueueProperties))
return new Type[] { typeof(string) };
return null;
})
};
An here is the implementation
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Concurrent;
using System.Reflection;
namespace ConsoleApp76.Json
{
class DynamicObjectResolver : DefaultContractResolver
{
private readonly Func<Type, Type[]> GetConstructorSignature;
private readonly ConcurrentDictionary<Type, ConstructorInfo> TypeToConstructorLookup =
new ConcurrentDictionary<Type, ConstructorInfo>();
public DynamicObjectResolver(Func<Type, Type[]> getConstructorSignature)
{
if (getConstructorSignature is null)
throw new ArgumentNullException(nameof(getConstructorSignature));
GetConstructorSignature = getConstructorSignature;
}
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var result = base.CreateObjectContract(objectType);
ConstructorInfo constructor = TypeToConstructorLookup.GetOrAdd(objectType, t => FindConstructorInfo(t));
if (constructor is null)
return result;
result.OverrideCreator = CreateParameterizedConstructor(constructor);
foreach (var param in CreateConstructorParameters(constructor, result.Properties))
result.CreatorParameters.Add(param);
return result;
}
private ConstructorInfo FindConstructorInfo(Type objectType)
{
Type[] constructorSignature = GetConstructorSignature(objectType);
if (constructorSignature is null)
return null;
return objectType.GetConstructor(
bindingAttr:
System.Reflection.BindingFlags.Public
| System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Instance,
binder: null,
types: new Type[] { typeof(string) },
modifiers: null);
}
private static ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
{
if (method is null)
throw new ArgumentNullException(nameof(method));
var c = method as ConstructorInfo;
if (c != null)
return a => c.Invoke(a);
return a => method.Invoke(null, a);
}
}
}
Solution:
public Response Get(string jsonData) {
var json = JsonConvert.DeserializeObject<modelname>(jsonData);
var data = StoredProcedure.procedureName(json.Parameter, json.Parameter, json.Parameter, json.Parameter);
return data;
}
Model:
public class modelname {
public long parameter{ get; set; }
public int parameter{ get; set; }
public int parameter{ get; set; }
public string parameter{ get; set; }
}
Related
I'm working on a C# program for JSON serialisation.
My object looks as follows (example of a property):
public class Device
{
[JsonProperty("ALLTYPES_NAME")]
public string ALLTYPES_NAME { get; set; }
[JsonProperty("INFORMATION")]
public string INFORMATION { get; set; }
...
Now I have the following information (in a textfile):
ALLTYPES_NAME "Object1"
ALLTYPES_NAME "Object2"
INFORMATION "Inside_Info"
I would like to create two objects, which are to be JSON serialised as follows:
"desired_objects": [
{
"ALLTYPES_NAME": "Object1",
},
{
"ALLTYPES_NAME": "Object2",
"INFORMATION": "Inside_Info,
...
In order to get this done, I need something like:
temp_obj.GetPropertyByName("ALLTYPES_NAME") = "Object1";
desired_objects.Add(temp_obj);
...
temp_obj.GetPropertyByName("ALLTYPES_NAME") = "Object2";
temp_obj.GetPropertyByName("INFORMATION") = "Inside_Information";
...
One way to do this, is working with templates. However I wonder if this is needed, seen the fact that the needed information is retrievable using the [JsonProperty] directives, hence my question:
Does a method .GetPropertyByName() exist, based on the [JsonProperty ...]? (Or even larger, can the [JsonProperty] directives be used for something else rather than the JSON serialiser?)
You can use Json.NET's IContractResolver to get a JsonObjectContract for your Device object that contains information about all the properties of the type including their JSON names and get/set methods. The following extension method does the trick:
public static partial class JsonExtensions
{
static readonly IContractResolver defaultResolver = JsonSerializer.CreateDefault().ContractResolver;
public static void SetJsonProperty<T>(T obj, string jsonName, object value, bool exact = false, IContractResolver resolver = null)
{
if (!TrySetJsonProperty(obj, jsonName, value, exact, resolver))
throw new ArgumentException(string.Format("Could not set property {0} in {1}.", jsonName, obj));
}
public static bool TrySetJsonProperty<T>(T obj, string jsonName, object value, bool exact = false, IContractResolver resolver = null)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
resolver = resolver ?? defaultResolver;
var contract = resolver.ResolveContract(obj.GetType()) as JsonObjectContract;
if (contract == null)
return false;
var property = contract.Properties.GetProperty(jsonName, exact ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
if (property == null || !property.Writable)
return false;
property.ValueProvider.SetValue(obj, value);
return true;
}
}
Furthermore, if you are reading from a text file and some of your property value types are not strings, you will need to convert the textual values from the file to the appropriate .Net type. The following does that:
public static partial class JsonExtensions
{
public static void SetConvertibleJsonProperty<T, TConvertible>(T obj, string jsonName, TConvertible value, bool exact = false, IContractResolver resolver = null) where TConvertible : IConvertible
{
if (!TrySetConvertibleJsonProperty(obj, jsonName, value, exact, resolver))
throw new ArgumentException(string.Format("Could not set property {0} in {1}.", jsonName, obj));
}
public static bool TrySetConvertibleJsonProperty<T, TConvertible>(T obj, string jsonName, TConvertible value, bool exact = false, IContractResolver resolver = null)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
resolver = resolver ?? defaultResolver;
var contract = resolver.ResolveContract(obj.GetType()) as JsonObjectContract;
if (contract == null)
return false;
var property = contract.Properties.GetProperty(jsonName, exact ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
if (property == null || !property.Writable)
return false;
var finalValue = value == null ? null : Convert.ChangeType(value, Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType, CultureInfo.InvariantCulture);
property.ValueProvider.SetValue(obj, finalValue);
return true;
}
}
And to use it, do:
JsonExtensions.SetConvertibleJsonProperty(temp_obj, "ALLTYPES_NAME", "Object1");
JsonExtensions.SetConvertibleJsonProperty(temp_obj, "ALLTYPES_NAME", "Object2");
JsonExtensions.SetConvertibleJsonProperty(temp_obj, "INFORMATION", "Inside_Information");
// The following sets the following property
// [JsonProperty("DECIMAL_DATA")]
// public decimal DecimalInformation { get; set; }
JsonExtensions.SetConvertibleJsonProperty(temp_obj, "DECIMAL_DATA", "3.1111");
Notes:
If you serialize and deserialize using camel case, pass CamelCasePropertyNamesContractResolver for the IContractResolver resolver parameter.
The method is not designed to work for dynamic objects or dictionaries.
For an equivalent method to get a property value by JSON property name, see this answer to How to get Json Property name using reflection in C#.
Demo fiddle here.
After some experiments, based on the comments of Jon Skeet, I realise that I don't even need the JSON directives for this, as you can see from following code excerpt:
System.Reflection.PropertyInfo[] list_of_attributes =
(new Own_Class()).GetType().GetProperties();
string[] list_of_attribute_names = new string[list_of_attributes.Length];
for (int i = 0; i< list_of_attributes.Length; i++)
list_of_attribute_names[i] = list_of_attributes[i].Name;
combobox_on_form.Items.AddRange(list_of_attribute_names);
I have a class that has a default constructor and also an overloaded constructor that takes in a set of parameters. These parameters match to fields on the object and are assigned on construction. At this point i need the default constructor for other purposes so i would like to keep it if i can.
My Problem: If I remove the default constructor and pass in the JSON string, the object deserializes correctly and passes in the constructor parameters without any issues. I end up getting back the object populated the way I would expect. However, as soon as I add the default constructor into the object, when i call JsonConvert.DeserializeObject<Result>(jsontext) the properties are no longer populated.
At this point I have tried adding new JsonSerializerSettings(){CheckAdditionalContent = true} to the deserialization call. That did not do anything.
Another note: the constructor parameters do match the names of the fields exactly except that the parameters are start with a lowercase letter. I wouldn't think this would matter since, like i mentioned, the deserialization works fine with no default constructor.
Here is a sample of my constructors:
public Result() { }
public Result(int? code, string format, Dictionary<string, string> details = null)
{
Code = code ?? ERROR_CODE;
Format = format;
if (details == null)
Details = new Dictionary<string, string>();
else
Details = details;
}
Json.Net prefers to use the default (parameterless) constructor on an object if there is one. If there are multiple constructors and you want Json.Net to use a non-default one, then you can add the [JsonConstructor] attribute to the constructor that you want Json.Net to call.
[JsonConstructor]
public Result(int? code, string format, Dictionary<string, string> details = null)
{
...
}
It is important that the constructor parameter names match the corresponding property names of the JSON object (ignoring case) for this to work correctly. You do not necessarily have to have a constructor parameter for every property of the object, however. For those JSON object properties that are not covered by the constructor parameters, Json.Net will try to use the public property accessors (or properties/fields marked with [JsonProperty]) to populate the object after constructing it.
If you do not want to add attributes to your class or don't otherwise control the source code for the class you are trying to deserialize, then another alternative is to create a custom JsonConverter to instantiate and populate your object. For example:
class ResultConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Result));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// Load the JSON for the Result into a JObject
JObject jo = JObject.Load(reader);
// Read the properties which will be used as constructor parameters
int? code = (int?)jo["Code"];
string format = (string)jo["Format"];
// Construct the Result object using the non-default constructor
Result result = new Result(code, format);
// (If anything else needs to be populated on the result object, do that here)
// Return the result
return result;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then, add the converter to your serializer settings, and use the settings when you deserialize:
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new ResultConverter());
Result result = JsonConvert.DeserializeObject<Result>(jsontext, settings);
A bit late and not exactly suited here, but I'm gonna add my solution here, because my question had been closed as a duplicate of this one, and because this solution is completely different.
I needed a general way to instruct Json.NET to prefer the most specific constructor for a user defined struct type, so I can omit the JsonConstructor attributes which would add a dependency to the project where each such struct is defined.
I've reverse engineered a bit and implemented a custom contract resolver where I've overridden the CreateObjectContract method to add my custom creation logic.
public class CustomContractResolver : DefaultContractResolver {
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var c = base.CreateObjectContract(objectType);
if (!IsCustomStruct(objectType)) return c;
IList<ConstructorInfo> list = objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).OrderBy(e => e.GetParameters().Length).ToList();
var mostSpecific = list.LastOrDefault();
if (mostSpecific != null)
{
c.OverrideCreator = CreateParameterizedConstructor(mostSpecific);
c.CreatorParameters.AddRange(CreateConstructorParameters(mostSpecific, c.Properties));
}
return c;
}
protected virtual bool IsCustomStruct(Type objectType)
{
return objectType.IsValueType && !objectType.IsPrimitive && !objectType.IsEnum && !objectType.Namespace.IsNullOrEmpty() && !objectType.Namespace.StartsWith("System.");
}
private ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
{
method.ThrowIfNull("method");
var c = method as ConstructorInfo;
if (c != null)
return a => c.Invoke(a);
return a => method.Invoke(null, a);
}
}
I'm using it like this.
public struct Test {
public readonly int A;
public readonly string B;
public Test(int a, string b) {
A = a;
B = b;
}
}
var json = JsonConvert.SerializeObject(new Test(1, "Test"), new JsonSerializerSettings {
ContractResolver = new CustomContractResolver()
});
var t = JsonConvert.DeserializeObject<Test>(json);
t.A.ShouldEqual(1);
t.B.ShouldEqual("Test");
Based on some of the answers here, I have written a CustomConstructorResolver for use in a current project, and I thought it might help somebody else.
It supports the following resolution mechanisms, all configurable:
Select a single private constructor so you can define one private constructor without having to mark it with an attribute.
Select the most specific private constructor so you can have multiple overloads, still without having to use attributes.
Select the constructor marked with an attribute of a specific name - like the default resolver, but without a dependency on the Json.Net package because you need to reference Newtonsoft.Json.JsonConstructorAttribute.
public class CustomConstructorResolver : DefaultContractResolver
{
public string ConstructorAttributeName { get; set; } = "JsonConstructorAttribute";
public bool IgnoreAttributeConstructor { get; set; } = false;
public bool IgnoreSinglePrivateConstructor { get; set; } = false;
public bool IgnoreMostSpecificConstructor { get; set; } = false;
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
// Use default contract for non-object types.
if (objectType.IsPrimitive || objectType.IsEnum) return contract;
// Look for constructor with attribute first, then single private, then most specific.
var overrideConstructor =
(this.IgnoreAttributeConstructor ? null : GetAttributeConstructor(objectType))
?? (this.IgnoreSinglePrivateConstructor ? null : GetSinglePrivateConstructor(objectType))
?? (this.IgnoreMostSpecificConstructor ? null : GetMostSpecificConstructor(objectType));
// Set override constructor if found, otherwise use default contract.
if (overrideConstructor != null)
{
SetOverrideCreator(contract, overrideConstructor);
}
return contract;
}
private void SetOverrideCreator(JsonObjectContract contract, ConstructorInfo attributeConstructor)
{
contract.OverrideCreator = CreateParameterizedConstructor(attributeConstructor);
contract.CreatorParameters.Clear();
foreach (var constructorParameter in base.CreateConstructorParameters(attributeConstructor, contract.Properties))
{
contract.CreatorParameters.Add(constructorParameter);
}
}
private ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
{
var c = method as ConstructorInfo;
if (c != null)
return a => c.Invoke(a);
return a => method.Invoke(null, a);
}
protected virtual ConstructorInfo GetAttributeConstructor(Type objectType)
{
var constructors = objectType
.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(c => c.GetCustomAttributes().Any(a => a.GetType().Name == this.ConstructorAttributeName)).ToList();
if (constructors.Count == 1) return constructors[0];
if (constructors.Count > 1)
throw new JsonException($"Multiple constructors with a {this.ConstructorAttributeName}.");
return null;
}
protected virtual ConstructorInfo GetSinglePrivateConstructor(Type objectType)
{
var constructors = objectType
.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);
return constructors.Length == 1 ? constructors[0] : null;
}
protected virtual ConstructorInfo GetMostSpecificConstructor(Type objectType)
{
var constructors = objectType
.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.OrderBy(e => e.GetParameters().Length);
var mostSpecific = constructors.LastOrDefault();
return mostSpecific;
}
}
Here is the complete version with XML documentation as a gist:
https://gist.github.com/bjorn-jarisch/80f77f4b6bdce3b434b0f7a1d06baa95
Feedback appreciated.
The default behaviour of Newtonsoft.Json is going to find the public constructors. If your default constructor is only used in containing class or the same assembly, you can reduce the access level to protected or internal so that Newtonsoft.Json will pick your desired public constructor.
Admittedly, this solution is rather very limited to specific cases.
internal Result() { }
public Result(int? code, string format, Dictionary<string, string> details = null)
{
Code = code ?? ERROR_CODE;
Format = format;
if (details == null)
Details = new Dictionary<string, string>();
else
Details = details;
}
Based on the answer by Zoltan, I created a variation that lets you use a specific constructor based on its signature.
Usage
return new JsonSerializerSettings
{
ContractResolver = new DynamicObjectResolver(t =>
{
if (t == typeof(QueueProperties))
return new Type[] { typeof(string) };
return null;
})
};
An here is the implementation
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Concurrent;
using System.Reflection;
namespace ConsoleApp76.Json
{
class DynamicObjectResolver : DefaultContractResolver
{
private readonly Func<Type, Type[]> GetConstructorSignature;
private readonly ConcurrentDictionary<Type, ConstructorInfo> TypeToConstructorLookup =
new ConcurrentDictionary<Type, ConstructorInfo>();
public DynamicObjectResolver(Func<Type, Type[]> getConstructorSignature)
{
if (getConstructorSignature is null)
throw new ArgumentNullException(nameof(getConstructorSignature));
GetConstructorSignature = getConstructorSignature;
}
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var result = base.CreateObjectContract(objectType);
ConstructorInfo constructor = TypeToConstructorLookup.GetOrAdd(objectType, t => FindConstructorInfo(t));
if (constructor is null)
return result;
result.OverrideCreator = CreateParameterizedConstructor(constructor);
foreach (var param in CreateConstructorParameters(constructor, result.Properties))
result.CreatorParameters.Add(param);
return result;
}
private ConstructorInfo FindConstructorInfo(Type objectType)
{
Type[] constructorSignature = GetConstructorSignature(objectType);
if (constructorSignature is null)
return null;
return objectType.GetConstructor(
bindingAttr:
System.Reflection.BindingFlags.Public
| System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Instance,
binder: null,
types: new Type[] { typeof(string) },
modifiers: null);
}
private static ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
{
if (method is null)
throw new ArgumentNullException(nameof(method));
var c = method as ConstructorInfo;
if (c != null)
return a => c.Invoke(a);
return a => method.Invoke(null, a);
}
}
}
Solution:
public Response Get(string jsonData) {
var json = JsonConvert.DeserializeObject<modelname>(jsonData);
var data = StoredProcedure.procedureName(json.Parameter, json.Parameter, json.Parameter, json.Parameter);
return data;
}
Model:
public class modelname {
public long parameter{ get; set; }
public int parameter{ get; set; }
public int parameter{ get; set; }
public string parameter{ get; set; }
}
I want to differentiate between these two json inputs in an action in Asp.Net Core:
{
"field1": null,
"field2": null
}
and
{
"field1": null,
}
I have an ordinary class like this in C#:
public class MyData
{
public string Field1 { get; set;}
public string Field2 { get; set;}
}
I want to run a partial update of an object that can accept null as the value, but when the field will not be in the input it means I don't want to update this field at all (something else from setting it to null).
This is what I ended up doing, as all other options seem to be too complicated (e.g. jsonpatch, model binding) or would not give the flexibility I want.
This solution means there is a bit of a boilerplate to write for each property, but not too much:
public class UpdateRequest : PatchRequest
{
public string Name
{
get => _name;
set { _name = value; SetHasProperty(nameof(Name)); }
}
}
public abstract class PatchRequest
{
private readonly HashSet<string> _properties = new HashSet<string>();
public bool HasProperty(string propertyName) => _properties.Contains(propertyName);
protected void SetHasProperty(string propertyName) => _properties.Add(propertyName);
}
The value can then be read like this:
if (request.HasProperty(nameof(request.Name)) { /* do something with request.Name */ }
and this is how it can be validated with a custom attribute:
var patchRequest = (PatchRequest) validationContext.ObjectInstance;
if (patchRequest.HasProperty(validationContext.MemberName) {/* do validation*/}
I've created a solution that works with System.Text.Json using a JsonConverter
DTO class:
public class MyDataDto : PatchRequest<MyDataDto>
{
public string? Field1 { get; set; }
public string? Field2 { get; set; }
}
PatchRequest class:
public abstract class PatchRequest
{
private readonly List<string> _setProperties = new();
public void MarkPropertyAsSet(string propertyName) => _setProperties.Add(propertyName);
public bool IsSet(string propertyName) => _setProperties.Contains(propertyName);
}
public abstract class PatchRequest<T> : PatchRequest where T : PatchRequest<T>
{
public bool IsSet<TProperty>(Expression<Func<T, TProperty>> expression)
=> IsSet((expression.Body as MemberExpression).Member.Name);
}
JsonConverter:
public class PatchRequestConverter : JsonConverter<PatchRequest>
{
public override bool CanConvert(Type typeToConvert) =>
typeof(PatchRequest).IsAssignableFrom(typeToConvert);
public override PatchRequest Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
throw new JsonException();
var patchRequest = (PatchRequest)Activator.CreateInstance(typeToConvert)!;
var properties = typeToConvert
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty | BindingFlags.GetProperty)
.ToDictionary(p => options.PropertyNamingPolicy?.ConvertName(p.Name) ?? p.Name);
while (reader.Read())
switch (reader.TokenType)
{
case JsonTokenType.EndObject:
return patchRequest;
case JsonTokenType.PropertyName:
var property = properties[reader.GetString()!];
reader.Read();
property.SetValue(patchRequest, JsonSerializer.Deserialize(ref reader, property.PropertyType, options));
patchRequest.MarkPropertyAsSet(property.Name);
continue;
}
throw new JsonException();
}
public override void Write(Utf8JsonWriter writer, PatchRequest value, JsonSerializerOptions options) =>
JsonSerializer.Serialize(writer, value, value.GetType(), options);
}
Register the JsonConverter like:
builder.Services.Configure<JsonOptions>(options =>
options.JsonSerializerOptions.Converters.Add(new PatchRequestConverter());
);
Use in in a API controller like:
public async Task<ActionResult> PatchMyDataAsync([FromBody] MyDataDto myDataDto)
{
var field1IsSet = myDataDto.IsSet(c => c.Field1);
var field2IsSet = myDataDto.IsSet(nameof(c.Field2));
//...
}
Just to add another 2 cents, we went the similar way to the Ilya's answer, except that we're not calling SetHasProperty from setter, but overriding DefaultContractResolver:
public class PatchRequestContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var prop = base.CreateProperty(member, memberSerialization);
prop.SetIsSpecified += (o, o1) =>
{
if (o is PatchRequest patchRequest)
{
patchRequest.SetHasProperty(prop.PropertyName);
}
};
return prop;
}
}
And then register this resolver in Startup:
services
.AddControllers()
.AddNewtonsoftJson(settings =>
settings.SerializerSettings.ContractResolver = new PatchRequestContractResolver());
Note, that we are still using JSON.Net and not the System.Text.Json (which is default for .NET 3+) for deserializing. As of now there's no way to do things similar to DefaultContractResolver with System.Text.Json
Intro: Asp.net core takes your request body and then deserializes to a object of Type MyData, and then it calls the method in your controller by passing the object as parameter. From the object myData you can not know if the field2 was null or not passed. Both ways the property will have a null value. The information you are trying to find is lost at deserialization.
Solution: To find out this, you need to read the request body, and check the request body if the field was passed or not. In asp.net core, there is a bit of complexity is reading the request body once it is read (by the asp.net core framework for creating the object of MyData). We need to rewind the request stream, and then read it. The code for it is below.
[HttpPost]
public void Post([FromBody] MyData myData)
{
HttpContext.Request.Body.Seek(0, System.IO.SeekOrigin.Begin);
System.IO.StreamReader sr = new System.IO.StreamReader(HttpContext.Request.Body);
var requestBody = sr.ReadToEnd();
//Now check the requestBody if the field was passed using JSON parsing or string manipulation
Console.WriteLine(requestBody);
}
Warning: Though this will work. What you are trying do will reduce the readability and make it difficult for other developers. Differentiating if a field value is null or was not present in the request body is not a common practice.
A low key will be like this:
public class MyData
{
public string Field1 { get; set; }
public string Field2 { get; set; }
// this can be extension method also.
public bool HasProperty(string propertyName)
{
return GetType().GetProperty(propertyName) != null;
}
}
In main:
string json = "{ \"field1\": null, \"field2\": null }";
MyData jsonObject = null;
jsonObject = JsonConvert.DeserializeObject<MyData>(json);
MyData source= null; // this will contain values
if (jsonObject.HasProperty("Field1"))
source.Field1 = jsonObject.Field1;
Reference: Check if a property exist in a class
I have a class that has a default constructor and also an overloaded constructor that takes in a set of parameters. These parameters match to fields on the object and are assigned on construction. At this point i need the default constructor for other purposes so i would like to keep it if i can.
My Problem: If I remove the default constructor and pass in the JSON string, the object deserializes correctly and passes in the constructor parameters without any issues. I end up getting back the object populated the way I would expect. However, as soon as I add the default constructor into the object, when i call JsonConvert.DeserializeObject<Result>(jsontext) the properties are no longer populated.
At this point I have tried adding new JsonSerializerSettings(){CheckAdditionalContent = true} to the deserialization call. That did not do anything.
Another note: the constructor parameters do match the names of the fields exactly except that the parameters are start with a lowercase letter. I wouldn't think this would matter since, like i mentioned, the deserialization works fine with no default constructor.
Here is a sample of my constructors:
public Result() { }
public Result(int? code, string format, Dictionary<string, string> details = null)
{
Code = code ?? ERROR_CODE;
Format = format;
if (details == null)
Details = new Dictionary<string, string>();
else
Details = details;
}
Json.Net prefers to use the default (parameterless) constructor on an object if there is one. If there are multiple constructors and you want Json.Net to use a non-default one, then you can add the [JsonConstructor] attribute to the constructor that you want Json.Net to call.
[JsonConstructor]
public Result(int? code, string format, Dictionary<string, string> details = null)
{
...
}
It is important that the constructor parameter names match the corresponding property names of the JSON object (ignoring case) for this to work correctly. You do not necessarily have to have a constructor parameter for every property of the object, however. For those JSON object properties that are not covered by the constructor parameters, Json.Net will try to use the public property accessors (or properties/fields marked with [JsonProperty]) to populate the object after constructing it.
If you do not want to add attributes to your class or don't otherwise control the source code for the class you are trying to deserialize, then another alternative is to create a custom JsonConverter to instantiate and populate your object. For example:
class ResultConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Result));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// Load the JSON for the Result into a JObject
JObject jo = JObject.Load(reader);
// Read the properties which will be used as constructor parameters
int? code = (int?)jo["Code"];
string format = (string)jo["Format"];
// Construct the Result object using the non-default constructor
Result result = new Result(code, format);
// (If anything else needs to be populated on the result object, do that here)
// Return the result
return result;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then, add the converter to your serializer settings, and use the settings when you deserialize:
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new ResultConverter());
Result result = JsonConvert.DeserializeObject<Result>(jsontext, settings);
A bit late and not exactly suited here, but I'm gonna add my solution here, because my question had been closed as a duplicate of this one, and because this solution is completely different.
I needed a general way to instruct Json.NET to prefer the most specific constructor for a user defined struct type, so I can omit the JsonConstructor attributes which would add a dependency to the project where each such struct is defined.
I've reverse engineered a bit and implemented a custom contract resolver where I've overridden the CreateObjectContract method to add my custom creation logic.
public class CustomContractResolver : DefaultContractResolver {
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var c = base.CreateObjectContract(objectType);
if (!IsCustomStruct(objectType)) return c;
IList<ConstructorInfo> list = objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).OrderBy(e => e.GetParameters().Length).ToList();
var mostSpecific = list.LastOrDefault();
if (mostSpecific != null)
{
c.OverrideCreator = CreateParameterizedConstructor(mostSpecific);
c.CreatorParameters.AddRange(CreateConstructorParameters(mostSpecific, c.Properties));
}
return c;
}
protected virtual bool IsCustomStruct(Type objectType)
{
return objectType.IsValueType && !objectType.IsPrimitive && !objectType.IsEnum && !objectType.Namespace.IsNullOrEmpty() && !objectType.Namespace.StartsWith("System.");
}
private ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
{
method.ThrowIfNull("method");
var c = method as ConstructorInfo;
if (c != null)
return a => c.Invoke(a);
return a => method.Invoke(null, a);
}
}
I'm using it like this.
public struct Test {
public readonly int A;
public readonly string B;
public Test(int a, string b) {
A = a;
B = b;
}
}
var json = JsonConvert.SerializeObject(new Test(1, "Test"), new JsonSerializerSettings {
ContractResolver = new CustomContractResolver()
});
var t = JsonConvert.DeserializeObject<Test>(json);
t.A.ShouldEqual(1);
t.B.ShouldEqual("Test");
Based on some of the answers here, I have written a CustomConstructorResolver for use in a current project, and I thought it might help somebody else.
It supports the following resolution mechanisms, all configurable:
Select a single private constructor so you can define one private constructor without having to mark it with an attribute.
Select the most specific private constructor so you can have multiple overloads, still without having to use attributes.
Select the constructor marked with an attribute of a specific name - like the default resolver, but without a dependency on the Json.Net package because you need to reference Newtonsoft.Json.JsonConstructorAttribute.
public class CustomConstructorResolver : DefaultContractResolver
{
public string ConstructorAttributeName { get; set; } = "JsonConstructorAttribute";
public bool IgnoreAttributeConstructor { get; set; } = false;
public bool IgnoreSinglePrivateConstructor { get; set; } = false;
public bool IgnoreMostSpecificConstructor { get; set; } = false;
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
// Use default contract for non-object types.
if (objectType.IsPrimitive || objectType.IsEnum) return contract;
// Look for constructor with attribute first, then single private, then most specific.
var overrideConstructor =
(this.IgnoreAttributeConstructor ? null : GetAttributeConstructor(objectType))
?? (this.IgnoreSinglePrivateConstructor ? null : GetSinglePrivateConstructor(objectType))
?? (this.IgnoreMostSpecificConstructor ? null : GetMostSpecificConstructor(objectType));
// Set override constructor if found, otherwise use default contract.
if (overrideConstructor != null)
{
SetOverrideCreator(contract, overrideConstructor);
}
return contract;
}
private void SetOverrideCreator(JsonObjectContract contract, ConstructorInfo attributeConstructor)
{
contract.OverrideCreator = CreateParameterizedConstructor(attributeConstructor);
contract.CreatorParameters.Clear();
foreach (var constructorParameter in base.CreateConstructorParameters(attributeConstructor, contract.Properties))
{
contract.CreatorParameters.Add(constructorParameter);
}
}
private ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
{
var c = method as ConstructorInfo;
if (c != null)
return a => c.Invoke(a);
return a => method.Invoke(null, a);
}
protected virtual ConstructorInfo GetAttributeConstructor(Type objectType)
{
var constructors = objectType
.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(c => c.GetCustomAttributes().Any(a => a.GetType().Name == this.ConstructorAttributeName)).ToList();
if (constructors.Count == 1) return constructors[0];
if (constructors.Count > 1)
throw new JsonException($"Multiple constructors with a {this.ConstructorAttributeName}.");
return null;
}
protected virtual ConstructorInfo GetSinglePrivateConstructor(Type objectType)
{
var constructors = objectType
.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);
return constructors.Length == 1 ? constructors[0] : null;
}
protected virtual ConstructorInfo GetMostSpecificConstructor(Type objectType)
{
var constructors = objectType
.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.OrderBy(e => e.GetParameters().Length);
var mostSpecific = constructors.LastOrDefault();
return mostSpecific;
}
}
Here is the complete version with XML documentation as a gist:
https://gist.github.com/bjorn-jarisch/80f77f4b6bdce3b434b0f7a1d06baa95
Feedback appreciated.
The default behaviour of Newtonsoft.Json is going to find the public constructors. If your default constructor is only used in containing class or the same assembly, you can reduce the access level to protected or internal so that Newtonsoft.Json will pick your desired public constructor.
Admittedly, this solution is rather very limited to specific cases.
internal Result() { }
public Result(int? code, string format, Dictionary<string, string> details = null)
{
Code = code ?? ERROR_CODE;
Format = format;
if (details == null)
Details = new Dictionary<string, string>();
else
Details = details;
}
Based on the answer by Zoltan, I created a variation that lets you use a specific constructor based on its signature.
Usage
return new JsonSerializerSettings
{
ContractResolver = new DynamicObjectResolver(t =>
{
if (t == typeof(QueueProperties))
return new Type[] { typeof(string) };
return null;
})
};
An here is the implementation
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Concurrent;
using System.Reflection;
namespace ConsoleApp76.Json
{
class DynamicObjectResolver : DefaultContractResolver
{
private readonly Func<Type, Type[]> GetConstructorSignature;
private readonly ConcurrentDictionary<Type, ConstructorInfo> TypeToConstructorLookup =
new ConcurrentDictionary<Type, ConstructorInfo>();
public DynamicObjectResolver(Func<Type, Type[]> getConstructorSignature)
{
if (getConstructorSignature is null)
throw new ArgumentNullException(nameof(getConstructorSignature));
GetConstructorSignature = getConstructorSignature;
}
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var result = base.CreateObjectContract(objectType);
ConstructorInfo constructor = TypeToConstructorLookup.GetOrAdd(objectType, t => FindConstructorInfo(t));
if (constructor is null)
return result;
result.OverrideCreator = CreateParameterizedConstructor(constructor);
foreach (var param in CreateConstructorParameters(constructor, result.Properties))
result.CreatorParameters.Add(param);
return result;
}
private ConstructorInfo FindConstructorInfo(Type objectType)
{
Type[] constructorSignature = GetConstructorSignature(objectType);
if (constructorSignature is null)
return null;
return objectType.GetConstructor(
bindingAttr:
System.Reflection.BindingFlags.Public
| System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Instance,
binder: null,
types: new Type[] { typeof(string) },
modifiers: null);
}
private static ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
{
if (method is null)
throw new ArgumentNullException(nameof(method));
var c = method as ConstructorInfo;
if (c != null)
return a => c.Invoke(a);
return a => method.Invoke(null, a);
}
}
}
Solution:
public Response Get(string jsonData) {
var json = JsonConvert.DeserializeObject<modelname>(jsonData);
var data = StoredProcedure.procedureName(json.Parameter, json.Parameter, json.Parameter, json.Parameter);
return data;
}
Model:
public class modelname {
public long parameter{ get; set; }
public int parameter{ get; set; }
public int parameter{ get; set; }
public string parameter{ get; set; }
}
Using a C# class generated from an XSD document, I can create an object, and serialize it successfully. However, some attributes have an XmlDefaultValue defined. If any objects have the default value, then those attributes do not get created when the object is serialized.
This is expected behavior according to the documentation. But this is not how I want it to behave. I need to have all such attributes generated in the XML document.
I've checked for any code attributes that can be applied that might force it to be outputted, even if it is the default value, but I couldn't find anything like that.
Is there any way to make that work?
The last answer regarding DataContract is NOT the answer. The XSD is generated automatically and the person consuming the classes is not in control of the attributes used by the original author. The question was about auto-generated classes based on an XSD.
The other answer is problematic too because properties that have defaults defined also may not allow null values (this happens often). The only real solution is to have a serializer where you can tell it what properties to ignore with respect to serialization. This has been and always be a serious problem with current XML serializers that simply don't allow one to pass in what properties to force being serialized.
Actual scenario:
A REST service accepts XML in the body to update an object. The XML has an XSD defined by the author of the rest service. The current object stored by the rest service has a non-default value set. The users modifies the XML to change it back to the default... but the serialized version put in the body of the REST post skips the value and doesn't include it because its set to a default value.
What a quagmire... can't update the value because the logic behind not exporting default values completely ignores the idea that XML can be used to update an object, not just create new ones based on the XML. I can't believe its been this many years and nobody modified XML serializers to handle this basic scenario with ease.
You can do this for a specific set of types when serializing by constructing an XmlAttributeOverrides that specifies new XmlAttributes() { XmlDefaultValue = null } for every field or property that has DefaultValueAttribute applied, then passing this to the XmlSerializer(Type, XmlAttributeOverrides) constructor:
var overrides = new XmlAttributeOverrides();
var attrs = new XmlAttributes() { XmlDefaultValue = null };
overrides.Add(typeToSerialize, propertyNameWithDefaultToIgnore, attrs);
var serializer = new XmlSerializer(typeToSerialize, overrides);
Note, however, this important warning from the documentation:
Dynamically Generated Assemblies
To increase performance, the XML serialization infrastructure dynamically generates assemblies to serialize and deserialize specified types. The infrastructure finds and reuses those assemblies. This behavior occurs only when using the following constructors:
XmlSerializer.XmlSerializer(Type)
XmlSerializer.XmlSerializer(Type, String)
If you use any of the other constructors, multiple versions of the same assembly are generated and never unloaded, which results in a memory leak and poor performance. The easiest solution is to use one of the previously mentioned two constructors. Otherwise, you must cache the assemblies in a Hashtable, as shown in the following example.
However, the example given in the code doesn't give any suggestion of how to key the hashtable. It also isn't thread-safe. (Perhaps it dates from .Net 1.0?)
The following code creates a key scheme for xml serializers with overrides, and manufactures (via reflection) serializers for which the [DefaultValue] values (if any) of all properties and fields are overridden to be null, effectively cancelling the default value. Note, when creating a blank XmlAttributes() object all attributes are set to null. When overriding with this XmlAttributes object any attributes that are desired to stay need to be transferred into this new object:
public abstract class XmlSerializerKey
{
static class XmlSerializerHashTable
{
static Dictionary<object, XmlSerializer> dict;
static XmlSerializerHashTable()
{
dict = new Dictionary<object, XmlSerializer>();
}
public static XmlSerializer GetSerializer(XmlSerializerKey key)
{
lock (dict)
{
XmlSerializer value;
if (!dict.TryGetValue(key, out value))
dict[key] = value = key.CreateSerializer();
return value;
}
}
}
readonly Type serializedType;
protected XmlSerializerKey(Type serializedType)
{
this.serializedType = serializedType;
}
public Type SerializedType { get { return serializedType; } }
public override bool Equals(object obj)
{
if (ReferenceEquals(this, obj))
return true;
else if (ReferenceEquals(null, obj))
return false;
if (GetType() != obj.GetType())
return false;
XmlSerializerKey other = (XmlSerializerKey)obj;
if (other.serializedType != serializedType)
return false;
return true;
}
public override int GetHashCode()
{
int code = 0;
if (serializedType != null)
code ^= serializedType.GetHashCode();
return code;
}
public override string ToString()
{
return string.Format(base.ToString() + ": for type: " + serializedType.ToString());
}
public XmlSerializer GetSerializer()
{
return XmlSerializerHashTable.GetSerializer(this);
}
protected abstract XmlSerializer CreateSerializer();
}
public abstract class XmlserializerWithExtraTypesKey : XmlSerializerKey
{
static IEqualityComparer<HashSet<Type>> comparer;
readonly HashSet<Type> extraTypes = new HashSet<Type>();
static XmlserializerWithExtraTypesKey()
{
comparer = HashSet<Type>.CreateSetComparer();
}
protected XmlserializerWithExtraTypesKey(Type serializedType, IEnumerable<Type> extraTypes)
: base(serializedType)
{
if (extraTypes != null)
foreach (var type in extraTypes)
this.extraTypes.Add(type);
}
public Type[] ExtraTypes { get { return extraTypes.ToArray(); } }
public override bool Equals(object obj)
{
if (!base.Equals(obj))
return false;
XmlserializerWithExtraTypesKey other = (XmlserializerWithExtraTypesKey)obj;
return comparer.Equals(this.extraTypes, other.extraTypes);
}
public override int GetHashCode()
{
int code = base.GetHashCode();
if (extraTypes != null)
code ^= comparer.GetHashCode(extraTypes);
return code;
}
}
public sealed class XmlSerializerIgnoringDefaultValuesKey : XmlserializerWithExtraTypesKey
{
readonly XmlAttributeOverrides overrides;
private XmlSerializerIgnoringDefaultValuesKey(Type serializerType, IEnumerable<Type> ignoreDefaultTypes, XmlAttributeOverrides overrides)
: base(serializerType, ignoreDefaultTypes)
{
this.overrides = overrides;
}
public static XmlSerializerIgnoringDefaultValuesKey Create(Type serializerType, IEnumerable<Type> ignoreDefaultTypes, bool recurse)
{
XmlAttributeOverrides overrides;
Type [] typesWithOverrides;
CreateOverrideAttributes(ignoreDefaultTypes, recurse, out overrides, out typesWithOverrides);
return new XmlSerializerIgnoringDefaultValuesKey(serializerType, typesWithOverrides, overrides);
}
protected override XmlSerializer CreateSerializer()
{
var types = ExtraTypes;
if (types == null || types.Length < 1)
return new XmlSerializer(SerializedType);
return new XmlSerializer(SerializedType, overrides);
}
static void CreateOverrideAttributes(IEnumerable<Type> types, bool recurse, out XmlAttributeOverrides overrides, out Type[] typesWithOverrides)
{
HashSet<Type> visited = new HashSet<Type>();
HashSet<Type> withOverrides = new HashSet<Type>();
overrides = new XmlAttributeOverrides();
foreach (var type in types)
{
CreateOverrideAttributes(type, recurse, overrides, visited, withOverrides);
}
typesWithOverrides = withOverrides.ToArray();
}
static void CreateOverrideAttributes(Type type, bool recurse, XmlAttributeOverrides overrides, HashSet<Type> visited, HashSet<Type> withOverrides)
{
if (type == null || type == typeof(object) || type.IsPrimitive || type == typeof(string) || visited.Contains(type))
return;
var attrs = new XmlAttributes() { XmlDefaultValue = null };
foreach (var property in type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public))
if (overrides[type, property.Name] == null) // Check to see if overrides for this base type were already set.
if (property.GetCustomAttributes<DefaultValueAttribute>(true).Any())
{
withOverrides.Add(type);
overrides.Add(type, property.Name, attrs);
}
foreach (var field in type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public))
if (overrides[type, field.Name] == null) // Check to see if overrides for this base type were already set.
if (field.GetCustomAttributes<DefaultValueAttribute>(true).Any())
{
withOverrides.Add(type);
overrides.Add(type, field.Name, attrs);
}
visited.Add(type);
if (recurse)
{
var baseType = type.BaseType;
if (baseType != type)
CreateOverrideAttributes(baseType, recurse, overrides, visited, withOverrides);
}
}
}
And then you would call it like:
var serializer = XmlSerializerIgnoringDefaultValuesKey.Create(typeof(ClassToSerialize), new[] { typeof(ClassToSerialize), typeof(AdditionalClass1), typeof(AdditionalClass2), ... }, true).GetSerializer();
For example, in the following class hierarchy:
public class BaseClass
{
public BaseClass() { Index = 1; }
[DefaultValue(1)]
public int Index { get; set; }
}
public class MidClass : BaseClass
{
public MidClass() : base() { MidDouble = 1.0; }
[DefaultValue(1.0)]
public double MidDouble { get; set; }
}
public class DerivedClass : MidClass
{
public DerivedClass() : base() { DerivedString = string.Empty; }
[DefaultValue("")]
public string DerivedString { get; set; }
}
public class VeryDerivedClass : DerivedClass
{
public VeryDerivedClass() : base() { this.VeryDerivedIndex = -1; }
[DefaultValue(-1)]
public int VeryDerivedIndex { get; set; }
}
The default XmlSerializer produces:
<VeryDerivedClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
But the custom serializer produces
<?xml version="1.0" encoding="utf-16"?>
<VeryDerivedClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Index>1</Index>
<MidDouble>1</MidDouble>
<DerivedString />
<VeryDerivedIndex>-1</VeryDerivedIndex>
</VeryDerivedClass>
Finally, note that writing of null values is controlled by [XmlElement( IsNullable = true )] so writing of nulls is not affected by this serializer.
Example how to force serialize all public properties with XmlDefaultValue attribute:
[Test]
public void GenerateXMLWrapTest()
{
var xmlWrap = new XmlWrap();
using (var sw = new StringWriter())
{
var overrides = new XmlAttributeOverrides();
var attrs = new XmlAttributes { XmlDefaultValue = null };
var type = typeof(XmlWrap);
foreach (var propertyInfo in type.GetProperties())
{
if (propertyInfo.CanRead && propertyInfo.CanWrite && propertyInfo.GetCustomAttributes(true).Any(o => o is DefaultValueAttribute))
{
var propertyNameWithDefaultToIgnore = propertyInfo.Name;
overrides.Add(type, propertyNameWithDefaultToIgnore, attrs);
}
}
var serializer = new XmlSerializer(type, overrides);
serializer.Serialize(sw, xmlWrap);
sw.Flush();
var xmlString = sw.ToString();
Console.WriteLine(xmlString);
}
}
Output:
<?xml version="1.0" encoding="utf-16"?>
<ConIdTranslator xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="urn:devices-description-1.0">
<Disabled>false</Disabled>
<HostPortParams>COM1 baud=115200 parity=None data=8 stop=One</HostPortParams>
<TranslatorObjectNumber>9000</TranslatorObjectNumber>
...
Where Disabled, HostPortParams, TranslatorObjectNumber public properties of serialized class has default value attribute:
[Serializable]
[XmlRoot("ConIdTranslator", Namespace = "urn:devices-description-1.0", IsNullable = false)]
public class ConIdTranslatorXmlWrap : HardwareEntityXmlWrap
{
#region Fields
[EditorBrowsable(EditorBrowsableState.Never)]
[XmlIgnore]
private string hostPortParams = "COM1 baud=115200 parity=None data=8 stop=One";
[EditorBrowsable(EditorBrowsableState.Never)]
[XmlIgnore]
private bool disabled = false;
...
#endregion
#region Properties
[XmlElement]
[DefaultValue(false)]
public bool Disabled
{
get => this.disabled;
set
{
this.disabled = value;
this.OnPropertyChanged("Disabled");
}
}
[XmlElement]
[DefaultValue("COM1 baud=115200 parity=None data=8 stop=One")]
public string HostPortParams
{
get => this.hostPortParams;
set
{
this.hostPortParams = value;
this.OnPropertyChanged("HostPortParams");
}
}
...
I found the answer:
https://msdn.microsoft.com/en-us/library/system.runtime.serialization.datamemberattribute.emitdefaultvalue%28v=vs.110%29.aspx
Set the attribute in the DataContract like this: [DataMember(EmitDefaultValue=true)]