Reputation: 85
I need to JsonSerializer.Serialize(...) a class containig a list of a base class:
// ----- Models -----
public class MainClass
{
[Key]
public Guid Id { get; private set; } = Guid.NewGuid();
public List<BaseClass> Properties { get; set; } = new List<BaseClass>();
}
public class BaseClass
{
[Key]
public Guid Id { get; private set; } = Guid.NewGuid();
public string Name { get; set; } = string.Empty;
}
public class GenericDerivedClass<T> : BaseClass
{
public T? Value { get; set; }
}
// ----- Implementation -----
var main = new MainClass
{
Properties = new List<BaseClass>
{
new GenericDerivedClass<string>
{
Name = "SoundFile",
Value = "Test.wav"
},
new GenericDerivedClass<float>
{
Name = "Volume",
Value = 1
},
new GenericDerivedClass<bool>
{
Name = "Autoplay",
Value = false
},
new GenericDerivedClass<bool>
{
Name = "Loop",
Value = false
},
}
};
Console.WriteLine(JsonSerializer.Serialize(main, new JsonSerializerOptions { WriteIndented = true }));
// ----- Output (JsonSerializer) ----
[
{
"main": [
{
"id": "ba348c86-aa86-45ea-8d21-a9beddd4368a",
"properties": [
{
"id": "a9f432d5-3916-4c1d-b44a-fd4b7d8fcb45",
"name": "SoundFile",
//"value": "Test.wav" <- I want this line here, but I cannot figure out how.
},
{
"id": "f585d863-b0d7-49b3-ad5c-0565171e6793",
"name": "Volume"
},
{
"id": "197802f3-17cd-4c1f-90be-7ea643ee5d7d",
"name": "Autoplay"
},
{
"id": "b90e3857-e497-4137-adeb-94b66293d375",
"name": "Loop"
}
]
}
],
}
]
The problem here is, that only the properties of the base class (BaseClass) are serialized (Id and Name). Is there a way to serialize the class "MainClass" with a List<BaseClass>
containing the information (Value) of each GenericDerivedClass<T>
?
(Using List<object>
instead of List<BaseClass>
is not an option, since I cannot use primitive types.)
Upvotes: 3
Views: 1262
Reputation: 9582
Beginning with .NET 7, System.Text.Json supports polymorphic type hierarchy serialization and deserialization with attribute annotations.
[JsonDerivedType(typeof(Dog))]
[JsonDerivedType(typeof(Cat))]
public class Animal
{
// ...snip
}
I haven't tested this with a derived classes that have a generic constraint like in your example, but I imagine it would be something similar:
[JsonDerivedType(typeof(GenericDerivedClass<string>))]
[JsonDerivedType(typeof(GenericDerivedClass<float>))]
[JsonDerivedType(typeof(GenericDerivedClass<bool>))]
public class BaseClass
{
// ...snip
}
Upvotes: 2
Reputation: 85
Olivers comment got me on the right track, thank you.
It's a bit tideous, to go over all "propertynames" in a switch, but it seems to do the trick!
If you run into the same problem, you have to write a custom converter, like in the c# example shown here:
Here is my code (overriding "Write()" in a class deriving from JsonConverter<>):
public class PropertyConverter : JsonConverter<Property>
{
public override bool CanConvert(Type typeToConvert) =>
typeof(Property).IsAssignableFrom(typeToConvert);
public override Property? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
public override void Write(Utf8JsonWriter writer, Property value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WriteString("name", value.Name);
if (value is GenericProperty<float> floatProperty)
{
writer.WriteNumber("value", floatProperty.Value);
}
else if (value is GenericProperty<int> intProperty)
{
writer.WriteNumber("value", intProperty.Value);
}
else if (value is GenericProperty<string> stringProperty)
{
writer.WriteString("value", stringProperty.Value);
}
else if (value is GenericProperty<bool> boolProperty)
{
writer.WriteBoolean("value", boolProperty.Value);
}
writer.WriteEndObject();
}
}
Make sure to add the custom converter to JsonSerializerOptions:
var serializeOptions = new JsonSerializerOptions()
{
WriteIndented = true,
};
serializeOptions.Converters.Add(new PropertyConverter());
Console.WriteLine(JsonSerializer.Serialize(_json, serializeOptions));
Upvotes: 2
Reputation: 61
System.Text.Json does not support it by design. However there might be a workaround- if you add [JsonIgnore]
before the Properties
property and add a property such as
[JsonPropertyName] public IEnumerable<object> JsonProperties {get=> Items;}
Keep in mind that you still can't deserialize it
Upvotes: 1