Skip to content
Merged
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
2 changes: 1 addition & 1 deletion ConsoleApp/ConsoleApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.10" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Text.Json" Version="9.0.7" />
<PackageReference Include="System.Text.Json" Version="9.0.10" />
</ItemGroup>

<ItemGroup>
Expand Down
6 changes: 3 additions & 3 deletions ReflectorNet.Tests/ReflectorNet.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="System.Text.Json" Version="9.0.7" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageReference Include="System.Text.Json" Version="9.0.10" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5" />
</ItemGroup>

<ItemGroup>
Expand Down
13 changes: 11 additions & 2 deletions ReflectorNet.Tests/SchemaTests/CollectionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,18 @@ public void GetTypeId_SimpleArray_ShouldAppendArray()
// Assert
Assert.NotNull(result);
Assert.NotNull(result[JsonSchema.Type]);
Assert.Equal(JsonSchema.Array, result[JsonSchema.Type]!.ToString());

Assert.NotNull(result[JsonSchema.Items]);
if (TypeUtils.IsDictionary(type))
{
Assert.Equal(JsonSchema.Object, result[JsonSchema.Type]!.ToString());
Assert.NotNull(result[JsonSchema.AdditionalProperties]);
}
else
{
Assert.Equal(JsonSchema.Array, result[JsonSchema.Type]!.ToString());
Assert.NotNull(result[JsonSchema.Items]);
}


Assert.Null(result[nameof(ListType.shouldBeIgnored)]);
Assert.Null(result[nameof(ListType.ShouldBeIgnored)]);
Expand Down
24 changes: 24 additions & 0 deletions ReflectorNet.Tests/SchemaTests/SchemaTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ namespace com.IvanMurzak.ReflectorNet.Tests.SchemaTests
{
public abstract class SchemaTestBase : BaseTest
{
static readonly Type[] RestrictedDefineTypes = new Type[]
{
typeof(string),
typeof(object),
typeof(DateTime),
typeof(DateTimeOffset),
typeof(Guid),
typeof(TimeSpan),
typeof(Uri)
};
protected SchemaTestBase(ITestOutputHelper output) : base(output)
{
}
Expand Down Expand Up @@ -264,6 +274,13 @@ protected void AssertAllRefsDefined(JsonNode schema)
return;
}

// References don't include restricted types
foreach (var reference in allReferences)
{
Assert.False(RestrictedDefineTypes.Any(x => JsonSchema.RefValue + x.GetSchemaTypeId() == reference),
$"Reference '{reference}' is for a restricted type that should not appear as a $ref.");
}

// Schema must have $defs if there are references
Assert.True(schema.AsObject().ContainsKey(JsonSchema.Defs),
$"Schema contains {allReferences.Count} $ref reference(s) but no $defs section. References: {string.Join(", ", allReferences)}");
Expand All @@ -281,6 +298,13 @@ protected void AssertAllRefsDefined(JsonNode schema)
$"Reference '{reference}' (type ID: '{typeId}') is not defined in $defs. " +
$"Available definitions: {string.Join(", ", defines.Select(d => d.Key))}");
}

// Defines don't include restricted types
foreach (var define in defines)
{
Assert.False(RestrictedDefineTypes.Any(x => x.GetSchemaTypeId() == define.Key),
$"Reference '{define.Key}' is for a restricted type that should not appear as a $ref.");
}
}

/// <summary>
Expand Down
4 changes: 2 additions & 2 deletions ReflectorNet/ReflectorNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.7" />
<PackageReference Include="System.Text.Json" Version="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.10" />
<PackageReference Include="System.Text.Json" Version="9.0.10" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ public SerializedMemberConverter(Reflector reflector)
public override IEnumerable<Type> GetDefinedTypes()
{
yield return typeof(SerializedMemberList);
yield return typeof(SerializedMember);
}

public override SerializedMember? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public SerializedMemberListConverter(Reflector reflector)
};
public override IEnumerable<Type> GetDefinedTypes()
{
yield return typeof(SerializedMemberList);
yield return typeof(SerializedMember);
}

Expand Down
8 changes: 3 additions & 5 deletions ReflectorNet/src/Utils/Json/JsonSchema.Internal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,6 @@ JsonNode GenerateSchemaFromType(Reflector reflector, Type type, JsonObject defin
var required = new JsonArray();
var schema = new JsonObject { [Type] = Object };

defines ??= new();

// Get serializable fields
var fields = reflector.GetSerializableFields(type);
if (fields != null)
Expand Down Expand Up @@ -231,13 +229,13 @@ JsonNode GenerateSchemaFromType(Reflector reflector, Type type, JsonObject defin
}
}

if (properties.Count > 0)
schema[Properties] = properties;

// Add required array if it has items
if (required.Count > 0)
schema[Required] = required;

if (properties.Count > 0)
schema[Properties] = properties;

return schema;
}

Expand Down
32 changes: 30 additions & 2 deletions ReflectorNet/src/Utils/Json/JsonSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,31 @@ public JsonNode GetSchema(Reflector reflector, Type type, JsonObject? defines =
defines[defTypeId] = def;
}
}
else if (TypeUtils.IsDictionary(type))
{
var genericArgs = TypeUtils.GetDictionaryGenericArguments(type);
if (genericArgs == null)
throw new InvalidOperationException($"Unable to get generic arguments for dictionary type '{type.GetTypeName(pretty: false)}'.");

foreach (var genericArgument in genericArgs)
{
if (TypeUtils.IsPrimitive(genericArgument))
continue;

var defTypeId = genericArgument.GetSchemaTypeId();
if (defines.ContainsKey(defTypeId))
continue;

var defSchema = GetSchema(reflector, genericArgument, defines);
if (defSchema != null)
defines[defTypeId] = defSchema;
}

if (definesNeeded && !defineContainsType)
defines[typeId] = schema;

schema = new JsonObject { [Type] = Object, [AdditionalProperties] = true };
Comment thread
IvanMurzak marked this conversation as resolved.
}
else
{
schema = GenerateSchemaFromType(reflector, type, defines);
Expand Down Expand Up @@ -530,8 +555,7 @@ public JsonNode GenerateSchema(
var schema = new JsonObject
{
// [SchemaDraft] = JsonValue.Create(SchemaDraftValue),
[Type] = Object,
[Properties] = properties
[Type] = Object
};

foreach (var parameter in types)
Expand Down Expand Up @@ -588,8 +612,12 @@ public JsonNode GenerateSchema(
}
}

if (properties.Count > 0)
schema[Properties] = properties;

if (defines.Count > 0 && needToAddDefines)
schema[Defs] = defines;

if (required.Count > 0)
schema[Required] = required;

Expand Down
26 changes: 26 additions & 0 deletions ReflectorNet/src/Utils/TypeUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,33 @@ public static partial class TypeUtils
return type;
}

public static bool IsDictionary(Type type)
{
if (type.IsGenericType &&
(type.GetGenericTypeDefinition() == typeof(Dictionary<,>) ||
type.GetGenericTypeDefinition() == typeof(IDictionary<,>)))
{
return true;
}

return type.GetInterfaces()
.Any(i => i.IsGenericType && (i.GetGenericTypeDefinition() == typeof(IDictionary<,>)));
}

public static Type[]? GetDictionaryGenericArguments(Type type)
{
if (type.IsGenericType &&
(type.GetGenericTypeDefinition() == typeof(Dictionary<,>) ||
type.GetGenericTypeDefinition() == typeof(IDictionary<,>)))
{
return type.GetGenericArguments();
}

var dictionaryInterface = type.GetInterfaces()
.FirstOrDefault(i => i.IsGenericType && (i.GetGenericTypeDefinition() == typeof(IDictionary<,>)));

return dictionaryInterface?.GetGenericArguments();
}

public static string? GetDescription(Type type)
{
Expand Down