Генерация VS Swagger с двойным наследием и дискриминатором

Рейтинг: 1Ответов: 0Опубликовано: 15.03.2023

Замечание: VS использует для генерации кода NSwag библиотеки

Есть сваггер:

{
  "openapi": "3.0.1",
  "info": {...},
  "servers": [...],
  "paths": {...},
  "components": {
    "schemas": {
      "Interface": {
        "type": "object",
        "properties": {...},
        "discriminator": {
          "propertyName": "ByClass",
          "mapping": {
            "1_1": "#/components/schemas/Class1_1",
            "1_2": "#/components/schemas/Class1_2"
          }
        }
      },
      "Class1_1": {
        "allOf": [
          {"$ref": "#/components/schemas/Interface"},
          {
            "type": "object",
            "properties": {...}
          }
        ],
        "discriminator": {
          "propertyName": "BySubClass",
          "mapping": {
            "2_1": "#/components/schemas/Class2_1",
            "2_2": "#/components/schemas/Class2_2"
          }
        }
      }      
      "Class1_2": {
        "allOf": [
          {"$ref": "#/components/schemas/Interface"},
          {
            "type": "object",
            "properties": {...}
          }
        ]
      }
      "Class2_1": {
        "allOf": [
          {"$ref": "#/components/schemas/Class1_1"},
          {
            "type": "object",
            "properties": {...}
          }
        ]
      }
      "Class2_2": {
        "allOf": [
          {"$ref": "#/components/schemas/Class1_1"},
          {
            "type": "object",
            "properties": {...}
          }
        ]
      }
    }
  }
}      
        

По факту описывает наследование классов

Interface ┬-> Class1_1 ┬-> Class2_1 
          │            └-> Class2_2
          └-> Class1_2

И дискриминаторы.

Таким образом если у ресурса есть 2 поля

{
  "ByClass": "1_1",
  "BySubClass": "2_1", 
  ...
}

то код, сгенереный в VS должен при десериализации сделать класс Class2_1 Но сделает он класс Class1_1, и вот почему, при генерации будет следующий код:

[Newtonsoft.Json.JsonConverter(typeof(JsonInheritanceConverter), "ByClass")]
[JsonInheritanceAttribute("1_1", typeof(Class1_1))]
[JsonInheritanceAttribute("1_2", typeof(Class1_2))]
[JsonInheritanceAttribute("2_1", typeof(Class2_1))]
[JsonInheritanceAttribute("2_2", typeof(Class2_1))]
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.0.22.0 (Newtonsoft.Json v11.0.0.0)")]
public partial class Interface
{
  ...
}

Таким образом теряется дискриминатор BySubClass и поэтому вернётся экземпляр Class1_1

и даже если явно перенести в Class1_1

[JsonInheritanceAttribute("2_1", typeof(Class2_1))]
[JsonInheritanceAttribute("2_2", typeof(Class2_1))]

и добавить

[Newtonsoft.Json.JsonConverter(typeof(JsonInheritanceConverter), "BySubClass")]

то всё равно вернётся класс Class1_1 и вот почему:

[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.0.22.0 (Newtonsoft.Json v11.0.0.0)")]
internal class JsonInheritanceConverter : Newtonsoft.Json.JsonConverter
{
  ...
  public override bool CanRead
  {
    get
    {
      if (_isReading)
      {
          _isReading = false;
          return false;
      }
      return true;
    }
  }
  ...
}

при двойной сериализации класса, второй раз подряд этот конвертер не отработает.

Я пошёл дальше и изменил строчку в этом сериализаторе:

public override object ReadJson(Newtonsoft.Json.JsonReader reader, System.Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
{
  ...
  try
  {
    _isReading = subtype == objectType;// Была добавлена эта строчка
    return serializer.Deserialize(jObject.CreateReader(), subtype);
  }
  finally
  {
    _isReading = false;
  }
}

и всё заработало, но теперь вопрос, как без этих танцев сделать чтобы сгенеренный код верно парсил ресурс?

Ответы

Ответов пока нет.