Why Json.NET can't deserialize structs without JsonConstructor attribute? [duplicate] - c#

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

How can I get a null value instead of a serialization error when deserializing an enum by string conversion?

A few of my API endpoints have models that include enums. FluentValidation is being used to verify that the values sent across meet their respective requirements.
To aid in usability and document generation, enums are allowed to be sent as strings rather than integers. Validation that the value sent across is in the correct range works fine if an invalid integer is sent, but serialization will fail if an invalid string is sent across.
public enum Foo
{
A = 1,
B = 2
}
public class Bar
{
public Foo? Foo {get;set;}
}
void Main()
{
var options = new JsonSerializerOptions();
options.Converters.Add(new JsonStringEnumConverter());
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
var jsonString = "{\"foo\": \"C\"}";
var jsonSpan = (ReadOnlySpan<byte>)Encoding.UTF8.GetBytes(jsonString);
try
{
var result = JsonSerializer.Deserialize<Bar>(jsonSpan, options);
Console.WriteLine(result.Foo == null);
}
catch(Exception ex)
{
Console.WriteLine("Serialization Failed");
}
}
My desired outcome would be to simply deserialize the enum property to null when the string does not match any of the enum's fields so that the model can be passed through to the validator to create a friendly message.
How can I achieve this? This is using net-core 3 preview 8 with the System.Text.Json API.
As far I have tried, I have 2 solutions, one using System.Text.Json and the other one is Newtonsoft.
System.Text.Json
Your create a custom class using JsonConverter
You introduce Unknown enum in Foo.
in stead of using JsonStringEnumConverter
options.Converters.Add(new JsonStringEnumConverter());
Use your customized class CustomEnumConverter
options.Converters.Add(new CustomEnumConverter());
So lets put thing together:
public enum Foo
{
A = 1,
B = 2,
// what ever name and enum number that fits your logic
Unknown = 99
}
public class Bar
{
public Foo? Foo { get; set; }
}
public static void Main()
{
var options = new JsonSerializerOptions();
options.Converters.Add(new CustomEnumConverter());
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
var jsonString = "{\"foo\": \"C\"}";
var jsonSpan = (ReadOnlySpan<byte>)Encoding.UTF8.GetBytes(jsonString);
try
{
var result = JsonSerializer.Deserialize<Bar>(jsonSpan, options);
if (result.Foo == Foo.Unknown)
result.Foo = null;
Console.WriteLine(result.Foo == null);
}
catch (Exception ex)
{
Console.WriteLine("Serialization Failed" + ex.Message);
}
}
Here is the code CustomEnumConverter
internal sealed class CustomEnumConverter : JsonConverter<Foo>
{
public override Foo Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
switch (reader.TokenType)
{
case JsonTokenType.String:
var isNullable = IsNullableType(typeToConvert);
var enumType = isNullable ? Nullable.GetUnderlyingType(typeToConvert) : typeToConvert;
var names = Enum.GetNames(enumType ?? throw new InvalidOperationException());
if (reader.TokenType != JsonTokenType.String) return Foo.Unknown;
var enumText = System.Text.Encoding.UTF8.GetString(reader.ValueSpan);
if (string.IsNullOrEmpty(enumText)) return Foo.Unknown;
var match = names.FirstOrDefault(e => string.Equals(e, enumText, StringComparison.OrdinalIgnoreCase));
return (Foo) (match != null ? Enum.Parse(enumType, match) : Foo.Unknown);
default:
throw new ArgumentOutOfRangeException();
}
}
public override void Write(Utf8JsonWriter writer, Foo value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}
private static bool IsNullableType(Type t)
{
return (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>));
}
}
Running this code should return True with out exception.
For this solution I got some inspiration from here.
The other way is a bit similar but using Newtonsoft.
Note: Remember what I did here is just example to demonstrate stuff,
please validate every thing, test it before going production.
Newtonsoft (Original Answer)
Another way to solve this using Newtonsoft with custom JsonConverter.
What you do is added attribute of your custom JsonConverter to your Foo class [JsonConverter(typeof(CustomEnumConverter))].
Then make your class method to return null if the enum is not recognized.
You can of course customize almost any type and have different customization classes.
Ok install Newtonsoft.Json nuget package via Nuget Manager.
We start with you code modification:
//add the attribute here
[JsonConverter(typeof(CustomEnumConverter))]
public enum Foo
{
A = 1,
B = 2
}
public class Bar
{
public Foo? Foo { get; set; }
}
public static void Main()
{
var jsonString = "{\"foo\": \"C\"}";
try
{
// use newtonsoft json converter
var result = JsonConvert.DeserializeObject<Bar>(jsonString);
Console.WriteLine(result.Foo == null);
}
catch (Exception ex)
{
Console.WriteLine("Serialization Failed" + ex.Message);
}
}
And now for your customization class:
public class CustomEnumConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
var type = IsNullableType(objectType) ? Nullable.GetUnderlyingType(objectType) : objectType;
return type != null && type.IsEnum;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var isNullable = IsNullableType(objectType);
var enumType = isNullable ? Nullable.GetUnderlyingType(objectType) : objectType;
var names = Enum.GetNames(enumType ?? throw new InvalidOperationException());
if (reader.TokenType != JsonToken.String) return null;
var enumText = reader.Value.ToString();
if (string.IsNullOrEmpty(enumText)) return null;
var match = names.FirstOrDefault(e => string.Equals(e, enumText, StringComparison.OrdinalIgnoreCase));
return match != null ? Enum.Parse(enumType, match) : null;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
public override bool CanWrite => true;
private static bool IsNullableType(Type t)
{
return (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>));
}
}
Now it is test time.
When we fire the program with out [JsonConverter(typeof(CustomEnumConverter))] we get error as shown here:
But when we added [JsonConverter(typeof(CustomEnumConverter))] and run the program again it works:
Links:
https://www.newtonsoft.com/json
I got inspiration from this answer: How can I ignore unknown enum values during json deserialization?
https://bytefish.de/blog/enums_json_net/
You can deserialize into a string and TryParse
public class Bar
{
public string Foo { get; set; }
public Foo? FooEnum { get; set; }
}
...
var result = JsonSerializer.Deserialize<Bar>(jsonSpan, options);
Enum.TryParse<Foo>(result, out Bar.FooEnum);

How to add metadata to describe which properties are dates in JSON.Net

I would like to add a metadata property to my json so that the client side can know what properties are dates.
For example if I had an object like this:
{
"notADate": "a value",
"aDate": "2017-04-23T18:25:43.511Z",
"anotherDate": "2017-04-23T18:25:43.511Z"
}
I would like to add a metadata property to tell the consumer which properties to treat as dates something like this:
{
"_date_properties_": ["aDate", "anotherDate"],
"notADate": "a value",
"aDate": "2017-04-23T18:25:43.511Z",
"anotherDate": "2017-04-23T18:25:43.511Z"
}
Any help would be great, thanks!
You could create a custom ContractResolver that inserts a synthetic "_date_properties_" property into the contract of every object that is serialized.
To do this, first subclass DefaultContractResolver to allow contracts to be fluently customized after they have been created by application-added event handlers:
public class ConfigurableContractResolver : DefaultContractResolver
{
readonly object contractCreatedPadlock = new object();
event EventHandler<ContractCreatedEventArgs> contractCreated;
int contractCount = 0;
void OnContractCreated(JsonContract contract, Type objectType)
{
EventHandler<ContractCreatedEventArgs> created;
lock (contractCreatedPadlock)
{
contractCount++;
created = contractCreated;
}
if (created != null)
{
created(this, new ContractCreatedEventArgs(contract, objectType));
}
}
public event EventHandler<ContractCreatedEventArgs> ContractCreated
{
add
{
lock (contractCreatedPadlock)
{
if (contractCount > 0)
{
throw new InvalidOperationException("ContractCreated events cannot be added after the first contract is generated.");
}
contractCreated += value;
}
}
remove
{
lock (contractCreatedPadlock)
{
if (contractCount > 0)
{
throw new InvalidOperationException("ContractCreated events cannot be removed after the first contract is generated.");
}
contractCreated -= value;
}
}
}
protected override JsonContract CreateContract(Type objectType)
{
var contract = base.CreateContract(objectType);
OnContractCreated(contract, objectType);
return contract;
}
}
public class ContractCreatedEventArgs : EventArgs
{
public JsonContract Contract { get; private set; }
public Type ObjectType { get; private set; }
public ContractCreatedEventArgs(JsonContract contract, Type objectType)
{
this.Contract = contract;
this.ObjectType = objectType;
}
}
public static class ConfigurableContractResolverExtensions
{
public static ConfigurableContractResolver Configure(this ConfigurableContractResolver resolver, EventHandler<ContractCreatedEventArgs> handler)
{
if (resolver == null || handler == null)
throw new ArgumentNullException();
resolver.ContractCreated += handler;
return resolver;
}
}
Next, create an extension method to add the desired property to a JsonObjectContract:
public static class JsonContractExtensions
{
const string DatePropertiesName = "_date_properties_";
public static void AddDateProperties(this JsonContract contract)
{
var objectContract = contract as JsonObjectContract;
if (objectContract == null)
return;
var properties = objectContract.Properties.Where(p => p.PropertyType == typeof(DateTime) || p.PropertyType == typeof(DateTime?)).ToList();
if (properties.Count > 0)
{
var property = new JsonProperty
{
DeclaringType = contract.UnderlyingType,
PropertyName = DatePropertiesName,
UnderlyingName = DatePropertiesName,
PropertyType = typeof(string[]),
ValueProvider = new FixedValueProvider(properties.Select(p => p.PropertyName).ToArray()),
AttributeProvider = new NoAttributeProvider(),
Readable = true,
Writable = false,
// Ensure // Ensure PreserveReferencesHandling and TypeNameHandling do not apply to the synthetic property.
ItemIsReference = false,
TypeNameHandling = TypeNameHandling.None,
};
objectContract.Properties.Insert(0, property);
}
}
class FixedValueProvider : IValueProvider
{
readonly object properties;
public FixedValueProvider(object value)
{
this.properties = value;
}
#region IValueProvider Members
public object GetValue(object target)
{
return properties;
}
public void SetValue(object target, object value)
{
throw new NotImplementedException("SetValue not implemented for fixed properties; set JsonProperty.Writable = false.");
}
#endregion
}
class NoAttributeProvider : IAttributeProvider
{
#region IAttributeProvider Members
public IList<Attribute> GetAttributes(Type attributeType, bool inherit) { return new Attribute[0]; }
public IList<Attribute> GetAttributes(bool inherit) { return new Attribute[0]; }
#endregion
}
}
Finally, serialize your example type as follows:
var settings = new JsonSerializerSettings
{
ContractResolver = new ConfigurableContractResolver
{
// Here I am using CamelCaseNamingStrategy as is shown in your JSON.
// If you don't want camel case, leave NamingStrategy null.
NamingStrategy = new CamelCaseNamingStrategy(),
}.Configure((s, e) => { e.Contract.AddDateProperties(); }),
};
var json = JsonConvert.SerializeObject(example, Formatting.Indented, settings);
This solution only handles statically typed DateTime and DateTime? properties. If you have object-valued properties that sometimes have DateTime values, or a Dictionary<string, DateTime>, or extension data containing DateTime values, you will need a more complex solution.
(As an alternative implementation, you could instead subclass DefaultContractResolver.CreateObjectContract and hardcode the required properties there using JsonContractExtensions.AddDateProperties(), however I thought it would be more interesting to create a general-purpose, fluently configurable contract resolver in case it becomes necessary to plug in different customizations later.)
You may want to cache the contract resolver for best performance.
Sample .Net fiddle.

Implementing ASP.NET Web API Optional Parameters

I need the ability to distinguish between a key not being supplied and null.
An example of the JSON would be:
# key not specified
{}
# key specified but null
{'optionalKey' : null}
# key specified and is valid
{'optionalKey' : 123}
To distinguishable between a key's absence and null, I've created a generic Optional class which wraps each field, but this requires writing a custom JsonConverter and DefaultContractResolver to flatten the JSON / unpack the OptionalType (sending nested JSON for each field is not an option).
I've managed to create a LINQPad script to do this but I can't help but thinking there must be an easier way that doesn't involve reflection?
void Main()
{
//null
Settings settings = null;
JsonConvert.SerializeObject(settings, new JsonSerializerSettings() { ContractResolver = new ShouldSerializeContractResolver() }).Dump();
settings = new Settings();
// no key {}
settings.OptionalIntegerSetting = null;
JsonConvert.SerializeObject(settings, new JsonSerializerSettings() { ContractResolver = new ShouldSerializeContractResolver() }).Dump();
// null key {\"OptionalIntegerSetting\" : null}
settings.OptionalIntegerSetting = new Optional<uint?>(); // assigning this to null assigns the optional type class, it does not use the implict operators.
JsonConvert.SerializeObject(settings, new JsonSerializerSettings() { ContractResolver = new ShouldSerializeContractResolver() }).Dump();
// has value {\"OptionalIntegerSetting\" : 123}
settings.OptionalIntegerSetting = 123;
JsonConvert.SerializeObject(settings, new JsonSerializerSettings() { ContractResolver = new ShouldSerializeContractResolver() }).Dump();
JsonConvert.DeserializeObject<Settings>("{}").Dump();
JsonConvert.DeserializeObject<Settings>("{'OptionalIntegerSetting' : null}").Dump();
JsonConvert.DeserializeObject<Settings>("{'OptionalIntegerSetting' : '123'}").Dump(); // supplying 'a string' instead of '123' currently breaks OptionalConverter.ReadJson
}
public class Settings
{
public Optional<uint?> OptionalIntegerSetting { get; set; }
}
[JsonConverter(typeof(OptionalConverter))]
public class Optional<T>
{
public T Value { get; set; }
public Optional() { }
public Optional(T value)
{
Value = value;
}
public static implicit operator Optional<T>(T t)
{
return new Optional<T>(t);
}
public static implicit operator T(Optional<T> t)
{
return t.Value;
}
}
// Provides a way of populating the POCO Resource model with CanSerialise proerties at the point just before serialisation.
// This prevents having to define a CanSerialiseMyProperty method for each property.
public class ShouldSerializeContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(Optional<>))
{
// add an additional ShouldSerialize property to omit no json
property.ShouldSerialize = instance =>
instance.GetType().GetProperty(property.PropertyName).GetValue(instance) != null;
}
return property;
}
}
// Performs the conversion to and from a JSON value to compound type
public class OptionalConverter : JsonConverter
{
public override bool CanWrite => true;
public override bool CanRead => true;
public override bool CanConvert(Type objectType)
{
return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(Optional<>);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jtoken = JToken.Load(reader);
var genericTypeArgument = objectType.GetGenericArguments()[0];
var constructor = objectType.GetConstructor(new[] { genericTypeArgument });
var result = JTokenType.Null != jtoken.Type ? jtoken.ToObject(genericTypeArgument) : null;
return constructor.Invoke(new object[] { JTokenType.Null != jtoken.Type ? jtoken.ToObject(genericTypeArgument) : null });
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var val = value.GetType().GetProperty("Value").GetValue(value);
(val != null ? JValue.FromObject(val) : JValue.CreateNull()).WriteTo(writer);
}
}
Full credit goes to #dbc.
void Main()
{
var settings = new Settings();
// no key {}
settings.OptionalIntegerSetting = null;
JsonConvert.SerializeObject(settings).Dump();
// null key {\"OptionalIntegerSetting\" : null}
settings.OptionalIntegerSetting = null;
settings.OptionalIntegerSettingSpecified = true;
JsonConvert.SerializeObject(settings).Dump();
// has value {\"OptionalIntegerSetting\" : 123}
settings.OptionalIntegerSetting = 123;
JsonConvert.SerializeObject(settings).Dump();
JsonConvert.DeserializeObject<Settings>("{}").Dump();
JsonConvert.DeserializeObject<Settings>("{'OptionalIntegerSetting' : null}").Dump();
JsonConvert.DeserializeObject<Settings>("{'OptionalIntegerSetting' : '123'}").Dump();
}
public class Settings
{
public uint? OptionalIntegerSetting { get; set; }
[JsonIgnore]
public bool OptionalIntegerSettingSpecified { get; set;}
}

id field resolution when using JObject.FromObject

I have a family of custom Json Converters. They work like this:
public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer) {
JObject jo = JObject.FromObject(value);
// do my own stuff to the JObject here -- basically adding a property. The value of the property depends on the specific converter being used.
jo.WriteTo(writer, this);
}
The problem with this is that the id field of the JObject is always 1. Not good. So I tried using an inner serializer to get the id field:
private JsonSerializer _InnerSerializer {get;set;}
private JsonSerializer InnerSerializer {
get {
if (_InnerSerializer == null) {
_InnerSerializer = new JsonSerializer();
}
return _InnerSerializer;
}
}
public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer) {
JsonSerializer inner = this.InnerSerializer;
jo = JObject.FromObject(value, inner);
//my stuff here
jo.WriteTo(writer, this);
}
That gives a different id each time, even if it hits the same object twice. What I really want is to use Json's usual id resolution with my custom serialization. How can I do that?
Your idea of using an inner serializer will not work as-is. The id-to-object mapping table is held in a private field JsonSerializerInternalBase._mappings with no way to copy it from the outer serializer to the inner serializer.
As an alternative, you could make a recursive call to serialize using the same serializer, and have the converter disable itself using a thread-static pushdown stack along the lines of Generic method of modifying JSON before being returned to client and JSON.Net throws StackOverflowException when using [JsonConvert()]. You would need to enhance the converters in these examples to manually check for and add the necessary "$id" and "$ref" properties by making use of the JsonSerializer.ReferenceResolver property.
However, since your converters just add properties, a more straightforward solution to your problem might be to create a custom contract resolver that allows types to customize their contract as it is generated via a callback method declared in an attribute applied to the type, for instance:
public class ModifierContractResolver : DefaultContractResolver
{
// As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons.
// http://www.newtonsoft.com/json/help/html/ContractResolver.htm
// http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm
// "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance."
// See also https://stackoverflow.com/questions/33557737/does-json-net-cache-types-serialization-information
static ModifierContractResolver instance;
// Explicit static constructor to tell C# compiler not to mark type as beforefieldinit
static ModifierContractResolver() { instance = new ModifierContractResolver(); }
public static ModifierContractResolver Instance { get { return instance; } }
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
// Apply in reverse order so inherited types are applied after base types.
foreach (var attr in objectType.GetCustomAttributes<JsonObjectContractModifierAttribute>(true).Reverse())
{
var modifier = (JsonObjectContractModifier)Activator.CreateInstance(attr.ContractModifierType, true);
modifier.ModifyContract(objectType, contract);
}
return contract;
}
}
public abstract class JsonObjectContractModifier
{
public abstract void ModifyContract(Type objectType, JsonObjectContract contract);
}
[System.AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public class JsonObjectContractModifierAttribute : System.Attribute
{
private readonly Type _contractModifierType;
public Type ContractModifierType { get { return _contractModifierType; } }
public JsonObjectContractModifierAttribute(Type contractModifierType)
{
if (contractModifierType == null)
{
throw new ArgumentNullException("contractModifierType");
}
if (!typeof(JsonObjectContractModifier).IsAssignableFrom(contractModifierType))
{
throw new ArgumentNullException(string.Format("{0} is not a subtype of {1}", contractModifierType, typeof(JsonObjectContractModifier)));
}
this._contractModifierType = contractModifierType;
}
}
Then, apply it to your types as in the following example:
[JsonObjectContractModifier(typeof(TestContractModifier))]
public class Test
{
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }
}
class TestContractModifier : JsonObjectContractModifier
{
class EmptyValueProvider : IValueProvider
{
// Explicit static constructor to tell C# compiler not to mark type as beforefieldinit
static EmptyValueProvider() { }
internal static readonly EmptyValueProvider Instance = new EmptyValueProvider();
#region IValueProvider Members
public object GetValue(object target)
{
var test = target as Test;
if (test == null)
return null;
return test.A == null && test.B == null && test.C == null;
}
public void SetValue(object target, object value)
{
var property = target as Test;
if (property == null)
return;
if (value != null && value.GetType() == typeof(bool) && (bool)value == true)
{
property.A = property.B = property.C = null;
}
}
#endregion
}
public override void ModifyContract(Type objectType, JsonObjectContract contract)
{
var jsonProperty = new JsonProperty
{
PropertyName = "isEmpty",
UnderlyingName = "isEmpty",
PropertyType = typeof(bool?),
NullValueHandling = NullValueHandling.Ignore,
Readable = true,
Writable = true,
DeclaringType = typeof(Test),
ValueProvider = EmptyValueProvider.Instance,
};
contract.Properties.Add(jsonProperty);
}
}
And serialize as follows:
var settings = new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects, // Or PreserveReferencesHandling.All
ContractResolver = ModifierContractResolver.Instance,
};
var json = JsonConvert.SerializeObject(root, Formatting.Indented, settings);
This produces the following JSON:
[
{
"$id": "1",
"A": "hello",
"B": "goodbye",
"C": "sea",
"isEmpty": false
},
{
"$ref": "1"
},
{
"$id": "2",
"A": null,
"B": null,
"C": null,
"isEmpty": true
},
}
As you can see, both the synthetic "isEmpty" property and reference handling properties are present. Prototype fiddle.

Serialize only simple types using Json.Net

I am not sure why anybody has not asked about this question yet but I am trying to serialize only simple types of a given object using IContractResolver interface. I don't really want to mark each property using ShouldSerialize method or JsonDataAttribute or anything like this.
What I have done so far shown as following on LinqPad
Some sample classes to serialize
class Customer
{
public List<Order> Orders {get;set;}
public int CustomerId {get;set;}
public string[] Addresses {get;set;}
}
class Order
{
public int OrderId{get;set;}
public string Name {get;set;}
public int Amount {get;set;}
public Order PreviousOrder {get;set;}
}
An extension method to serialize all the objects
static class ExtensionMethods
{
public static string JsonSerialize (this object obj)
{
var settings = new JsonSerializerSettings();
settings.ContractResolver = new MyContractResolver();
settings.DefaultValueHandling = DefaultValueHandling.Ignore;
settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
return JsonConvert.SerializeObject(obj,settings);
}
}
My Custom Contract Resolver Class
public class MyContractResolver: DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member,memberSerialization);
property.ShouldSerialize = instance => instance.GetType().IsPrimitive || instance.GetType() == typeof(string) || instance.GetType() == typeof(decimal);
return property;
}
}
and Main method:
void Main()
{
var customer = new Customer
{
Orders = ProduceSomeOrders(),
Addresses = new string[] {"9450 S. Small Street Dr.","9521 Flintstone Dr. S"},
CustomerId = 1
};
var jsonOrder = customer.JsonSerialize();
jsonOrder.Dump();
}
I only want to serialize sample types such as int,double,string,decimal,bool etc. but not arrays, collections, custom objects etc. and it will only navigate the first level, not 2nd or more levels down. I really wonder why there is not any simple method which does this in Json.Net.
This is the output when I run this code: (an empty json)
{ }
I've realized one thing when I run this code, the first member parameter passed into the CreateProperty method is the main object itself which in this case is Customer instance. But since this will work for all sort of types I just don't want to say instance.GetType() == typeof(Customer) or something like this in the method. The expected output in this case is only CustomerId in this case.
Do you know any graceful way to handle my problem?
By changing the method below I got the result: {"CustomerId":1}. Is this what you were looking for?
public class MyContractResolver:DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member,memberSerialization);
var propertyType = property.PropertyType;
if(propertyType == typeof(int)
|| propertyType == typeof(string)){
property.ShouldSerialize = instance => true;
}
else
{
property.ShouldSerialize = instance => false;
}
return property;
}
}
Instead of overriding CreateProperty method, I am overriding GetSerializableObjects method as it gives all the possible members I will get:
public class MyContractResolver: DefaultContractResolver
{
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
var members = base.GetSerializableMembers(objectType);
var filteredMembers = new List<MemberInfo>();
members.ForEach(m=>{
if(m.MemberType == MemberTypes.Property)
{
PropertyInfo info = (PropertyInfo) m;
var type = info.PropertyType;
if(type.IsPrimitive || type == typeof(string) || type == typeof(decimal))
{
filteredMembers.Add(m);
}
}
});
return filteredMembers;
}
}

Categories

Resources