Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,61 @@ public async Task When_JsonSchemaFlattenAttribute_is_used_on_class_then_inherite
Assert.True(schema.Definitions["B"].Properties.ContainsKey("Ccc"));
}

[JsonSchemaFlatten]
public class FlattenedChild : MiddleClass
{
public string Name { get; set; }
}

public class MiddleClass : BaseClass
{
public string FirstName { get; set; }
}

public class BaseClass
{
public string LastName { get; set; }
}

[Fact]
public async Task When_JsonSchemaFlattenAttribute_with_multiple_inheritance_levels_then_no_duplicate_properties()
{
// Arrange
var settings = new NewtonsoftJsonSchemaGeneratorSettings();

// Act
var schema = NewtonsoftJsonSchemaGenerator.FromType<FlattenedChild>(settings);
var data = schema.ToJson();

// Assert
Assert.True(schema.Properties.ContainsKey("Name"));
Assert.True(schema.Properties.ContainsKey("FirstName"));
Assert.True(schema.Properties.ContainsKey("LastName"));
Assert.Empty(schema.AllOf);
Assert.False(schema.Definitions.ContainsKey("MiddleClass"));
Assert.False(schema.Definitions.ContainsKey("BaseClass"));
}

[Fact]
public async Task When_FlattenInheritanceHierarchy_setting_with_multiple_levels_then_no_duplicate_properties()
{
// Arrange
var settings = new NewtonsoftJsonSchemaGeneratorSettings
{
FlattenInheritanceHierarchy = true
};

// Act - Note: FlattenedChild also has the attribute, but we test with the setting too
var schema = NewtonsoftJsonSchemaGenerator.FromType<FlattenedChild>(settings);
var data = schema.ToJson();

// Assert
Assert.True(schema.Properties.ContainsKey("Name"));
Assert.True(schema.Properties.ContainsKey("FirstName"));
Assert.True(schema.Properties.ContainsKey("LastName"));
Assert.Empty(schema.AllOf);
}

[Fact]
public async Task When_class_inherited_and_json_flattened_then_ignore_base_property_with_same_name()
{
Expand Down
23 changes: 22 additions & 1 deletion src/NJsonSchema/Generation/JsonSchemaGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -986,7 +986,7 @@ private void AddKnownType(Type type, JsonSchemaResolver schemaResolver)
if (!typeDescription.IsDictionary && !type.Type.IsArray)
{
Settings.ReflectionService.GenerateProperties(schema, baseType, Settings, this, schemaResolver);
var actualSchema = GenerateInheritance(baseType, schema, schemaResolver);
var actualSchema = FlattenAncestorProperties(baseType, schema, schemaResolver);

GenerateInheritanceDiscriminator(baseType, schema, actualSchema ?? schema);
}
Expand Down Expand Up @@ -1058,6 +1058,27 @@ private void AddKnownType(Type type, JsonSchemaResolver schemaResolver)
return null;
}

private JsonSchema? FlattenAncestorProperties(ContextualType type, JsonSchema schema, JsonSchemaResolver schemaResolver)
{
var baseType = type.BaseType;
if (baseType != null && baseType.Type != typeof(object) && baseType.Type != typeof(ValueType))
{
if (baseType.GetContextOrTypeAttributes(false).FirstAssignableToTypeNameOrDefault("JsonSchemaIgnoreAttribute", TypeNameStyle.Name) == null &&
baseType.GetContextOrTypeAttributes(false).FirstAssignableToTypeNameOrDefault("SwaggerIgnoreAttribute", TypeNameStyle.Name) == null &&
Settings.ExcludedTypeNames?.Contains(baseType.Type.FullName) != true)
{
var typeDescription = Settings.ReflectionService.GetDescription(baseType, Settings);
if (!typeDescription.IsDictionary && !type.Type.IsArray)
{
Settings.ReflectionService.GenerateProperties(schema, baseType, Settings, this, schemaResolver);
return FlattenAncestorProperties(baseType, schema, schemaResolver);
}
}
}

return null;
}

private void GenerateInheritanceDiscriminator(Type type, JsonSchema schema, JsonSchema typeSchema)
{
if (!Settings.GetActualFlattenInheritanceHierarchy(type))
Expand Down