C# Serialize inherited class without base properties - c#

I have a base class with some properties like:
public class BaseClass {
public string Name { get; set; }
public string Comment { get; set; }
}
And classes who extend the base class like:
public class A : BaseClass {
public string AProperty{ get; set; }
}
and
public class B : BaseClass {
public string BProperty{ get; set; }
}
Now I need to be able to serialize the data from the extension and from the base class into different Locations.
And for deserialization I need to be able to deserialize one of the classes and "merge" with base class who was deserialized earlier.
The baseclass is mine so I can change it as needet, but not the extension classes (A and B in my sample).
It would be nice to have a solution that works with XML and JSON (Newtonsoft JSON serializer) serializer.
Thanks for ideas.

I do not know of a way to do this with XML, but with Newtonsoft JSON.NET:
You cannot merge the deserialized result unless you know the final target type first. This probably means you will have to include the type name in your JSON in some way either automatically via TypeNameHandling or manually.
If you chose to use TypeNameHandling, you can create the object by calling Deserialize(JsonReader) and let it create an object of the proper type.
If you stored the type in some other way, you can create the object by using the overload which takes a type: Deserialize(JsonReader, Type)
Once you have the correct object, you can then add the additional properties from the second fragment via Populate.
Note: If you don't know the type of the object until the second fragment, why not just keep the first fragment as JSON and parse the second fragment first? Then add the first fragment via Populate. It really doesn't matter in which order the fragments are deserialized as long as they don't have any overlapping fields.
To perform the serialization, you will need to use a custom IContractResolver to filter the fields. For example, something like this would allow filtering to all properties that are declared in a particular type (Warning: I did not attempt to compile this):
public class DeclaredMembersResolver<T> : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> filtered = new List<JsonProperty>();
foreach (JsonProperty p in base.CreateProperties(type, memberSerialization))
if(p.DeclaringType == typeof(T))
filtered.Add(p);
return filtered;
}
}
// Example of use:
var ser = JsonSerializer.CreateDefault(new JsonSerializerSettings() {
ContractResolver = new DeclaredMembersResolver<BaseClass>()
});
ser.Serialize(writer, obj); // Only the base properties of obj will be written
Obviously you would then extend the above to do the inverse (exclude base class properties) to serialize your second JSON block.

What you want to do can be done with Newtonsoft's JSON library with a bit of work by using JObject and Linq to query for properties of the deserialized object and manually doing what you want.
Further info can be found here: http://james.newtonking.com/json/help/index.html
As an example: If you have a baseObject that has been already deserialized and a string containing the data:
JObject jObj = JObject.Parse(json);
A aObj = new A();
aObj.Name = baseObject.Name;
aObj.Comment = baseObject.Name;
a.AProperty = (string)jObj[propertyInfo.Name];
If you do not know the additional properties of objects, but have access to the types at runtime that can easily be found through reflection. Example continuing from above
var t = typeof(A);
PropertyInfo[] pi = t.GetProperties();
foreach (var propertyInfo in pi)
propertyInfo.SetValue(aObject, (string)jObj["AProperty"]);

Related

Why does Asp WebApi HttpGet lose derived class properties? What's up with polymorphism? [duplicate]

The JSON response from my ASP.NET Core 3.1 API controller is missing properties. This happens when a property uses a derived type; any properties defined in the derived type but not in the base/interface will not be serialized to JSON. It seems there is some lack of support for polymorphism in the response, as if serialization is based on a property's defined type instead of its runtime type. How can I change this behavior to ensure that all public properties are included in the JSON response?
Example:
My .NET Core Web API Controller returns this object that has a property with an interface type.
// controller returns this object
public class Result
{
public IResultProperty ResultProperty { get; set; } // property uses an interface type
}
public interface IResultProperty
{ }
Here is a derived type that defines a new public property named Value.
public class StringResultProperty : IResultProperty
{
public string Value { get; set; }
}
If I return the derived type from my controller like this:
return new MainResult {
ResultProperty = new StringResultProperty { Value = "Hi there!" }
};
then the actual response includes an empty object (the Value property is missing):
I want the response to be:
{
"ResultProperty": { "Value": "Hi there!" }
}
While the other answers are good and solves the problem, if all you want is the general behavior to be like pre netcore3, you can use the Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet package and in Startup.cs do:
services.AddControllers().AddNewtonsoftJson()
More info here. This way, you don't need to create any extra json-converters.
I ended up creating a custom JsonConverter (System.Text.Json.Serialization namespace) which forces JsonSerializer to serialize to the object's runtime type. See the Solution section below. It's lengthy but it works well and does not require me to sacrifice object oriented principles in my API's design. (If you need something quicker and can use Newtonsoft then check out the top voted answer instead.)
Some background: Microsoft has a System.Text.Json serialization guide with a section titled Serialize properties of derived classes with good information relevant to my question. In particular it explains why properties of derived types are not serialized:
This behavior is intended to help prevent accidental exposure of data
in a derived runtime-created type.
If that is not a concern for you then the behavior can be overridden in the call to JsonSerializer.Serialize by either explicitly specifying the derived type or by specifying object, for example:
// by specifying the derived type
jsonString = JsonSerializer.Serialize(objToSerialize, objToSerialize.GetType(), serializeOptions);
// or specifying 'object' works too
jsonString = JsonSerializer.Serialize<object>(objToSerialize, serializeOptions);
To accomplish this with ASP.NET Core you need to hook into the serialization process. I did this with a custom JsonConverter that calls JsonSerializer.Serialize one of the ways shown above. I also implemented support for deserialization which, while not explicitly asked for in the original question, is almost always needed anyway. (Oddly, supporting only serialization and not deserialization proved to be tricky anyway.)
Solution
I created a base class, DerivedTypeJsonConverter, which contains all of the serialization & deserialization logic. For each of your base types, you would create a corresponding converter class for it that derives from DerivedTypeJsonConverter. This is explained in the numbered directions below.
This solution follows the "type name handling" convention from Json.NET which introduces support for polymorphism to JSON. It works by including an additional $type property in the derived type's JSON (ex: "$type":"StringResultProperty") that tells the converter what the object's true type is. (One difference: in Json.NET, $type's value is a fully qualified type + assembly name, whereas my $type is a custom string which helps future-proof against namespace/assembly/class name changes.) API callers are expected to include $type properties in their JSON requests for derived types. The serialization logic solves my original problem by ensuring that all of the object's public properties are serialized, and for consistency the $type property is also serialized.
Directions:
1) Copy the DerivedTypeJsonConverter class below into your project.
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
public abstract class DerivedTypeJsonConverter<TBase> : JsonConverter<TBase>
{
protected abstract string TypeToName(Type type);
protected abstract Type NameToType(string typeName);
private const string TypePropertyName = "$type";
public override bool CanConvert(Type objectType)
{
return typeof(TBase) == objectType;
}
public override TBase Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
// get the $type value by parsing the JSON string into a JsonDocument
JsonDocument jsonDocument = JsonDocument.ParseValue(ref reader);
jsonDocument.RootElement.TryGetProperty(TypePropertyName, out JsonElement typeNameElement);
string typeName = (typeNameElement.ValueKind == JsonValueKind.String) ? typeNameElement.GetString() : null;
if (string.IsNullOrWhiteSpace(typeName)) throw new InvalidOperationException($"Missing or invalid value for {TypePropertyName} (base type {typeof(TBase).FullName}).");
// get the JSON text that was read by the JsonDocument
string json;
using (var stream = new MemoryStream())
using (var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = options.Encoder })) {
jsonDocument.WriteTo(writer);
writer.Flush();
json = Encoding.UTF8.GetString(stream.ToArray());
}
// deserialize the JSON to the type specified by $type
try {
return (TBase)JsonSerializer.Deserialize(json, NameToType(typeName), options);
}
catch (Exception ex) {
throw new InvalidOperationException("Invalid JSON in request.", ex);
}
}
public override void Write(Utf8JsonWriter writer, TBase value, JsonSerializerOptions options)
{
// create an ExpandoObject from the value to serialize so we can dynamically add a $type property to it
ExpandoObject expando = ToExpandoObject(value);
expando.TryAdd(TypePropertyName, TypeToName(value.GetType()));
// serialize the expando
JsonSerializer.Serialize(writer, expando, options);
}
private static ExpandoObject ToExpandoObject(object obj)
{
var expando = new ExpandoObject();
if (obj != null) {
// copy all public properties
foreach (PropertyInfo property in obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanRead)) {
expando.TryAdd(property.Name, property.GetValue(obj));
}
}
return expando;
}
}
2) For each of your base types, create a class that derives from DerivedTypeJsonConverter. Implement the 2 abstract methods which are for mapping $type strings to actual types. Here is an example for my IResultProperty interface that you can follow.
public class ResultPropertyJsonConverter : DerivedTypeJsonConverter<IResultProperty>
{
protected override Type NameToType(string typeName)
{
return typeName switch
{
// map string values to types
nameof(StringResultProperty) => typeof(StringResultProperty)
// TODO: Create a case for each derived type
};
}
protected override string TypeToName(Type type)
{
// map types to string values
if (type == typeof(StringResultProperty)) return nameof(StringResultProperty);
// TODO: Create a condition for each derived type
}
}
3) Register the converters in Startup.cs.
services.AddControllers()
.AddJsonOptions(options => {
options.JsonSerializerOptions.Converters.Add(new ResultPropertyJsonConverter());
// TODO: Add each converter
});
4) In requests to the API, objects of derived types will need to include a $type property. Example JSON: { "Value":"Hi!", "$type":"StringResultProperty" }
Full gist here
The documentation shows how to serialize as the derived class when calling the serializer directly. The same technique can also be used in a custom converter that we then can tag our classes with.
First, create a custom converter
public class AsRuntimeTypeConverter<T> : JsonConverter<T>
{
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return JsonSerializer.Deserialize<T>(ref reader, options);
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
JsonSerializer.Serialize(writer, value, value?.GetType() ?? typeof(object), options);
}
}
Then mark the relevant classes to be used with the new converter
[JsonConverter(typeof(AsRuntimeTypeConverter<MyBaseClass>))]
public class MyBaseClass
{
...
Alternately, the converter can be registered in startup.cs instead
services
.AddControllers(options =>
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new AsRuntimeTypeConverter<MyBaseClass>());
}));
I had a similar issue, where I was returning an enumerable of type TAnimal (but the object instances were of derived types such as Dog, Cat, etc.):
[HttpGet]
public IEnumerable<TAnimal> GetAnimals()
{
IEnumerable<TAnimal> list = GetListOfAnimals();
return list;
}
This only included properties defined in TAnimal.
However, in ASP .NET Core 3.1 at least, I found that I could just cast the object instances to object, and the JSON serializer then included all the properties from the derived classes:
[HttpGet]
public IEnumerable<object> GetAnimals()
{
IEnumerable<TAnimal> list = GetListOfAnimals();
return list.Select(a => (object)a);
}
(Note that the signature of the GetAnimals method must also changed, but that doesn't usually matter much in a web API context). If you need to provide type information for Swagger or whatever, you can annotate the method:
[HttpGet]
[Produces(MediaTypeNames.Application.Json, Type = typeof(TAnimal[]))]
public IEnumerable<object> GetAnimals()
{
...
}
Casting to object is a simple solution if you only have a 1-layer-deep object hierarchy to worry about.
This is the expected result. You're upcasting when you do that, so what will be serialized is the upcasted object, not the actual derived type. If you need stuff from the derived type, then that has to be the type of the property. You may want to use generics for this reason. In other words:
public class Result<TResultProperty>
where TResultProperty : IResultProperty
{
public TResultProperty ResultProperty { get; set; } // property uses an interface type
}
Then:
return new Result<StringResultProperty> {
ResultProperty = new StringResultProperty { Value = "Hi there!" }
};
I solved it by writing this extension:
public static class JsonSerializationExtensions
{
public static string ToJson<T>(this IEnumerable<T> enumerable, bool includeDerivedTypesProperties = true)
where T : class
{
var jsonOptions = new JsonSerializerOptions()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
if (includeDerivedTypeProperties)
{
var collection = enumerable.Select(e => e as object).ToList();
return JsonSerializer.Serialize<object>(collection, jsonOptions);
}
else
{
return JsonSerializer.Serialize(enumerable, jsonOptions);
}
}
}
I was also struggling with this in a .NET Core 3.1 API, where I wanted the result to include $type attribute.
As suggested, install the correct package and then 'AddNewtonsoftJson'.
I wanted the $type field to be added to show the derived type handling, to get that
services.AddControllers().AddNewtonsoftJson(options =>
{
options.SerializerSettings.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.All;
});
Not knocking Newtonsoft, but I found an easier way to resolve this with the built handlers.
[OperationContract]
[WebInvoke(Method = "GET", UriTemplate = "/emps", BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
List<emp> GetEmps();
//[DataContract(Namespace = "foo")] <<< comment/removed this line
public class emp
{
public string userId { get; set; }
public string firstName { get; set; }
}
public class dept
{
public string deptId{ get; set; }
public string deptName{ get; set; }
}
In my case dept objects where working fine, but emp ones were not - they came across as empty.

Creating a predefined JsonConvert attribute?

My problem is quite simple..
What I would like to do is this:
[JsonConverter(typeof(MyConverter)]
object SomeProperty {get;set;}
But be able to write it as a custom attribute so I simply can decorate my properties with a pre-defined JsonConverter attribute.. so for instance
[MyCustomConverter]
object SomeProperty {get;set;}
Would in this case be treated as [JsonConverter(typeof(MyConverter))]
Any ideas?
Br,
Inx
It's not trivial, but you can do this if you implement a custom IContractResolver that takes your attributes into account.
There are several steps involved in doing this:
Create an abstract base class for your attributes that extends System.Attribute:
public abstract class ConverterAttribute : Attribute
{
public abstract JsonConverter Converter { get; }
}
Next, you need to create the IContractResolver that will actually use your attribute1:
public class CustomAttributeContractResolver : DefaultContractResolver
{
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
JsonObjectContract contract =
base.CreateObjectContract(objectType);
IEnumerable<JsonProperty> withoutConverter =
contract.Properties.Where(
pr => pr.MemberConverter == null &&
pr.Converter == null);
// Go over the results of calling the default implementation.
// check properties without converters for your custom attribute
// and pull the custom converter out of that attribute.
foreach (JsonProperty property in withoutConverter)
{
PropertyInfo info =
objectType.GetProperty(property.UnderlyingName);
var converterAttribute =
info.GetCustomAttribute<ConverterAttribute>();
if (converterAttribute != null)
{
property.Converter = converterAttribute.Converter;
property.MemberConverter = converterAttribute.Converter;
}
}
return contract;
}
}
Create the attribute that overrrides ConverterAttribute.Converter, returning your custom converter:
public class MyCustomConverterAttribute : ConverterAttribute
{
get { return new MyCustomConverter(); }
}
Decorate your class with the attribute:
public class MyClass
{
[MyCustomConverter]
public object MyProperty { get; set; }
}
When serializing or deserializing, specify the contract resolver in the JsonSerializerSettings you use:
var settings = new JsonSerializerSettings();
settings.ContractResolver = new CustomAttributeContractResolver();
string serialized = JsonConverter.SerializeObject(new MyClass());
I'd say that this probably isn't worth the small benefit--all you're really doing is saving a few characters, unless your attribute does something else.
1: I'm not sure what the difference between MemberConverter and Converter. When serializing, only the Converter property was needed, but deserializing required MemberConverter. I'll keep digging, but hopefully someone can provide some insight. Looks like others have had this same question as well.
It does not appear to be possible. The JsonConverterAttribute and TypeConverterAttribute classes are both sealed, and these are the classes used by Json.NET to supply a custom type converter.

Deserializing a mixed list of objects from JSON

I'm using the DataContractJsonSerializer to deserialize objects from an external service. In most cases, this has worked great for me. However, there is one case where I need to deserialize JSON that contains a list of objects that all inherit from the same base class, but there are many different types of objects in that list.
I know that it can be done easily by including a list of known types in the serializer's constructor, but I don't have access to the code that generated this JSON service. The types that I'm using will be different from the types used in the service (mostly just the class name and namespace will be different). In other words, the classes that the data was serialized with will not be the same classes that I'll use to deserialize it even though they'll be very similar.
With the XML DataContractSerializer, I can pass in a DataContractResolver to map the services types to my own types, but there is no such constructor for the DataContractJsonSerializer. Is there any way to do this? The only options that I've been able to find are: write my own deserializer, or use Microsoft's JsonObject that isn't tested and "should not be used in production environments."
Here is an example:
[DataContract]
public class Person
{
[DataMember]
public string Name { get; set; }
}
[DataContract]
public class Student : Person
{
[DataMember]
public int StudentId { get; set; }
}
class Program
{
static void Main(string[] args)
{
var jsonStr = "[{\"__type\":\"Student:#UnknownProject\",\"Name\":\"John Smith\",\"StudentId\":1},{\"Name\":\"James Adams\"}]";
using (var stream = new MemoryStream())
{
var writer = new StreamWriter(stream);
writer.Write(jsonStr);
writer.Flush();
stream.Position = 0;
var s = new DataContractJsonSerializer(typeof(List<Person>), new Type[] { typeof(Student), typeof(Person) });
// Crashes on this line with the error below
var personList = (List<Person>)s.ReadObject(stream);
}
}
}
Here is the error mentioned in the comment above:
Element ':item' contains data from a type that maps to the name
'http://schemas.datacontract.org/2004/07/UnknownProject:Student'. The
deserializer has no knowledge of any type that maps to this name. Consider using
a DataContractResolver or add the type corresponding to 'Student' to the list of
known types - for example, by using the KnownTypeAttribute attribute or by adding
it to the list of known types passed to DataContractSerializer.
I found the answer. It was very simple. I just needed to update my DataContract attribute to specify which namespace (you can also specify a different name) they map to in the source JSON like this:
[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/UnknownProject")]
public class Person
{
[DataMember]
public string Name { get; set; }
}
[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/UnknownProject"]
public class Student : Person
{
[DataMember]
public int StudentId { get; set; }
}
That JsonObject was a sample for .NET 3.5. There is a project in codeplex - http://wcf.codeplex.com - which has a tested implementation of the JsonValue/JsonObject/JsonArray/JsonPrimitive classes, including source code and unit tests. With that you can parse "untyped" JSON. Another well-used JSON framework is the JSON.NET at http://json.codeplex.com.
You may create a DTO before serializing.
I use a class like: (pseudo code)
class JsonDto
string Content {get;set;}
string Type {get;set;}
ctor(object) => sets Content & Type Properties
static JsonDto FromJson(string) // => Reads a Serialized JsonDto
// and sets Content+Type Properties
string ToJson() // => serializes itself into a json string
object Deserialize() // => deserializes the wrapped object to its saved Type
// using Content+Type properties
T Deserialize<T>() // deserializes the object as above and tries to cast to T
Using the JsonDto you can easily serialize arbitrary objects to JSON and deserialize them to their common base type because the deserializer will always know the original type and returns an type of object reference which will be casted if you use the generic Deserialize<T> method.
One caveat: If you set the Type property you should use the AssemblyQualifiedName of the type, however without the version attribute (ex: MyCompany.SomeNamespace.MyType, MyCompany.SomeAssembly). If you just use the AssemblyQualifiedName property of the Type class you will end up with errors if your assembly version changes.
I implemented a JsonDtoCollection the same way, which derives from List<JsonDto> and provides methods to handle collections of objects.
class JsonDtoCollection : List<JsonDto>
ctor(List<T>) => wraps all items of the list and adds them to itself
static JsonDtoCollection FromJson(string) // => Reads a collection of serialized
// JsonDtos and deserializes them,
// returning a Collection
string ToJson() // => serializes itself into a json string
List<object> Deserialize() // => deserializes the wrapped objects using
// JsonDto.Deserialize
List<T> Deserialize<T>() // deserializes the as above and tries to cast to T

Using JSON.net, how do I prevent serializing properties of a derived class, when used in a base class context?

Given a data model:
[DataContract]
public class Parent
{
[DataMember]
public IEnumerable<ChildId> Children { get; set; }
}
[DataContract]
public class ChildId
{
[DataMember]
public string Id { get; set; }
}
[DataContract]
public class ChildDetail : ChildId
{
[DataMember]
public string Name { get; set; }
}
For implementation convenience reasons, there are times when the ChildId objects on the Parent are in fact ChildDetail objects. When I use JSON.net to serialize the Parent, they are written out with all of the ChildDetail properties.
Is there any way to instruct JSON.net (or any other JSON serializer, I'm not far enough into the project to be committed to one) to ignore derived class properties when serializing as a base class?
EDIT: It is important that when I serialize the derived class directly that I'm able to produce all the properties. I only want to inhibit the polymorphism in the Parent object.
I use a custom Contract Resolver to limit which of my properties to serialize. This might point you in the right direction.
e.g.
/// <summary>
/// json.net serializes ALL properties of a class by default
/// this class will tell json.net to only serialize properties if they MATCH
/// the list of valid columns passed through the querystring to criteria object
/// </summary>
public class CriteriaContractResolver<T> : DefaultContractResolver
{
List<string> _properties;
public CriteriaContractResolver(List<string> properties)
{
_properties = properties
}
protected override IList<JsonProperty> CreateProperties(
JsonObjectContract contract)
{
IList<JsonProperty> filtered = new List<JsonProperty>();
foreach (JsonProperty p in base.CreateProperties(contract))
if(_properties.Contains(p.PropertyName))
filtered.Add(p);
return filtered;
}
}
In the override IList function, you could use reflection to populate the list with only the parent properties perhaps.
Contract resolver is applied to your json.net serializer. This example is from an asp.net mvc app.
JsonNetResult result = new JsonNetResult();
result.Formatting = Formatting.Indented;
result.SerializerSettings.ContractResolver =
new CriteriaContractResolver<T>(Criteria);
I had the exact same problem and looked up how to build the ContractResolver I was actually looking for and that better answer this question. This only serializes the Properties of the Type T you actually want to serialize, but with this example you can also easily build similar approaches:
public class TypeOnlyContractResolver<T> : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
property.ShouldSerialize = instance => property.DeclaringType == typeof (T);
return property;
}
}
Having encountered a similar problem, this is the ContractResolver I came up with:
public class StrictTypeContractResolver : DefaultContractResolver
{
private readonly Type _targetType;
public StrictTypeContractResolver( Type targetType ) => _targetType = targetType;
protected override IList<JsonProperty> CreateProperties( Type type, MemberSerialization memberSerialization )
=> base.CreateProperties
(
_targetType.IsAssignableFrom( type ) ? _targetType : type,
memberSerialization
);
}
It cuts off only the properties of targetType's descendants, without affecting the properties of its base classes or of other types that targetType's properties might reference. Which, depending on your needs, may or may not be an improvement over the other answers provided here at the time.
Check out the answers in this similar thread, particularly the IgnorableSerializerContractResolver in my answer and the nicer lambda version
Usage:
var jsonResolver = new IgnorableSerializerContractResolver();
// ignore single property
jsonResolver.Ignore(typeof(Company), "WebSites");
// ignore single datatype
jsonResolver.Ignore(typeof(System.Data.Objects.DataClasses.EntityObject));
var jsonSettings = new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, ContractResolver = jsonResolver };
I have not used JSON.Net in particular so not positive this will help you. If JSON.Net derives itself from the .Net serialization system then you should be able to add the [NonSerialized] attribute to your properties you do now wish to be serialized in the base class. When you call the serialize methods on the base class, serialization should then skip those elements.
Haven't compared performance implications, but this is a working solution as well, and works with nested/referenced objects as well.
Derived d = new Derived();
string jsonStringD = JsonConvert.SerializeObject(d);
Base b = new Base();
JsonConvert.PopulateObject(jsonStringD, b);
string jsonStringB = JsonConvert.SerializeObject(b);

Using JSON.NET, how do I serialize these inherited members?

I have the following:
public class MyClass : SuperClass {
[JsonProperty]
public virtual string Id { get; set; }
}
public abstract class SuperClass {
public int GetHashCode() {
//do things here
}
}
I cannot alter SuperClass. When I go to serialize to Json using JsonNet I'll do something like this:
JsonSerializerSettings serializer = new JsonSerializerSettings {
//serializer settings
};
var jsonNetResult = new JsonNetResult {
Data = myClass,
SerializerSettings = serializer
};
return jsonNetResult;
Obviously it will not serialize GetHashCode(). If I go:
var jsonNetResult = new JsonNetResult {
Data = myClass.GetHashCode(),
SerializerSettings = serializer
};
It will correctly serialize the value, is there some serializer setting I can use to tell it to include GetHashCode()?
Edit: I should add that right now I'm creating a property with only get to accomplish this, i.e.
[JsonProperty]
public virtual int GetHashCodeJson { get { return GetHashCode(); }
This is not so much an issue with JSON.Net as with .net serialization in general.
You need to serialize objects by their properties and you are asking to serialize the return value of a method. So there is not a way to do this with the syntax you want.
That you are able to do this:
Data = myClass.GetHashCode()
Only means that the return value of the method (an int) can be serialized and not that the serializer cares at all about what method that value is coming from.
If you think about it, there is not a lot of sense to saying that a value is the serialized return value of a method because how do you deserialize that then? You would never be able to write the value back to the method because its a return value only, not a 2-way relationship like a property with {get;set;}.

Categories