Ignore [JsonIgnore] Attribute on Serialization / Deserialization [duplicate] - c#

This question already has answers here:
Can I optionally turn off the JsonIgnore attribute at runtime?
(3 answers)
Closed 6 years ago.
Is there a way I can ignore Json.NET's [JsonIgnore] attribute on a class that I don't have permission to modify/extend?
public sealed class CannotModify
{
public int Keep { get; set; }
// I want to ignore this attribute (and acknowledge the property)
[JsonIgnore]
public int Ignore { get; set; }
}
I need all properties in this class to be serialized/deserialized. I've tried subclassing Json.NET's DefaultContractResolver class and overriding what looks to be the relevant method:
public class JsonIgnoreAttributeIgnorerContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
// Serialize all the properties
property.ShouldSerialize = _ => true;
return property;
}
}
but the attribute on the original class seems to always win:
public static void Serialize()
{
string serialized = JsonConvert.SerializeObject(
new CannotModify { Keep = 1, Ignore = 2 },
new JsonSerializerSettings { ContractResolver = new JsonIgnoreAttributeIgnorerContractResolver() });
// Actual: {"Keep":1}
// Desired: {"Keep":1,"Ignore":2}
}
I dug deeper, and found an interface called IAttributeProvider that can be set (it had a value of "Ignore" for the Ignore property, so that was a clue this might be something that needs changing):
...
property.ShouldSerialize = _ => true;
property.AttributeProvider = new IgnoreAllAttributesProvider();
...
public class IgnoreAllAttributesProvider : IAttributeProvider
{
public IList<Attribute> GetAttributes(bool inherit)
{
throw new NotImplementedException();
}
public IList<Attribute> GetAttributes(Type attributeType, bool inherit)
{
throw new NotImplementedException();
}
}
But the code isn't ever hit.

You were on the right track, you only missed the property.Ignored serialization option.
Change your contract to the following
public class JsonIgnoreAttributeIgnorerContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
property.Ignored = false; // Here is the magic
return property;
}
}

Related

C# reflection - get object value from property value within JSON ContractResolver

I have a class PersonDto which contains a property instance of type AddressDto. I am building a custom ContractResolver named eg. ShouldSerializeContractResolver with Newtonsoft.Json marshalling .NET lib that will include only specific properties into serialization that are marked with my custom attribute eg. [ShouldSerialize]
The problem occurs when the CreateProperty method of the resolver goes into the complex / custom type of the PersonDto ie. it goes into the AddressDto and it is not aware that the property instance is tagged with the [ShouldSerialize] attribute. The resulting serialization then looks like "Address": {} instead of "Address": { "StreetNumber": 123 }
The code looks like:
class AddressDto
{
// PROBLEM 1/2: value does not get serialized, but I want it serialized as its property is [ShouldSerialize] attr tagged
public int StreetNumber { get; set; }
}
class PersonDto
{
public string Name { get; set; } // should not serialize as has not attr on it
[ShouldSerialize]
public string Id { get; set; }
[ShouldSerialize]
public AddressDto Address { get; set; }
}
// JSON contract resolver:
public class ShouldSerializeContractResolver: DefaultContractResolver
{
public ShouldSerializeContractResolver() { }
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
var attr = member.GetCustomAttribute<ShouldSerializeContractResolver>(inherit: false);
// PROBLEM 2/2: here I need the code to access the member.DeclaringType instance somehow and then
// find its AddressDto property and its GetCustomAttribute<ShouldSerializeContractResolver>
if (attr is null)
{
property.ShouldSerialize = instance => { return false; };
}
return property;
}
}
// code invoked as:
PersonDto somePerson = IrrelevantSomePersonCreateNewFactoryFn();
var jsonSettings = new JsonSerializerSettings { ContractResolver = new ShouldSerializeContractResolver() };
var strJson = JsonConvert.SerializeObject(somePerson, jsonSettings);
The serializer works in a "flat" mode, ie. it runs through all the props with the resolver and it comes to the point where the member is StreetNumber and from it I do not know how to access the "parent" MemberInfo, which would be great.
What I find as the core issue here is I do not have the "parent" / DeclaringType object instance and need to find a way on how to obtain it.
Please note that I can not solve this issue through [JsonProperty], [JsonIgnore] etc. as my attribute is complex and involves its own logic.
You would like AddressDto to be serialized differently depending upon whether it was encountered via a property marked with [ShouldSerialize], however that cannot easily be done using a custom contract resolver because Json.NET creates exactly one contract for each type no matter where it is encountered in the serialization graph. I.e. a contract resolver will generate the same contract for AddressDto for both of the following data models:
class PersonDto
{
public string Name { get; set; } // should not serialize as has not attr on it
[ShouldSerialize]
public string Id { get; set; }
[ShouldSerialize]
public AddressDto Address { get; set; } // This and its properties should get serialized.
}
class SomeOtherDto
{
[ShouldSerialize]
public string SomeOtherValue { get; set; }
public AddressDto SecretAddress { get; set; } // Should not get serialized.
}
This is why you cannot get the referring property's attributes when creating the properties for a referenced type.
Instead, you will need to track in runtime when the serializer begins and ends serialization of a [ShouldSerialize] property, setting some thread-safe state variable while inside. This can be done e.g. by using your contract resolver to inject a custom JsonConverter that sets the necessary state, disables itself temporarily to prevent recursive calls, then does a default serialization:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class ShouldSerializeAttribute : System.Attribute
{
}
public class ShouldSerializeContractResolver: DefaultContractResolver
{
static ThreadLocal<bool> inShouldSerialize = new (() => false);
static bool InShouldSerialize { get => inShouldSerialize.Value; set => inShouldSerialize.Value = value; }
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
var attr = member.GetCustomAttribute<ShouldSerializeAttribute>(inherit: false);
if (attr is null)
{
var old = property.ShouldSerialize;
property.ShouldSerialize = instance => InShouldSerialize && (old == null || old(instance));
}
else
{
var old = property.Converter;
if (old == null)
property.Converter = new InShouldSerializeConverter();
else
property.Converter = new InShouldSerializeConverterDecorator(old);
}
return property;
}
class InShouldSerializeConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var old = InShouldSerialize;
try
{
InShouldSerialize = true;
serializer.Serialize(writer, value);
}
finally
{
InShouldSerialize = old;
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => throw new NotImplementedException();
public override bool CanRead => false;
public override bool CanConvert(Type objectType) => throw new NotImplementedException();
}
class InShouldSerializeConverterDecorator : JsonConverter
{
readonly JsonConverter innerConverter;
public InShouldSerializeConverterDecorator(JsonConverter innerConverter) => this.innerConverter = innerConverter ?? throw new ArgumentNullException(nameof(innerConverter));
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var old = InShouldSerialize;
try
{
InShouldSerialize = true;
if (innerConverter.CanWrite)
innerConverter.WriteJson(writer, value, serializer);
else
serializer.Serialize(writer, value);
}
finally
{
InShouldSerialize = old;
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var old = InShouldSerialize;
try
{
InShouldSerialize = true;
if (innerConverter.CanRead)
return innerConverter.ReadJson(reader, objectType, existingValue, serializer);
else
return serializer.Deserialize(reader, objectType);
}
finally
{
InShouldSerialize = old;
}
}
public override bool CanConvert(Type objectType) => throw new NotImplementedException();
}
}
Then serialize as follows:
IContractResolver resolver = new ShouldSerializeContractResolver(); // Cache statically & reuse for best performance
var settings = new JsonSerializerSettings
{
ContractResolver = resolver,
};
var json = JsonConvert.SerializeObject(person, Formatting.Indented, settings);
Notes:
Newtonsoft recommends caching and reusing your contract resolver for best performance.
The above implementation has the limitation that, if your [ShouldSerialize] is also marked with JsonPropertyAttribute, fields that control serialization of the property value such as ItemConverterType and IsReference will be ignored.
Demo fiddle here.

Removing User Object Property

I am trying to remove a property within a Microsoft.graph User object.
I am currently using the "Property.Remove()" method. First, I am setting the User object to a deserialized JObject.
The way I am doing this is serializing the User object into a Json string (jsonString) and deserializing jsonString (jsonStringUpdated). Afterwards, I am calling jsonStringUpdated.property("____") .Remove();
Is there a simpler way to do this? Below is an example:
{
string jsonString = JsonConvert.SerializeObject(user);
var jsonStringUpdated = (JObject)JsonConvert.DeserializeObject(jsonString);
jsonStringUpdated.Property("country").Remove();
}
I would not say that this is simpler option, but I would say that it is better one (in terms of reusability, flexibility and possibly performance). You can create custom contract resolver:
// generic type parameter can be omitted with hardcoding typeof
// handling for multiple properties can be added
public class IgnorePropertyResolver<T> : DefaultContractResolver
{
private readonly string _propName;
public IgnorePropertyResolver(string propName)
{
_propName = propName;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (typeof(T).IsAssignableFrom(member.DeclaringType) && member.Name == _propName)
{
property.ShouldSerialize = o => false;
}
return property;
}
}
And usage:
class MyClass
{
public int Prop1 { get; set; }
public int Prop2 { get; set; }
}
var serializeObject = JsonConvert.SerializeObject(new MyClass(),
new JsonSerializerSettings
{
ContractResolver = new IgnorePropertyResolver<MyClass>(nameof(MyClass.Prop2))
});
Console.WriteLine(serializeObject); // prints {"Prop1":0}

How can I solve "Required property not found in json" error without changing the json and its corresponding class?

A)
I have a DLL library include a class like the bellow code:
public class Thing
{
// OTHER_PROPERTIES
//..........................
[JsonProperty(Required = Required.Always)]
public bool IsBook { get; set; }
}
Assumption: I have not access to the source of the Thing class.
B)
And I have a JSON like this:
{
OTHER_PROPERTIES
}
Note: It has not a pair of name-value for IsBook
Assumption: I wont change the JSON to include IsBook in it.
C)
If I run JsonConvert.DeserializeObject<Thing>(json); then I get the following error:
Error: Required property not found in json ....
Question:
How can I write a custom code to solve the above error by observing the assumptions of the problem?
(please read the above assumptions again before writing your answer.)
You can use custom contract resolver to achieve that. For example:
public class Thing
{
[JsonProperty("Name")]
public string Name { get; set; }
[JsonProperty(Required = Required.Always)]
public bool IsBook { get; set; }
}
class NeverRequiredContractResolver : DefaultContractResolver {
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
var prop = base.CreateProperty(member, memberSerialization);
prop.Required = Required.Default;
return prop;
}
}
And then:
var test = JsonConvert.DeserializeObject<Thing>("{\"Name\":\"some name\"}",
new JsonSerializerSettings {
ContractResolver = new NeverRequiredContractResolver()
});
Of course you can assign resolver globally and not pass it to every DeserializeObject:
JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
{
ContractResolver = new NeverRequiredContractResolver()
};
var test = JsonConvert.DeserializeObject<Thing>("{\"Name\":\"some name\"}");
you can make only specific properties of specific classes non-required (and not all of them like in example above):
class CustomPropertyContractResolver : DefaultContractResolver {
private readonly Action<MemberInfo, JsonProperty> _propFixup;
public CustomPropertyContractResolver(Action<MemberInfo, JsonProperty> propFixup) {
_propFixup = propFixup;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
var prop = base.CreateProperty(member, memberSerialization);
_propFixup?.Invoke(member , prop);
return prop;
}
}
JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
{
ContractResolver = new CustomPropertyContractResolver((member, prop) => {
if (member.DeclaringType == typeof(Thing) && member.Name == "IsBook") {
prop.Required = Required.Default;
}
})
};
var test = JsonConvert.DeserializeObject<Thing>("{\"Name\":\"some name\"}");
and overall adjust example above however you wish for your specific use case.

Newtonsoft.Json: AllowNull bug?

Im trying to write serializer which will serialize/deserialize to exact same class structure. So, even if default value of property is not provided - it should throw error. I thought what I accomplished it by setting member handling and contract resolver, but It wont work. Exceptions are not thrown where it should by all means (I explicitly specified it in contract resolver).
And here is my serializer:
public sealed class JsonSerializer : ISerializer
{
private readonly JsonSerializerSettings _settings = new JsonSerializerSettings
{
ContractResolver = new RequireObjectPropertiesContractResolver(),//Everything required. Null are allowed.
MissingMemberHandling = MissingMemberHandling.Error, //If something missing in target class - it should throw error.
Formatting = Formatting.Indented
};
public object Deserialize(string input, Type type)
{
return JsonConvert.DeserializeObject(input, type, _settings);
}
public string Serialize(object input)
{
return JsonConvert.SerializeObject(input, _settings);
}
private class RequireObjectPropertiesContractResolver : DefaultContractResolver
{
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
contract.ItemRequired = Required.AllowNull;
return contract;
}
}
}
And here is test case which I can't get working:
[DataContract]
private class TestInput
{
[DataMember]
public string Value0 { get; set; }
[DataMember]
public string Value1 { get; set; }
[DataMember]
public Inner Inner { get; set; }
}
[DataContract]
private class Inner
{
[DataMember]
public string NewValue0 { get; set; }
}
[TestMethod]
public void TestSerialization()
{
using (var kernel = new StandardKernel(new MyModule()))
{
var serializer = kernel.Get<ISerializer>();
//It should throw error because Inner not provided as null (not thrown)
string json = "{\"Value0\":\"test0\", \"Value1\":\"test1\"}";
Extensions.Throws<Exception> (()=>serializer.Deserialize(json, typeof(TestInput)));
//It should throw error because nothing were provided as null (not thrown)
json = "{}";
Extensions.Throws<Exception>(() => serializer.Deserialize(json, typeof(TestInput)));
//this one is correct one (no problems here)
json = "{\"Value0\":\"test0\", \"Value1\":\"test1\", \"Inner\":null}";
Extensions.NotThrows(()=>serializer.Deserialize(json, typeof(TestInput)));
}
}
These two tests just won't pass.
After some time I managed to make it work. DataContract applied to my classes were in the way so I just reassigned values they are setting:
private class RequireObjectPropertiesContractResolver : DefaultContractResolver
{
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
contract.ItemRequired = Required.AllowNull;
return contract;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var properties = base.CreateProperties(type, memberSerialization);
foreach (var p in properties)
{
p.Required = Required.AllowNull;
}
return properties;
}
}
Just setted Required.AllowNull on all properties, instead of contract itself.

Refactor of ShouldSerialize () in class... can I use IContractResolver?

I have an API that returns a big list of car features.... all are either bool or ints... and basically I only want to display the ones that return true values or >0 for the ints.
I am using JSON.net so that I san use the ShouldSerialize() property to determine if I should serialize the property based upon its value and my code looks like this:
public class Features
{
public bool ABS { get; set; }
public bool ShouldSerializeABS()
{
// don't serialize the ABS property if ABS is false
return (ABS != false);
}
public bool Immobiliser { get; set; }
public bool ShouldSerializeImmobiliser ()
{
// don't serialize the Immobiliser property if Immobiliser is false
return (Immobiliser != false);
}
public int BHP { get; set; }
public bool ShouldSerializeBHP ()
{
// don't serialize the BHP property if BHP is false
return (BHP != 0);
}
//..... etc
}
This works great and gives me the results I am after, however I was just wondering if there is a way to re-factor this so that my class does not become cluttered with all the ShouldSerialize() properties?
I have been looking into CopyConditional properties with IContractResolver on http://james.newtonking.com/projects/json/help/index.html?topic=html/ConditionalProperties.htm and looks like it might be possible to use IContractResolver for such a purpose, but I still seem to end up with lots of code that does not seem to re-factor out
public class ShouldSerializeContractResolver : DefaultContractResolver
{
public new static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.DeclaringType == typeof(Features) && property.PropertyName == "ABS")
{
property.ShouldSerialize =
instance =>
{
Features e = (Features)instance;
return e.ABS != false;
};
}
if (property.DeclaringType == typeof(Features) && property.PropertyName == "Immobiliser")
{
property.ShouldSerialize =
instance =>
{
Features e = (Features)instance;
return e.Immobiliser != false;
};
}
return property;
}
}
and this method using the ShouldSerializeContractResolver does not seem to remove the property from the class if it is false... any help is greatly appreciated
It sounds like what you are trying to accomplish by writing all these ShouldSerialize() methods can be accomplished by just changing the DefaultValueHandling setting on the serializer to Ignore. This will cause any values that are equal to their default values (false for bool, 0 for int) not to be serialized.
JsonSerializerSettings jsonSettings = new JsonSerializerSettings();
jsonSettings.DefaultValueHandling = DefaultValueHandling.Ignore;
string json = JsonConvert.SerializeObject(yourObject, jsonSettings);
If you're using Web API, then you can access the settings of the Json.NET serializer via the Register method of the WebApiConfig class (in the App_Start folder).
JsonSerializerSettings settings = config.Formatters.JsonFormatter.SerializerSettings;
settings.DefaultValueHandling = DefaultValueHandling.Ignore;

Categories