Closed rootix closed 9 months ago
Using the AddOpenApiDocument
call to set a flat hierarchy solves the first issue and allows compilation again.
namespace Net80NJsonSchema11;
using System.Text.Json;
using NJsonSchema;
using NJsonSchema.Generation;
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddOpenApiDocument(c =>
{
c.SchemaSettings = new SystemTextJsonSchemaGeneratorSettings
{
SchemaType = SchemaType.OpenApi3,
FlattenInheritanceHierarchy = true,
};
});
builder.Services.AddControllers().AddJsonOptions(o => o.JsonSerializerOptions.Converters.Add(new NetTopologySuite.IO.Converters.GeoJsonConverterFactory()));
// builder.Services.AddSwaggerDocument();
var app = builder.Build();
app.MapControllers();
app.Run();
}
}
If you exclude further types, e.g "excludedTypeNames" in nswag.json: "LazyOfPrecisionModel", "SortIndexValue" the generated api.ts is basically the same as with sdk 7.
But in both version (7 / 8) the OpenApi definition while using NetTopologySuite NetTopologySuite is misleading or wrong, hence the reason why you exclude types or I use for very restricted use cases ITypeMapper.
Thanks for your investigation! The generation works indeed on my way more complex case.
But i can't handle the flat hierarchy in our project. I have different types where inheritance is essential in the client code and additionally i use the JsonInheritanceConverter which completly breaks by setting this flag. There is no discriminator or mapping code for child classes anymore.
I understand that NetTopology is not serializable. That is the reason why they provide a GeoJson package for conversion (Newtonsoft and System.Text.Json) that we use on the API at runtime. I saw that you configured the SchemaSettings with SystemTextJsonSchemaGeneratorSettings where i can define custom json serializer options and had hope that i can register the geometry factory there to make them serializable. But i still get the same Exception as reported.
Since this worked with NJsonSchema 10 (or because of .NET 7?), is this a problem that can (and should) be solved in NJsonSchema 11 to get it working again or am I out of luck here?
For a restricted subset of GeoJson I found the use of ITypeMapper the most convenient solution. In your example add the typemapper for Point at startup. FlattenInheritanceHierarchy is no longer necessary.
namespace Net80NJsonSchema11;
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApiDocument(c =>
{
c.SchemaSettings.TypeMappers.Add(new PointTypeMapper());
});
builder.Services.AddControllers().AddJsonOptions(o => o.JsonSerializerOptions.Converters.Add(new NetTopologySuite.IO.Converters.GeoJsonConverterFactory()));
var app = builder.Build();
app.UseOpenApi();
app.MapControllers();
app.UseSwaggerUi(options =>
{
options.Path = "/openapi";
});
app.Run();
}
}
Typemapper for GeoJson Point type
using NetTopologySuite.Geometries;
using NJsonSchema;
using NJsonSchema.Generation.TypeMappers;
namespace Net80NJsonSchema11;
// see also for specification https://app.swaggerhub.com/apis/OlivierMartineau/GeoJSON/1.0.0#
class PointTypeMapper : ITypeMapper
{
public void GenerateSchema(JsonSchema schema, TypeMapperContext context)
{
if (context.Type == typeof(Point))
{
var hasSchema = context.JsonSchemaResolver.HasSchema(typeof(Point), false);
schema.Type = JsonObjectType.Object;
schema.Description = "GeoJSON Point";
schema.Properties.Add("type", new JsonSchemaProperty
{
Type = JsonObjectType.String,
IsRequired = true,
Example = "Point",
});
schema.Properties.Add("coordinates", new JsonSchemaProperty
{
Type = JsonObjectType.Array,
Item = new JsonSchema
{
Type = JsonObjectType.Number
},
IsRequired = true,
Example = new double[] { 8.541694, 47.376888 },
MinItems = 2,
MaxItems = 3,
Description = "A position is an array of numbers. There MUST be two or more elements. The first two elements are longitude and latitude, or easting and northing, precisely in that order and using decimal numbers. Altitude or elevation MAY be included as an optional third element."
});
if (!hasSchema)
{
context.JsonSchemaResolver.AddSchema(typeof(Point), false, schema);
}
}
}
public Type MappedType => typeof(Point);
public bool UseReference => true;
}
The excludedTypeNames are no longer necessary. This approach gives you a correct JSON (by o.JsonSerializerOptions.Converters.Add(new NetTopologySuite.IO.Converters.GeoJsonConverterFactory())
) and a API definition which is for this usecase correct.
Please use with caution, in the final production version I never exposed NetTopologySuite objects but copied the info to an own external representation.
Thanks @RobSlgm for your your example! I wrote type mappers for almost the complete spec even if i still have to exclude them on the client side since the use of geojson.js (And despite the generated open api spec looks the same as the reference, the client is generated differently, but this is another problem). But it makes the Swagger UI more useful.
I got things working for me and this issue is therefore resolved. I'm not sure whether something with the reflection stuff is broken or if this is an intended / not relevant behaviour tho.
I'm in the process of upgrading a .NET 7 based solution which uses Nswag assembly scanning to .NET 8 with the .csproj approach.
I get an error with our use of NetTopologySuite geometries on the upgraded solution:
For testing purposes, i switched the .NET 7 version over to the .csproj approach where it worked.
I prepared a minimal sample repository with 2 projects (MsBuild AfterBuildTarget):
https://github.com/rootix/NJsonSchemaIssue
Everything except the updated .NET version and the updated Packages (Nswag 14 Preview 12) is the same.
Am i missing something? I tried to add "Coordinate" to the ExcludedTypeNames, but with no luck. And it worked in the older version anyway.
Thanks for the help!