Reputation: 305
I am tiring to serialize a fairly large list of entities, that are all derived from a base class. I only need the base class properties in the client. How do I achieve this without instantiating a new instance of the base class?
I have tried creating a custom ContractResolver, but it seems that it does a getType() at runtime, instead of using the Type of the list/Array being serialized
See code sample below.
I want to achieve. castBaseString == actualBaseString ;
So I want castBaseString to = [{"Id":1},{"Id":2}]
not [{"Value":"value","Id":1},{"Value":"value2","Id":2}]
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Tests {
[TestClass]
public class JsonNetTest {
class Base {
public int Id { get; set; }
}
class Derived : Base {
public string Value { get; set; }
}
class OtherDerived : Base {
public string Value { get; set; }
public string OtherValue { get; set; }
}
[TestMethod]
public void Test() {
IEnumerable<Derived> deriveds = new Derived[] {
new Derived {Id = 1, Value = "value" },
new Derived {Id = 2, Value = "value2" }
};
IEnumerable<Base> castBases = deriveds.Cast<Base>().ToList();
IEnumerable<Base> bases = new Base[] {
new Base {Id = 1 },
new Base {Id = 2 }
};
JsonSerializerSettings s = new JsonSerializerSettings();
var derString = JsonConvert.SerializeObject(deriveds, s);
var castBaseString = JsonConvert.SerializeObject(castBases, s);
var actualBaseString = JsonConvert.SerializeObject(bases, s);
Assert.AreEqual(actualBaseString, castBaseString);
Assert.AreNotEqual(castBaseString, derString);
}
}
}
EDIT BASED ON COMMENTS
Additional Context:
I just posted this simple test case for clarity.
the actual context this is being used is in an aspnet core application.
Consider there are 3 controllers
when a client calls 1, we want to return a list of Derived, when a client calls 2 we want to return a list of OtherDerived when they call 3, we want to return a list of Base
The data is stored in 2 different tables in the database TBL_DERIVED and TBL_OTHERDERIVED.
What we want to achieve when they call base is to return data from One or both of these tables, but just the common properties of these tables.
Hope this clarifies.
Upvotes: 1
Views: 1269
Reputation: 305
Turns out this is not possible the way the json serialization works. @stuartd answer might be a good workaround if you only have limited cases you want to do this for.
Upvotes: 0
Reputation: 73243
If you don't want to use attributes, you can use a ContractResolver to force only properties from Base
to be serialized:
public class DerivedTypeFilterContractResolver<T> : DefaultContractResolver {
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.DeclaringType != typeof(T)) {
property.ShouldSerialize = instance => false;
}
return property;
}
}
Then use it like this:
void Main() {
IEnumerable<Derived> deriveds = new Derived[] {
new Derived {Id = 1, Value = "value" },
new Derived {Id = 2, Value = "value2" }
};
IEnumerable<Base> castBases = deriveds.Cast<Base>().ToList();
IEnumerable<Base> bases = new Base[] {
new Base {Id = 1 },
new Base {Id = 2 }
};
JsonSerializerSettings s = new JsonSerializerSettings {
ContractResolver = new DerivedTypeFilterContractResolver<Base>()
};
var derString = JsonConvert.SerializeObject(deriveds, s);
var castBaseString = JsonConvert.SerializeObject(castBases, s);
var actualBaseString = JsonConvert.SerializeObject(bases, s);
Console.WriteLine(castBaseString);
}
class Base {
public int Id { get; set; }
}
class Derived : Base {
public string Value { get; set; }
}
Output:
[{"Id":1},{"Id":2}]
Upvotes: 2
Reputation: 4513
Add [JsonIgnore]
top of property.
class Derived : Base {
[JsonIgnore]
public string Value { get; set; }
}
Upvotes: 0