15

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 ?
PLNech
  • 3,087
  • 1
  • 23
  • 52

5 Answers5

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

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
  • 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 :) – PLNech Feb 24 '15 at 09:31
  • +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. – Rami Alshareef Jul 30 '18 at 15:33
13

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>()});
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • 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. – PLNech Feb 23 '15 at 13:37
  • 1
    @PLNech Sure thing. This is definitely "the heaviest" solution, but i assume it is also the "proper" way of handling it with Json.NET. – Yuval Itzchakov Feb 23 '15 at 13:39
  • 1
    And I am thankful that I now have a reference to the "proper" way, should I find myself unable to use a "lazy" one :P – PLNech Feb 23 '15 at 13:40
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; }
}
John Caprez
  • 393
  • 2
  • 10
  • 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... – Christopher Bonitz Nov 16 '16 at 11:11
  • It is working for me. But I can't understand why JsonProperty is not working when use virtual & override – Ramil Aliyev 007 May 24 '21 at 14:45
  • 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. – John Caprez Jun 22 '21 at 11:50
6

You can probably simply overwrite ParentId in the derived class.

public new int ParentId
{
    get { return base.ParentId; }
    set { base.ParentId = value; }
}
xanatos
  • 109,618
  • 12
  • 197
  • 280
  • 1
    but be aware, that any cast to the underlying class will give access to the hidden property! –  Feb 23 '15 at 13:02
  • 1
    @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. – xanatos Feb 23 '15 at 13:05
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);
iguypouf
  • 770
  • 4
  • 15