Abstract Base Class and Inheritance
See original GitHub issueHey,
I have a special situation in which one of the methods of my controller returns an array of an abstract base class which contains children of that class. However, the code generated with the NSwag Studio is unusable and also there is no way to access properties exclusive to the child classes even if the code generated by the NSwag is fixed manually. Let’s take a look at the following examples:
What Happens Now:
Api Server:
public abstract class BaseSettings {
public string SharedProperty { get; set; } = "Shared";
}
public class BarSettings : BaseSettings {
public string BarProperty { get; set; } = "Bar";
}
public class FooSettings : BaseSettings {
public string FooProperty { get; set; } = "Foo";
}
public class SampleController : ApiController {
[SwaggerResponse(HttpStatusCode.OK, typeof(BaseSettings[]))]
public IEnumerable<BaseSettings> GetSettings() {
return new BaseSettings[] { new BarSettings(), new FooSettings() };
}
}
Generated Definition:
.......
"BaseSettings": {
"type": "object",
"x-abstract": true,
"additionalProperties": false,
"properties": {
"SharedProperty": {
"type": "string"
}
}
},
.......
"/api/Settings/GetSettings": {
"get": {
"tags": [
"Settings"
],
"operationId": "Settings_GetSettings",
"responses": {
"200": {
"x-nullable": true,
"description": "",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/BaseSettings"
}
}
}
},
.......
Actual Response;
[
{
"SharedProperty": "Shared",
"BarProperty": "Bar"
},
{
"SharedProperty": "Shared",
"FooProperty": "Foo"
}
]
Generated client code:
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "9.10.67.0 (Newtonsoft.Json v9.0.0.0)")]
public partial abstract class BaseSettings : System.ComponentModel.INotifyPropertyChanged
{
private string _sharedProperty;
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
[Newtonsoft.Json.JsonProperty("SharedProperty", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public string SharedProperty
{
get { return _sharedProperty; }
set
{
if (_sharedProperty != value)
{
_sharedProperty = value;
RaisePropertyChanged();
}
}
}
public string ToJson()
{
return Newtonsoft.Json.JsonConvert.SerializeObject(this);
}
public static BaseSettings FromJson(string data)
{
return Newtonsoft.Json.JsonConvert.DeserializeObject<BaseSettings>(data);
}
protected virtual void RaisePropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
Now as you can clearly see from the above example,
-
This method by itself is not executable as the generated
BaseSettings
class is, in fact, anabstract
class and can’t instantiate from. This can be solved by manually removingabstract
modifier from the generated C# client code tho. -
However, the problem with not being able to access child properties remains. This is especially unfortunate because the child class’s property values are in fact available in the response generated by WebAPI, but we can’t decode it right.
Is there any way to solve this or at least a way to work around this problem?
Proposed solution:
- A new property for the
SwaggerResponseAttribute
class allowing us to provide a list of additional types for the WebApi Server:
public class SampleController : ApiController {
[SwaggerResponse(HttpStatusCode.OK, typeof(BaseSettings[]), AdditionalTypes = new [] {typeof(BarSettings[]), typeof(FooSettings[])})]
public IEnumerable<BaseSettings> GetSettings() {
return new BaseSettings[] { new BarSettings(), new FooSettings() };
}
}
- Generating type definition for the child classes based on this property; also containing an
x-inherits
property (x-implements
for interfaces?):
.......
"BaseSettings": {
"type": "object",
"x-abstract": true,
"additionalProperties": false,
"properties": {
"SharedProperty": {
"type": "string"
}
}
},
"BarSettings": {
"type": "object",
"x-abstract": false,
"x-inherits": "#/definitions/BaseSettings",
"additionalProperties": false,
"properties": {
"SharedProperty": {
"type": "string"
},
"BarProperty": {
"type": "string"
}
}
},
"FooSettings": {
"type": "object",
"x-abstract": false,
"x-inherits": "#/definitions/BaseSettings",
"additionalProperties": false,
"properties": {
"SharedProperty": {
"type": "string"
},
"FooProperty": {
"type": "string"
}
}
},
.......
- Specifying the target type in the method response:
[
{
"$type": "#/definitions/BarSettings",
"SharedProperty": "Shared",
"BarProperty": "Bar"
},
{
"$type": "#/definitions/FooSettings",
"SharedProperty": "Shared",
"FooProperty": "Foo"
}
]
I suspect this problem to arise also when an interface is used instead of an abstract class.
Issue Analytics
- State:
- Created 5 years ago
- Comments:5 (2 by maintainers)
Did you check: https://github.com/RSuter/NJsonSchema/wiki/Inheritance
I am going to close this and let the discussion continue in here:
https://github.com/RSuter/NJsonSchema/pull/829