RicoSuter / NJsonSchema

JSON Schema reader, generator and validator for .NET
http://NJsonSchema.org
MIT License
1.4k stars 535 forks source link

Create schema from generated code with inheritance. #1162

Open SebastianStehle opened 4 years ago

SebastianStehle commented 4 years ago

Hi,

I have special case, that causes issues.

I have a API with inheritance and a custom discriminator:

In short it looks like this:

[JsonConverter(typeof(TypedJsonInheritanceConverter<FieldPropertiesDto>), "fieldType")]
    [KnownType(nameof(Subtypes))]
    public abstract class FieldPropertiesDto
    {
        public static Type[] Subtypes()
        {
            return type.Assembly ...
        }
    }

As you can see, I have a custom converter, which derives from your InheritanceConverter and uses custom type names. This works fine, thanks to your awesome library.

From the API I generate a client library in C# and the output is like this:

[JsonConverter(typeof(JsonInheritanceConverter), new[] { "fieldType" })]
    [JsonInheritanceAttribute("Array", typeof(ArrayFieldPropertiesDto))]]
    public abstract class FieldPropertiesDto
    {
    }
}

The output is as expected. But this client library should be extended with an import feature, so I would like to use the generated code to create a json schema from it and I expect the result to be identical to the API schema.

But there are two issues:

  1. The discriminator is discriminator instead of fieldType. I think the reason is that the generated JsonInheritanceConverter class does not contain a DiscriminatorName property. This should be easy to fix.

  2. The type names are used instead of my custom names, e.g ArrayFieldPropertiesDto instead of Array. I don't know how to solve that.

RicoSuter commented 4 years ago

I think "round-trip" does not work currently because the schema generator does not read/know "JsonInheritanceAttribute"I think. Shouldnt be hard to support both I think.

SebastianStehle commented 4 years ago

The other problem is that the attribute needs to be public, otherwise the runtime cannot access it. I solved it by excluding the InheritenceConverter from the list of generated types and using the default converter (copy and paste) from NJsonSchema.

STeeL835 commented 3 years ago

We had the same problems (default discriminator names and values) when schema is generated based on generated client of other service. We were able to resolve these issues by using modified JsonInheritanceConverter.liquid template based on Newtonsoft converter with added DiscriminatorName property and GetDiscriminatorValue method (for some reason original template has them only for System.Text.Json converter) and class access modifier changed to public (otherwise it was throwing an exception trying to access DiscriminatorName)

Exception if template converter is internal ``` System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'Newtonsoft.Json.JsonConverter' does not contain a definition for 'DiscriminatorName' at CallSite.Target(Closure , CallSite , Object ) at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0) at NJsonSchema.Generation.JsonSchemaGenerator.TryGetInheritanceDiscriminatorName(Object jsonInheritanceConverter) at NJsonSchema.Generation.JsonSchemaGenerator.GenerateInheritanceDiscriminator(Type type, JsonSchema schema, JsonSchema typeSchema) at NJsonSchema.Generation.JsonSchemaGenerator.GenerateObject(JsonSchema schema, JsonTypeDescription typeDescription, JsonSchemaResolver schemaResolver) at NSwag.Generation.OpenApiSchemaGenerator.GenerateObject(JsonSchema schema, JsonTypeDescription typeDescription, JsonSchemaResolver schemaResolver) ... ```

I didn't look into how to keep the template converter internal though, it was okay for us to make it public. Most probably it would require changes in NJsonSchema