3

Very simple example (I use this in my unit tests):

    private interface ISampleSubset
    {
        int id { get; }
    }

    private class Sample : ISampleSubset
    {
        public int id { get; set; }
        public string name { get; set; }
    }

Here's a small wrapper around NewtonSoft's JSON Serialize:

public string Serialize<T>(T t)
{
        using (var sw = new StringWriter())
        {
            using (var jw = new JsonTextWriter(sw))
            {
                var js = JsonSerializer.Create();
                js.Serialize(jw, t);
                jw.Flush();
            }
            return sw.GetStringBuilder().ToString();
        }
}

Now I want to serialize ISampleSubset: And call it like so:

ISampleSubSet t = new Sample()
{
    id = 1,
    name = "joe"
};
string json = Serialize(t);

I expect to get

{"id":1}

but instead I get

{"id":1,"name":"joe"}

I'm guessing js.Serialize is using reflection to 'see' the other properties on the object 'outside' of the interface. How do I limit it to just those properties on the interface?

n8wrl
  • 19,439
  • 4
  • 63
  • 103
  • Related question: https://stackoverflow.com/questions/17123821/serialize-only-interface-properties-to-json-with-json-net – Zac Faragher Nov 01 '18 at 01:05

2 Answers2

4

The serializer doesn't even know about your interface, so it's giving you everything - it accepts an object, so it doesn't know that you've declared your variable of type ISampleSubset - all it knows is the object itself is an instance of Sample.

Probably not the best solution, but you can use a JsonConverter to restrict the properties that appear in your serialized object.

This code is probably very inefficient - please don't judge - just threw it together, you can clean up the details and implement however you need:

public class MyConverter<T> : JsonConverter {
    private readonly string[] _propertyNames;
    public MyConverter() {
        _propertyNames = typeof(T).GetProperties().Select(p => p.Name).ToArray();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
        var objectType = value.GetType();
        var newObject = new Dictionary<string, object>();
        foreach (string propName in _propertyNames) {
            var prop = objectType.GetProperty(propName);
            if (prop != null) {
                newObject[propName] = prop.GetValue(value, null);
            }
        }
        string s = JsonConvert.SerializeObject(newObject);
        writer.WriteRaw(s);
    }

    public override bool CanConvert(Type objectType) {
        return true; // ?
    }

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

public static string Serialize<T>(T t) {
    return JsonConvert.SerializeObject(t, new MyConverter<T>());
}

Basically what it's doing is using reflection on the interface type to retrieve its properties, then creating a dictionary using only the properties found on the interface (you can do that a number of ways) - then using the simple JsonConvert class to serialize the dictionary.

Joe Enos
  • 39,478
  • 11
  • 80
  • 136
1

NewtonSoft.JSON is serializing the object instance you created based on the default rule (as of .NET 3.5 IIRC) that all properties of an object are by default serializable. It doesn't matter if you declared your variable as an interface type because it's probably doing the serialization by reflection.

If you want to restrict the properties that get serialized the best way is to use theNonSerialized attribute.

tucaz
  • 6,524
  • 6
  • 37
  • 60