1

I'm using the Newtonsoft.JSON library to serialize several objects. In some cases I don't want to serialize a property so I've used the ShouldSerialize prefix which has been largely successful in most cases. In one case I only want to serialize a property if it belongs to a specific class.

I've tried using the stack trace but it only tells me that the JSON object is calling the ShouldSerialize method. I don't need to know what calls ShouldSerialize, I need to know what parent class ShouldSerialize belongs to such as Parent.Child.ShouldSerialize.

How can I determine what the parent class name is while using the JSON object using the code sample below?

class Foo
{
    public SharedClass SomeProperty
    {
           get;
           set;
    }
}

class Bar
{
    public SharedClass SomeProperty
    {
           get;
           set;
    }
}

class SharedClass
{
    public string SomeValue
    {
           get;
           set;
    }

    public bool ShouldSerializeSomeValue
    {
           //pseudo logic
           return ClassName == "Foo";
    }
}
Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
blkbam
  • 548
  • 1
  • 5
  • 10
  • could you pass that information in with a constructor argument? – Daniel A. White Jan 07 '15 at 14:30
  • 2
    why is SharedClass a string in your example? Very confusing... – DrKoch Jan 07 '15 at 14:31
  • Isn't there an attribute you can use to stop JSON.NET from serializing a property? Maybe `[NonSerialized]` works. – Dirk Jan 07 '15 at 14:31
  • you could also use inheritance. – Daniel A. White Jan 07 '15 at 14:31
  • @Dirk and if not, you should create a custom attribute and not prefixes. – Jevgeni Geurtsen Jan 07 '15 at 14:31
  • Unless you specifically add a reference to the owning class to `SharedClass`, there is no way it can know where its references are. You will need to find a different way to accomplish what you want. For instance, you could have 2 classes, one of which inherits from the other and adds the extra property. – Lasse V. Karlsen Jan 07 '15 at 14:34
  • Your question and code is incredibly confusing, but in JSON.NET to stop a public from being serialised you you the `JsonIgnore` attribute. – Michael Coxon Jan 07 '15 at 14:35
  • @Dirk It's just an example as I note in the comment above the line. – blkbam Jan 07 '15 at 15:37
  • The point is to dynamically determine when to serialize the property based on what parent it has. The hardcoded attributes won't work in this case because they can't be dynamic. – blkbam Jan 07 '15 at 15:43

1 Answers1

1

As was pointed out by Lasse Karlsen in the comments, if your SharedClass does not have a reference to its parent, there is no way for the ShouldSerializeSomeValue() method in that class to know what the parent class is.

However, if you are using Json.Net 6.0 Release 6 or later, you can work around this by using a custom JsonConverter as a means to selectively omit properties from the shared class (instead of using a ShouldSerialize() method), and then place [JsonConverter] attributes on the SharedClass properties within the appropriate parent classes to indicate which properties should be omitted for that instance.

Here is how the updated example class definitions might look. You'll notice I've marked the SharedClass instance on Foo to indicate it should use a custom converter called OmitPropertiesConverter to omit the SomeValue property. The SharedClass instance on Bar does not use a converter, so that instance will be serialized as normal.

class Foo
{
    [JsonConverter(typeof(OmitPropertiesConverter), "SomeValue")]
    public SharedClass Shared { get; set; }
}

class Bar
{
    public SharedClass Shared { get; set; }
}

class SharedClass
{
    public string SomeValue { get; set; }
    public string SomeOtherValue { get; set; }
}

Below is the code for the OmitPropertiesConverter. Its constructor accepts a propsToOmit string which is a comma-delimited list of property names to be excluded from the serialization. This gets split into an array for later use in the WriteJson method. The WriteJson method takes the SharedClass value, converts it to a JObject, then programmatically removes the properties which are in the propsToOmit array before writing the JObject to the JsonWriter.

class OmitPropertiesConverter : JsonConverter
{
    string[] propsToOmit;

    public OmitPropertiesConverter(string propsToOmit)
    {
        this.propsToOmit = propsToOmit.Split(new char[] {','},
                                             StringSplitOptions.RemoveEmptyEntries);
    }

    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(SharedClass));
    }

    public override void WriteJson(JsonWriter writer, object value, 
                                   JsonSerializer serializer)
    {
        JObject jo = JObject.FromObject(value, serializer);

        // Note: ToList() is needed here to prevent "collection was modified" error
        foreach (JProperty prop in jo.Properties()
                                     .Where(p => propsToOmit.Contains(p.Name))
                                     .ToList())
        {
            prop.Remove();
        }

        jo.WriteTo(writer);
    }

    public override bool CanRead
    {
        get { return false; }
    }

    public override object ReadJson(JsonReader reader, Type objectType,
                                    object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Here is a simple demo program which shows the converter in action:

class Program
{
    static void Main(string[] args)
    {
        var root = new
        {
            Foo = new Foo
            {
                Shared = new SharedClass
                {
                    SomeValue = "foo1",
                    SomeOtherValue = "foo2"
                }
            },
            Bar = new Bar
            {
                Shared = new SharedClass
                {
                    SomeValue = "bar1",
                    SomeOtherValue = "bar2"
                }
            }
        };

        string json = JsonConvert.SerializeObject(root, Formatting.Indented);
        Console.WriteLine(json);
    }
}

And here is the output of the above demo. You'll notice that the SomeValue property on the SharedClass instance inside Foo is not included in the output, but it is included on the instance inside Bar.

{
  "Foo": {
    "Shared": {
      "SomeOtherValue": "foo2"
    }
  },
  "Bar": {
    "Shared": {
      "SomeValue": "bar1",
      "SomeOtherValue": "bar2"
    }
  }
}
Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
  • Yes, that's exactly what I'm looking for. Unfortunately I'm limited to using the Newtonsoft.Json library. – blkbam Jan 07 '15 at 21:30
  • Json.Net and Newtonsoft.Json are the same library. So if you are already using this library, then you should be able to use this solution. Give it a try. – Brian Rogers Jan 08 '15 at 18:44
  • Sorry for the delay, had a different task that needed tending to. Initially I though you were just showing a sample makeup of OmitPropertiesConverter on Json.Net. Reading through I get it now. I tried you code and it does exactly what I need. thanks! – blkbam Jan 09 '15 at 16:41
  • No worries; glad I was able to help. – Brian Rogers Jan 09 '15 at 16:46