How can I "un-JsonIgnore" an attribute in a derived class?
Asked Answered
A

6

16

I am using Newtonsoft's JsonSerializer to serialise some classes.

As I wanted to omit one field of my class in the serialisation process, I declared it as follow:

[JsonIgnore]
public int ParentId { get; set; }

This worked, but I am now facing a new problem : In a derived class, I would like this field to appear (and do so only in this specific derived class).

I have been looking through the documentation and on the Internet for a way to override this setting in child classes (I guess I need something like [JsonStopIgnore], but I couldn't find anything close).


  • Is there any way for me to force JsonSerializer to pick up again this attribute ?
  • Is it possible to explicitly mark an attribute as [JsonIgnore], but only in base class ?
Alcaeus answered 23/2, 2015 at 12:46 Comment(0)
T
14

You can do this by creating a custom DefaultContractResolver and overriding its CreateProperty method.

For example, given a Foo base and a derived Bar:

public class Foo
{
    [JsonIgnore]
    public string Name { get; set; }
    public int Age { get; set; }
}

public class Bar : Foo 
{ }

You can create the following contract resolver:

public class MyTypeContractResolver<T> : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member,
                                                   MemberSerialization
                                                       memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);

        property.Ignored = false;
        property.ShouldSerialize = propInstance => property.DeclaringType != typeof (T);
        return property;
    }
}

This will set all properties to Ignored = false, and then analyze them by the given predicate:

propInstance => property.DeclaringType != typeof (T);

Which in our case means "you should serialize only if they are not of type Foo" (since Foo is the DeclaryingType).

And then when you want to deserialize, you pass an instance of the contract resolver to JsonSerializerSettings:

var bar = new Bar();
var result = JsonConvert.SerializeObject(bar,
    new JsonSerializerSettings {ContractResolver = new MyTypeContractResolver<Bar>()});
Temperate answered 23/2, 2015 at 13:0 Comment(3)
Thank you for this explanation on ContractResolvers ! If I can't find a lighter solution, I will go this way which at least allows torough customization.Alcaeus
@Alcaeus Sure thing. This is definitely "the heaviest" solution, but i assume it is also the "proper" way of handling it with Json.NET.Temperate
And I am thankful that I now have a reference to the "proper" way, should I find myself unable to use a "lazy" one :PAlcaeus
H
18

The only way to "override" the behavior of the [JsonIgnore] attribute is to use a contract resolver, as @Yuval Itzchakov nicely explained in his answer.

However, there is another possible solution that might work for you: instead of using a [JsonIgnore] attribute, you could implement a ShouldSerializeParentId() method in your classes to control whether the ParentId property gets serialized. In the base class, make this method return false; then, override the method in the derived class to return true. (This feature is known as conditional property serialization in Json.Net.)

public class Base
{
    public int Id { get; set; }
    public int ParentId { get; set; }

    public virtual bool ShouldSerializeParentId()
    {
        return false;
    }
}

public class Derived : Base 
{ 
    public override bool ShouldSerializeParentId()
    {
        return true;
    }
}

Fiddle: https://dotnetfiddle.net/65sCSz

Histidine answered 23/2, 2015 at 18:24 Comment(2)
Thank you for this explanation of a mechanism I knew nothing about! Although a bit heavy, this may well be a simpler solution than using a custom contract resolver to achieve the same result :)Alcaeus
+1 I found this to be a great solution for a limited set of customized behaviors. If more generic approach is needed, I would probably go with the ContractResolver way.Populous
T
14

You can do this by creating a custom DefaultContractResolver and overriding its CreateProperty method.

For example, given a Foo base and a derived Bar:

public class Foo
{
    [JsonIgnore]
    public string Name { get; set; }
    public int Age { get; set; }
}

public class Bar : Foo 
{ }

You can create the following contract resolver:

public class MyTypeContractResolver<T> : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member,
                                                   MemberSerialization
                                                       memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);

        property.Ignored = false;
        property.ShouldSerialize = propInstance => property.DeclaringType != typeof (T);
        return property;
    }
}

This will set all properties to Ignored = false, and then analyze them by the given predicate:

propInstance => property.DeclaringType != typeof (T);

Which in our case means "you should serialize only if they are not of type Foo" (since Foo is the DeclaryingType).

And then when you want to deserialize, you pass an instance of the contract resolver to JsonSerializerSettings:

var bar = new Bar();
var result = JsonConvert.SerializeObject(bar,
    new JsonSerializerSettings {ContractResolver = new MyTypeContractResolver<Bar>()});
Temperate answered 23/2, 2015 at 13:0 Comment(3)
Thank you for this explanation on ContractResolvers ! If I can't find a lighter solution, I will go this way which at least allows torough customization.Alcaeus
@Alcaeus Sure thing. This is definitely "the heaviest" solution, but i assume it is also the "proper" way of handling it with Json.NET.Temperate
And I am thankful that I now have a reference to the "proper" way, should I find myself unable to use a "lazy" one :PAlcaeus
E
7

I solved the same problem by using the new keyword on the property of the derived class.

public class Foo
{
    [JsonIgnore]
    public int ParentId { get; set; }
}

public class Bar: Foo
{
    [JsonProperty("ParentId")]
    public new int ParentId { get; set; }
}
Eccles answered 18/3, 2016 at 16:31 Comment(3)
I have no idea of why this works, but it does. I'm using mongoDb to save objects afterwards, and that throws out mapping errors at me now...Perrine
It is working for me. But I can't understand why JsonProperty is not working when use virtual & overrideArachne
The new modifier tells the compiler to replace the property and not inherit it from the base class. As it now does not inherit JsonIgnore anymore it will be serialized.Eccles
P
6

You can probably simply overwrite ParentId in the derived class.

public new int ParentId
{
    get { return base.ParentId; }
    set { base.ParentId = value; }
}
Photogrammetry answered 23/2, 2015 at 12:52 Comment(2)
but be aware, that any cast to the underlying class will give access to the hidden property!Aba
@AndreasNiedermair Yes. But I think that both during serialization and deserialization with Newtonsoft JSonSerializer it should work. For the remaining code it's ok if this property is bypassed.Photogrammetry
C
1

I solved the same problem with a ghost property :

public class Foo
{
    [JsonIgnore]
    public int ParentId { get; set; }

    [NotMapped]
    public int FooParent { get; set; }
}

When I want to show this property quite always hidden, I populate it, other times it is null :

Foos.ForEach(x => x.FooParent = ParentId);
Christabelle answered 12/4, 2017 at 13:44 Comment(0)
W
0

Another approach is to define a backing member variable, then serialize the backing variable:

public class Foo
{
    [JsonIgnore]
    public int ParentId { get; set; }
}

public class Bar: Foo
{
    [JsonProperty("_parentId")]
    private int _parentId;

    public int ParentId 
    {
       get
       {
         return _parentId;
       }
       set
       {
         _parentId = value;
       }
    }
}
Woodnote answered 3/7 at 3:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.