diff --git a/ConsoleApp/ConsoleApp.csproj b/ConsoleApp/ConsoleApp.csproj
index 99a3af7..42d5456 100644
--- a/ConsoleApp/ConsoleApp.csproj
+++ b/ConsoleApp/ConsoleApp.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/ReflectorNet.Tests.OuterAssembly/ReflectorNet.Tests.OuterAssembly.csproj b/ReflectorNet.Tests.OuterAssembly/ReflectorNet.Tests.OuterAssembly.csproj
index aceedd7..51ca04f 100644
--- a/ReflectorNet.Tests.OuterAssembly/ReflectorNet.Tests.OuterAssembly.csproj
+++ b/ReflectorNet.Tests.OuterAssembly/ReflectorNet.Tests.OuterAssembly.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/ReflectorNet.Tests/ReflectorNet.Tests.csproj b/ReflectorNet.Tests/ReflectorNet.Tests.csproj
index a808c7b..8b3ce88 100644
--- a/ReflectorNet.Tests/ReflectorNet.Tests.csproj
+++ b/ReflectorNet.Tests/ReflectorNet.Tests.csproj
@@ -8,10 +8,10 @@
-
-
+
+
-
+
diff --git a/ReflectorNet.Tests/SchemaTests/CollectionsTests.cs b/ReflectorNet.Tests/SchemaTests/CollectionsTests.cs
index 81689cb..30868c5 100644
--- a/ReflectorNet.Tests/SchemaTests/CollectionsTests.cs
+++ b/ReflectorNet.Tests/SchemaTests/CollectionsTests.cs
@@ -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)]);
diff --git a/ReflectorNet.Tests/SchemaTests/SchemaTestBase.cs b/ReflectorNet.Tests/SchemaTests/SchemaTestBase.cs
index 845a9ac..b110387 100644
--- a/ReflectorNet.Tests/SchemaTests/SchemaTestBase.cs
+++ b/ReflectorNet.Tests/SchemaTests/SchemaTestBase.cs
@@ -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)
{
}
@@ -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)}");
@@ -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.");
+ }
}
///
diff --git a/ReflectorNet/ReflectorNet.csproj b/ReflectorNet/ReflectorNet.csproj
index 2948a2e..fb9102d 100644
--- a/ReflectorNet/ReflectorNet.csproj
+++ b/ReflectorNet/ReflectorNet.csproj
@@ -27,8 +27,8 @@
-
-
+
+
\ No newline at end of file
diff --git a/ReflectorNet/src/Convertor/Json/SerializedMemberConverter.cs b/ReflectorNet/src/Convertor/Json/SerializedMemberConverter.cs
index e458324..4f818b8 100644
--- a/ReflectorNet/src/Convertor/Json/SerializedMemberConverter.cs
+++ b/ReflectorNet/src/Convertor/Json/SerializedMemberConverter.cs
@@ -93,6 +93,7 @@ public SerializedMemberConverter(Reflector reflector)
public override IEnumerable GetDefinedTypes()
{
yield return typeof(SerializedMemberList);
+ yield return typeof(SerializedMember);
}
public override SerializedMember? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
diff --git a/ReflectorNet/src/Convertor/Json/SerializedMemberListConverter.cs b/ReflectorNet/src/Convertor/Json/SerializedMemberListConverter.cs
index 917d9ae..937f493 100644
--- a/ReflectorNet/src/Convertor/Json/SerializedMemberListConverter.cs
+++ b/ReflectorNet/src/Convertor/Json/SerializedMemberListConverter.cs
@@ -38,6 +38,7 @@ public SerializedMemberListConverter(Reflector reflector)
};
public override IEnumerable GetDefinedTypes()
{
+ yield return typeof(SerializedMemberList);
yield return typeof(SerializedMember);
}
diff --git a/ReflectorNet/src/Utils/Json/JsonSchema.Internal.cs b/ReflectorNet/src/Utils/Json/JsonSchema.Internal.cs
index 363d181..2158712 100644
--- a/ReflectorNet/src/Utils/Json/JsonSchema.Internal.cs
+++ b/ReflectorNet/src/Utils/Json/JsonSchema.Internal.cs
@@ -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)
@@ -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;
}
diff --git a/ReflectorNet/src/Utils/Json/JsonSchema.cs b/ReflectorNet/src/Utils/Json/JsonSchema.cs
index 455f5d2..28df10e 100644
--- a/ReflectorNet/src/Utils/Json/JsonSchema.cs
+++ b/ReflectorNet/src/Utils/Json/JsonSchema.cs
@@ -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 };
+ }
else
{
schema = GenerateSchemaFromType(reflector, type, defines);
@@ -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)
@@ -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;
diff --git a/ReflectorNet/src/Utils/TypeUtils.cs b/ReflectorNet/src/Utils/TypeUtils.cs
index 54c0c59..7cd07e3 100644
--- a/ReflectorNet/src/Utils/TypeUtils.cs
+++ b/ReflectorNet/src/Utils/TypeUtils.cs
@@ -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)
{