How to force Newtonsoft Json to serialize all properties? (Strange behavior with Specified property)
This behavior is mentioned, very briefly, in the Json.NET 4.0.1 release notes: New feature - Added XmlSerializer style Specified property support. The XmlSerializer
functionality is in turn described in MinOccurs Attribute Binding Support:
[For optional fields] Xsd.exe generates a public field of type
bool
whose name is the element field's name with Specified appended. For example, if the element field's name is startDate, thebool
field's name becomes startDateSpecified. When serializing an object to XML, the XmlSerializer class checks the value of thebool
field to determine whether to write the element.
I feel as though this functionality should be documented here, but is not. You might want to open a documentation issue with Newtonsoft.
Since you don't want this behavior, if you are using Json.NET 11.0.1 or later, you can disable it for all classes by instantiating your own DefaultContractResolver
and settting DefaultContractResolver.IgnoreIsSpecifiedMembers = true
:
public static class JsonContractResolvers
{
// Newtonsoft recommends caching and reusing contract resolvers for best performance:
// https://www.newtonsoft.com/json/help/html/Performance.htm#ReuseContractResolver
// But be sure not to modify IgnoreIsSpecifiedMembers after the contract resolver is first used to generate a contract.
public static readonly DefaultContractResolver IgnoreIsSpecifiedMembersResolver =
new DefaultContractResolver { IgnoreIsSpecifiedMembers = true };
}
Then pass it to JsonConvert
as follows:
var settings = new JsonSerializerSettings { ContractResolver = JsonContractResolvers.IgnoreIsSpecifiedMembersResolver };
var json = JsonConvert.SerializeObject(testObject, Formatting.Indented, settings);
Or to create a JToken
do:
var jToken = JToken.FromObject(testObject, JsonSerializer.CreateDefault(settings));
If you are using an earlier version, you will need to create and cache a custom contract resolver:
public static class JsonContractResolvers
{
public static readonly DefaultContractResolver IgnoreIsSpecifiedMembersResolver =
new IgnoreSpecifiedContractResolver();
}
internal class IgnoreSpecifiedContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
property.GetIsSpecified = null;
property.SetIsSpecified = null;
return property;
}
}
How to make Json.NET set IsSpecified properties for properties with complex values?
You appear to have encountered a limitation in Json.NET's support for {propertyName}Specified
members: the {propertyName}Specified
property is not set when populating an instance of a preallocated reference type property. As a workaround, you can deserialize with the setting JsonSerializerSettings.ObjectCreationHandling = ObjectCreationHandling.Replace
. If you do, new instances of reference type properties will be created by the serializer and set back after creation, thereby toggling the corresponding {propertyName}Specified
property.
A detailed explanation follows. In your Person
type, you automatically allocate instances of the child properties Address
and Details
in the default constructor:
public Person()
{
this._address = new Address();
this._details = new PersonalDetails();
}
Now, because Json.NET supports populating an existing object, during deserialization, after calling your default Person()
constructor, it will populate the values of Address
and Details
that you constructed, rather than creating new ones. And because of that, it apparently never calls the setters for Address
and Details
, perhaps because Newtonsoft assumed there was no need to do so. But that, in turn, seems to prevent the corresponding Specified
properties from being set, as it appears Json.NET toggles them only when the setter is called.
(For comparison, XmlSerializer
never populates preallocated reference type properties other than collection-valued properties, so this situation situation should not arise with XmlSerializer
.)
This might be a bug in Json.NET's implementation of the {propertyName}Specified
pattern. You might want to open an issue about it with Newtonsoft.
Demo fiddle #1 here.
As a workaround, you could:
Deserialize with the setting
JsonSerializerSettings.ObjectCreationHandling = ObjectCreationHandling.Replace
like so:config.Formatters.JsonFormatter.SerializerSettings.ObjectCreationHandling = ObjectCreationHandling.Replace;
This option will Always create new objects and thereby triggers the setting of
Specified
properties.Demo fiddle #2 here.
Remove allocation of
Address
andDetails
from the default constructor forPerson
. Not really recommended, but it does solve the problem.Demo fiddle #3 here.
Force single JSON property to always serialize while DefaultValueHandling is Ignore
You can also specify the default value handling for every property with the JsonProperty
attribute. And if you have default value handling specified globally and per property as well, the property's own attributed value takes precedence over the global setting.
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Include)]
public bool SomeFlagYouAlwaysWantToSee { get; set; }
Newtonsoft.Json - how can I choose the constructor to deserialize based on passed object properties types?
There is no way to configure Json.NET to choose a constructor based on the presence or absence of certain properties in the JSON to be deserialized. It simply isn't implemented.
As a workaround, you can create a custom JsonConverter<Test<T, U>>
that deserializes to some intermediate DTO that tracks the presence of both properties, and then chooses the correct constructor afterwards. Then you can create a custom contract resolver that applies the converter to all concrete types Test<T, U>
.
The following converter and contract resolver perform this task:
class TestConverter<T, U> : JsonConverter where U : struct
{
// Here we make use of the {PropertyName}Specified pattern to track which properties actually got deserialized.
// https://stackoverflow.com/questions/39223335/how-to-force-newtonsoft-json-to-serialize-all-properties-strange-behavior-with/
class TestDTO
{
public T FirstProperty { get; set; }
[JsonIgnore] public bool FirstPropertySpecified { get; set; }
public U SecondProperty { get; set; }
[JsonIgnore] public bool SecondPropertySpecified { get; set; }
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var dto = serializer.Deserialize<TestDTO>(reader);
if (dto == null)
return null;
else if (dto.FirstPropertySpecified && !dto.SecondPropertySpecified)
return new Test<T, U>(dto.FirstProperty);
else if (!dto.FirstPropertySpecified && dto.SecondPropertySpecified)
return new Test<T, U>(dto.SecondProperty);
else
throw new InvalidOperationException(string.Format("Wrong number of properties specified for {0}", objectType));
}
public override bool CanConvert(Type objectType) => objectType == typeof(Test<T, U>);
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}
public class TestContractResolver : DefaultContractResolver
{
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
if (objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(Test<,>))
contract.Converter = (JsonConverter)Activator.CreateInstance(typeof(TestConverter<,>).MakeGenericType(objectType.GetGenericArguments()));
return contract;
}
}
Then use them e.g. as follows:
var json1 = @"{""FirstProperty"":""hello""}";
var json2 = @"{""SecondProperty"": 10101}";
IContractResolver resolver = new TestContractResolver(); // Cache this statically for best performance
var settings = new JsonSerializerSettings
{
ContractResolver = resolver,
};
var test1 = JsonConvert.DeserializeObject<Test<string, long>>(json1, settings);
Assert.AreEqual(test1.FirstProperty, "hello");
var test2 = JsonConvert.DeserializeObject<Test<string, long>>(json2, settings);
Assert.AreEqual(test2.SecondProperty, 10101L);
Notes:
The converter throws an
InvalidOperationException
if the JSON does not contain exactly one of the two properties.Feel free to modify this as per your requirements.
The converter does not implement serialization as your
Test<T, U>
type does not provide a method to track which property was initialized.The converter does not attempt to handle subclasses of
Test<T, U>
.
Demo fiddle here.
Json.NET: Serializing a Charting Series just returns type-name
Thanks to @dbc pointing me to the NoTypeConverterJsonConverter<T>
discussed here I was able to solve my issue.
I made a modified version of the NoTypeConverterJsonConverter<T>
which instead takes a list of types to apply NoType Conversion:
public class NoTypeConverterJsonConverter : JsonConverter
{
private NoTypeConverterContractResolver Resolver = new NoTypeConverterContractResolver();
/// <summary>
/// The types where the default typeconverter will not be used
/// </summary>
public List<Type> TypesToOverride
{
get { return Resolver.DefaultedTypes; }
set { Resolver.DefaultedTypes = value; }
}
private class NoTypeConverterContractResolver : DefaultContractResolver
{
/// <summary>
/// The types where the default typeconverter will not be used
/// </summary>
public List<Type> DefaultedTypes = new List<Type>();
protected override JsonContract CreateContract(Type objectType)
{
// if its in the listed types
if (DefaultedTypes.Any(t => t.IsAssignableFrom(objectType)))
{
// create a default contract
JsonObjectContract contract = base.CreateObjectContract(objectType);
// Null out the converter to not use the default typeconverter.
contract.Converter = null;
return contract;
}
// if it decends from a list
else if (typeof(IList<>).IsAssignableFrom(objectType))
{
// create a contract from the object, avoiding any presets(?)
JsonObjectContract contract = this.CreateObjectContract(typeof(IList<>));
//contract.Converter = null; // Also null out the converter to prevent infinite recursion.
return contract;
}
try
{
// use default contract creator
return base.CreateContract(objectType);
}
catch (Exception ex)
{
// see if it can be treated as a list
if (typeof(IList<>).IsAssignableFrom(objectType))
{
// create a contract from the object, avoiding any presets(?)
JsonObjectContract contract = this.CreateObjectContract(typeof(IList<>));
//contract.Converter = null; // Also null out the converter to prevent infinite recursion.
return contract;
}
throw ex;
}
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
return property;
}
}
/// <summary>
/// Default Constructor
/// </summary>
/// <param name="skippedTypes"></param>
public NoTypeConverterJsonConverter(List<Type> skippedTypes = null)
{
if (skippedTypes != null)
{
TypesToOverride = skippedTypes;
}
}
public override bool CanConvert(Type objectType)
{
return TypesToOverride.Any(t => t.IsAssignableFrom(objectType));
}
public override object ReadJson(
JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return JsonSerializer.CreateDefault(
new JsonSerializerSettings { ContractResolver = Resolver }
).Deserialize(reader, objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JsonSerializer.CreateDefault(
new JsonSerializerSettings { ContractResolver = Resolver }
).Serialize(writer, value);
}
}
I also found that Collections like DataPointCollection
tended to be treated poorly, only returning the count, so I added the IList part:
// see if it can be treated as a list
if (typeof(IList<>).IsAssignableFrom(objectType))
{
// create a contract from the object, avoiding any presets(?)
JsonObjectContract contract = this.CreateObjectContract(typeof(IList<>));
//contract.Converter = null; // Also null out the converter to prevent infinite recursion.
return contract;
}
The code is then used like so:
// The types to not use the default typeconverter
List<Type> skippedTypes = new List<Type>()
{
typeof(DataPoint),
typeof(DataPointCustomProperties),
typeof(SmartLabelStyle)
};
// create converter
NoTypeConverterJsonConverter converter = new NoTypeConverterJsonConverter(skippedTypes);
// use the converter to serialize a series
string seriesstr = JsonConvert.SerializeObject(series, Formatting.Indented,
new JsonSerializerSettings()
{
Converters = new List<JsonConverter> { converter }
});
// deserialise using the same converter
Series series2 = JsonConvert.DeserializeObject<Series>(seriesstr, new JsonSerializerSettings()
{
Converters = new List<JsonConverter> { converter }
});
Effectively, just add any type that's giving you trouble to that list and it usually sorts it out.
Related Topics
How to Get the Value of Private Field Using Reflection
Creating Directories in a Ziparchive C# .Net 4.5
Interesting "Params of Ref" Feature, Any Workarounds
Using Statement with Generics: Using Iset<> = System.Collections.Generic.Iset<>
Converting ASP.NET MVC Razor @Helper Function into a Method of a Helper Class
Uploading Files Not Working - Need Assistance
How to Implement a Rule Engine
How to Use Push Notifications in Xamarin Forms
What Is the Point of the Iserializable Interface
How to Include Other Files to the Output Directory in C# Upon Build
Converting String Format to Datetime in Mm/Dd/Yyyy
How to Implement Automatic Sorting of Datagridview
Does Foreach Evaluate the Array at Every Iteration
Generate 16-Bit Grayscale Bitmapdata and Save to File
How to Make Linq Execute a (Sql) Like Range Search
Why Does .Net Foreach Loop Throw Nullrefexception When Collection Is Null