Reputation: 1991
I'm using Autorest
to generate a csharp sdk from a swagger 2.0 definition and I'm wondering if it is possible to have a generated child class override a parent's property with a different type.
Example swagger:
{
"swagger": "2.0",
"info": {
"version": "1",
"title": "My API"
},
"schemes": [
"https"
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {
"/myResource": {
"post": {
"parameters": [
{
"name": "myResource_data",
"in": "body",
"schema": {
"$ref": "#/definitions/myResourceCreateBody"
},
"required": true
}
],
"responses": {
"201": {
"description": "myResource response",
"schema": {
"$ref": "#/definitions/myResourceResponseExtended"
}
}
}
}
}
},
"definitions": {
"metaResponse": {
"type": "object",
"description": "Response metadata.",
"required": [
"api_version"
],
"properties": {
"api_version": {
"type": "string"
}
},
"x-services": [
"shared"
]
},
"extendedMetaResponse": {
"allOf": [
{
"$ref": "#/definitions/metaResponse"
},
{
"type": "object",
"properties": {
"myExtendedProp": {
"type": "string"
}
}
}
],
"x-services": [
"shared"
]
},
"myResourceResponse": {
"type": "object",
"required": [
"meta"
],
"additionalProperties": false,
"properties": {
"meta": {
"$ref": "#/definitions/metaResponse"
}
},
"x-services": [
"myResource"
]
},
"myResourceResponseExtended": {
"allOf": [
{
"$ref": "#/definitions/myResourceResponse"
},
{
"type": "object",
"properties": {
"meta": {
"$ref": "#/definitions/extendedMetaResponse"
}
}
}
],
"x-services": [
"myResource"
]
},
"myResourceCreateBody": {
"type": "object",
"required": [
"myResource_id"
],
"additionalProperties": false,
"properties": {
"myResource_id": {
"type": "string",
"description": "myResource identifier"
}
},
"x-services": [
"myResource"
]
}
}
}
Which generates the following C# base class:
public partial class MyResourceResponse
{
public MyResourceResponse()
{
CustomInit();
}
public MyResourceResponse(MetaResponse meta)
{
Meta = meta;
CustomInit();
}
partial void CustomInit();
[JsonProperty(PropertyName = "meta")]
public MetaResponse Meta { get; set; }
public virtual void Validate()
{
if (Meta == null)
{
throw new ValidationException(ValidationRules.CannotBeNull, "Meta");
}
if (Meta != null)
{
Meta.Validate();
}
}
}
}
And the following child class:
public partial class MyResourceResponseExtended : MyResourceResponse
{
public MyResourceResponseExtended()
{
CustomInit();
}
public MyResourceResponseExtended(MetaResponse meta)
: base(meta)
{
CustomInit();
}
partial void CustomInit();
public override void Validate()
{
base.Validate();
}
}
But my desired output would be to have MyResourceResponseExtended
to override the Meta
property with a Meta
property of type MetaResponseExtended
to expose its additional properties.
For example, my desired output would be:
public partial class MyResourceResponseExtended : MyResourceResponse
{
[JsonProperty(PropertyName = "meta")]
public new ExtendedMetaResponse Meta { get; set; }
public MyResourceResponseExtended(ExtendedMetaResponse meta)
: base(meta)
{
Meta = meta;
CustomInit();
}
}
Is there a better way to define this in Swagger 2.0? Is this a bug/limitation of Autorest?
Thanks! :-)
PS: I've looked into using discriminator
in the swagger file, but I'm either not using it correctly or it is not designed for this specific purpose.
Upvotes: 1
Views: 774
Reputation: 953
You definitely need to use a discriminator
, and make a polymorphic type out of it.
In order to have a polymorphic type, you have to have a property that you declare as a discriminator
and each type that is a child (ie, using allOf
) of the parent class must have an x-ms-discriminator-value
that it can use as a key to allow the deserializer to choose the correct type to deserialize into.
simple example:
swagger: '2.0'
info:
version: 1.0.0
title: Swagger Petstore
license:
name: MIT
host: petstore.swagger.io
basePath: "/v1"
schemes:
- https
consumes:
- application/json
produces:
- application/json
paths:
"/pets":
put:
summary: Add a pet
operationId: addPet
tags:
- pets
parameters:
- schema:
"$ref": "#/definitions/Pet"
responses:
'200':
description: OK
definitions:
Pet:
properties:
petKind:
type: string
name:
type: string
discriminator: petKind
required:
- name
- petType
Cat:
description: A representation of a cat
x-ms-discriminator-value: CAT
allOf:
- "$ref": "#/definitions/Pet"
- properties:
huntingSkill:
type: string
description: The measured skill for hunting
default: lazy
enum:
- clueless
- lazy
- adventurous
- aggressive
required:
- huntingSkill
Dog:
description: A representation of a dog
x-ms-discriminator-value: DOG
allOf:
- "$ref": "#/definitions/Pet"
- properties:
packSize:
type: integer
format: int32
description: the size of the pack the dog is from
default: 0
minimum: 0
required:
- packSize
Pet
objects have a petKind
property (DOG
or CAT
) that tells the deserializer what model to use to deserialize the value.
(In generated c# it would use an attribute on the class for that.)
Upvotes: 1