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
68 changes: 47 additions & 21 deletions src/Microsoft.OpenApi/Models/OpenApiSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Helpers;
using Microsoft.OpenApi.Interfaces;
Expand Down Expand Up @@ -356,12 +358,6 @@ private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version
// default
writer.WriteOptionalObject(OpenApiConstants.Default, Default, (w, d) => w.WriteAny(d));

// nullable
if (version is OpenApiSpecVersion.OpenApi3_0)
{
writer.WriteProperty(OpenApiConstants.Nullable, Nullable, false);
}

// discriminator
writer.WriteOptionalObject(OpenApiConstants.Discriminator, Discriminator, callback);

Expand Down Expand Up @@ -636,20 +632,39 @@ private void SerializeAsV2(

private void SerializeTypeProperty(JsonSchemaType? type, IOpenApiWriter writer, OpenApiSpecVersion version)
{
// check whether nullable is true for upcasting purposes
var isNullable = Nullable ||
(Type.HasValue && Type.Value.HasFlag(JsonSchemaType.Null)) ||
Extensions is not null &&
Extensions.TryGetValue(OpenApiConstants.NullableExtension, out var nullExtRawValue) &&
nullExtRawValue is OpenApiAny { Node: JsonNode jsonNode} &&
jsonNode.GetValueKind() is JsonValueKind.True;
if (type is null)
{
return;
}
if (!HasMultipleTypes(type.Value))
{
// check whether nullable is true for upcasting purposes
if (version is OpenApiSpecVersion.OpenApi3_1 && (Nullable || Extensions.ContainsKey(OpenApiConstants.NullableExtension)))
if (version is OpenApiSpecVersion.OpenApi3_0 && isNullable)
{
UpCastSchemaTypeToV31(type, writer);
writer.WriteProperty(OpenApiConstants.Nullable, true);
}
else
}
else if (!HasMultipleTypes(type.Value))
{

switch (version)
{
writer.WriteProperty(OpenApiConstants.Type, type.Value.ToIdentifier());
case OpenApiSpecVersion.OpenApi3_1 when isNullable:
UpCastSchemaTypeToV31(type.Value, writer);
break;
case OpenApiSpecVersion.OpenApi3_0 when isNullable && type.Value == JsonSchemaType.Null:
writer.WriteProperty(OpenApiConstants.Nullable, true);
writer.WriteProperty(OpenApiConstants.Type, JsonSchemaType.Object.ToIdentifier());
break;
case OpenApiSpecVersion.OpenApi3_0 when isNullable && type.Value != JsonSchemaType.Null:
writer.WriteProperty(OpenApiConstants.Nullable, true);
writer.WriteProperty(OpenApiConstants.Type, type.Value.ToIdentifier());
break;
default:
writer.WriteProperty(OpenApiConstants.Type, type.Value.ToIdentifier());
break;
}
}
else
Expand All @@ -664,6 +679,10 @@ private void SerializeTypeProperty(JsonSchemaType? type, IOpenApiWriter writer,
var list = (from JsonSchemaType flag in jsonSchemaTypeValues
where type.Value.HasFlag(flag)
select flag).ToList();
if (Nullable && !list.Contains(JsonSchemaType.Null))
{
list.Add(JsonSchemaType.Null);
}
writer.WriteOptionalCollection(OpenApiConstants.Type, list, (w, s) => w.WriteValue(s.ToIdentifier()));
}
}
Expand All @@ -681,14 +700,21 @@ private static bool HasMultipleTypes(JsonSchemaType schemaType)
schemaTypeNumeric != (int)JsonSchemaType.Null;
}

private void UpCastSchemaTypeToV31(JsonSchemaType? type, IOpenApiWriter writer)
private static void UpCastSchemaTypeToV31(JsonSchemaType type, IOpenApiWriter writer)
{
// create a new array and insert the type and "null" as values
Type = type | JsonSchemaType.Null;
var list = (from JsonSchemaType? flag in jsonSchemaTypeValues// Check if the flag is set in 'type' using a bitwise AND operation
where Type.Value.HasFlag(flag)
var temporaryType = type | JsonSchemaType.Null;
var list = (from JsonSchemaType flag in jsonSchemaTypeValues// Check if the flag is set in 'type' using a bitwise AND operation
where temporaryType.HasFlag(flag)
select flag.ToIdentifier()).ToList();
writer.WriteOptionalCollection(OpenApiConstants.Type, list, (w, s) => w.WriteValue(s));
if (list.Count > 1)
{
writer.WriteOptionalCollection(OpenApiConstants.Type, list, (w, s) => w.WriteValue(s));
}
else
{
writer.WriteProperty(OpenApiConstants.Type, list[0]);
}
}

#if NET5_0_OR_GREATER
Expand All @@ -711,7 +737,7 @@ private void DowncastTypeArrayToV2OrV3(JsonSchemaType schemaType, IOpenApiWriter

if (!HasMultipleTypes(schemaType ^ JsonSchemaType.Null) && (schemaType & JsonSchemaType.Null) == JsonSchemaType.Null) // checks for two values and one is null
{
foreach (JsonSchemaType? flag in jsonSchemaTypeValues)
foreach (JsonSchemaType flag in jsonSchemaTypeValues)
{
// Skip if the flag is not set or if it's the Null flag
if (schemaType.HasFlag(flag) && flag != JsonSchemaType.Null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
"maximum": 42,
"minimum": 10,
"exclusiveMinimum": true,
"nullable": true,
"type": "integer",
"default": 15,
"nullable": true,
"externalDocs": {
"url": "http://example.com/externalDocs"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"title":"title1","multipleOf":3,"maximum":42,"minimum":10,"exclusiveMinimum":true,"type":"integer","default":15,"nullable":true,"externalDocs":{"url":"http://example.com/externalDocs"}}
{"title":"title1","multipleOf":3,"maximum":42,"minimum":10,"exclusiveMinimum":true,"nullable":true,"type":"integer","default":15,"externalDocs":{"url":"http://example.com/externalDocs"}}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
"maximum": 42,
"minimum": 10,
"exclusiveMinimum": true,
"nullable": true,
"type": "integer",
"default": 15,
"nullable": true,
"externalDocs": {
"url": "http://example.com/externalDocs"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"title":"title1","multipleOf":3,"maximum":42,"minimum":10,"exclusiveMinimum":true,"type":"integer","default":15,"nullable":true,"externalDocs":{"url":"http://example.com/externalDocs"}}
{"title":"title1","multipleOf":3,"maximum":42,"minimum":10,"exclusiveMinimum":true,"nullable":true,"type":"integer","default":15,"externalDocs":{"url":"http://example.com/externalDocs"}}
10 changes: 5 additions & 5 deletions test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,9 @@ public async Task SerializeAdvancedSchemaNumberAsV3JsonWorks()
"maximum": 42,
"minimum": 10,
"exclusiveMinimum": true,
"nullable": true,
"type": "integer",
"default": 15,
"nullable": true,
"externalDocs": {
"url": "http://example.com/externalDocs"
}
Expand All @@ -268,6 +268,7 @@ public async Task SerializeAdvancedSchemaObjectAsV3JsonWorks()
"""
{
"title": "title1",
"nullable": true,
"properties": {
"property1": {
"properties": {
Expand Down Expand Up @@ -296,7 +297,6 @@ public async Task SerializeAdvancedSchemaObjectAsV3JsonWorks()
}
}
},
"nullable": true,
"externalDocs": {
"url": "http://example.com/externalDocs"
}
Expand All @@ -320,6 +320,7 @@ public async Task SerializeAdvancedSchemaWithAllOfAsV3JsonWorks()
"""
{
"title": "title1",
"nullable": true,
"allOf": [
{
"title": "title2",
Expand All @@ -335,6 +336,7 @@ public async Task SerializeAdvancedSchemaWithAllOfAsV3JsonWorks()
},
{
"title": "title3",
"nullable": true,
"properties": {
"property3": {
"properties": {
Expand All @@ -347,11 +349,9 @@ public async Task SerializeAdvancedSchemaWithAllOfAsV3JsonWorks()
"minLength": 2,
"type": "string"
}
},
"nullable": true
}
}
],
"nullable": true,
"externalDocs": {
"url": "http://example.com/externalDocs"
}
Expand Down
Loading