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
23 changes: 11 additions & 12 deletions src/Microsoft.OpenApi/Models/OpenApiSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version
}

// type
var serializedTypeProperty = TrySerializeTypeProperty(writer, version);
SerializeTypeProperty(writer, version);

// allOf
writer.WriteOptionalCollection(OpenApiConstants.AllOf, AllOf, callback);
Expand Down Expand Up @@ -595,13 +595,13 @@ private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version
writer.WriteOptionalObject(OpenApiConstants.Default, Default, (w, d) => w.WriteAny(d));

// nullable
if (version == OpenApiSpecVersion.OpenApi3_0 && serializedTypeProperty)
if (version == OpenApiSpecVersion.OpenApi3_0)
{
// https://spec.openapis.org/oas/v3.0.4.html#fixed-fields-20
// This keyword only takes effect if type is explicitly defined within the same Schema Object.
//
// If the user explicitly set IsNullable to true, we serialize it even if redundant.
// But if **we** are inferring it (from oneOf/anyOf), we don't serialize it when it's redundant.
// We don't care to avoid an unnecessary serialization.
// So, we attempt to serialize it regardless of whether or not a type property was serialized.
SerializeNullable(writer, version);
}

Expand Down Expand Up @@ -838,7 +838,7 @@ private void SerializeAsV2(
writer.WriteStartObject();

// type
TrySerializeTypeProperty(writer, OpenApiSpecVersion.OpenApi2_0);
SerializeTypeProperty(writer, OpenApiSpecVersion.OpenApi2_0);

// description
writer.WriteProperty(OpenApiConstants.Description, Description);
Expand Down Expand Up @@ -1006,14 +1006,13 @@ private void SerializeAsV2(
writer.WriteEndObject();
}

private bool TrySerializeTypeProperty(IOpenApiWriter writer, OpenApiSpecVersion version, JsonSchemaType? inferredType = null)
private void SerializeTypeProperty(IOpenApiWriter writer, OpenApiSpecVersion version)
{
// Use original type or inferred type when the explicit type is not set
var typeToUse = Type ?? inferredType;
var typeToUse = Type;

if (typeToUse is null)
{
return false;
return;
}

switch (version)
Expand All @@ -1023,15 +1022,15 @@ private bool TrySerializeTypeProperty(IOpenApiWriter writer, OpenApiSpecVersion
if (typeWithoutNull != 0 && !HasMultipleTypes(typeWithoutNull))
{
writer.WriteProperty(OpenApiConstants.Type, typeWithoutNull.ToFirstIdentifier());
return true;
return;
}
break;
default:
WriteUnifiedSchemaType(typeToUse.Value, writer);
return true;
return;
}

return false;
return;
}

private JsonNode? GetCompatibilityExample()
Expand Down
8 changes: 7 additions & 1 deletion src/Microsoft.OpenApi/Reader/JsonNodeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,13 @@ public static List<JsonNode> CreateListOfAny(this JsonNode? node, ParsingContext
throw new OpenApiReaderException("Cannot create a list from this type of node.", context);
}

return jsonArray.OfType<JsonNode>().ToList();
var list = new List<JsonNode>(jsonArray.Count);
foreach (var element in jsonArray)
{
list.Add(element ?? JsonNullSentinel.JsonNull);
}

return list;
}

public static List<T> CreateSimpleList<T>(this JsonNode? node, Func<JsonNode, OpenApiDocument?, T> map, OpenApiDocument? openApiDocument, ParsingContext context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,13 @@ public static IOpenApiSchema LoadSchema(JsonNode node, OpenApiDocument hostDocum
}
}

if (schema.Type is null && schema.Enum is { Count: 1 } &&
schema.Enum[0].IsJsonNullSentinel())
{
schema.Enum = null;
schema.Type = JsonSchemaType.Null;
}

return schema;
}
}
Expand Down
24 changes: 16 additions & 8 deletions test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -965,7 +965,8 @@ public async Task SerializeOneOfWithNullAsV3ShouldUseNullableAsync()
{
"enum": [
null
]
],
"nullable": true
},
{
"maxLength": 10,
Expand Down Expand Up @@ -1009,7 +1010,8 @@ public async Task SerializeOneOfWithNullAndMultipleSchemasAsV3ShouldMarkItAsNull
{
"enum": [
null
]
],
"nullable": true
},
{
"type": "string"
Expand Down Expand Up @@ -1061,7 +1063,8 @@ public async Task SerializeAnyOfWithNullAsV3ShouldUseNullableAsync()
{
"enum": [
null
]
],
"nullable": true
},
{
"type": "object",
Expand Down Expand Up @@ -1108,7 +1111,8 @@ public async Task SerializeAnyOfWithNullAndMultipleSchemasAsV3ShouldApplyNullabl
{
"enum": [
null
]
],
"nullable": true
},
{
"minLength": 1,
Expand Down Expand Up @@ -1153,7 +1157,8 @@ public async Task SerializeOneOfWithOnlyNullAsV3ShouldJustBeNullableAsync()
{
"enum": [
null
]
],
"nullable": true
}
]
}
Expand Down Expand Up @@ -1256,7 +1261,8 @@ public async Task SerializeOneOfWithNullAndRefAsV3ShouldUseNullableAsync()
{
"enum": [
null
]
],
"nullable": true
},
{
"$ref": "#/components/schemas/Pet"
Expand Down Expand Up @@ -2050,7 +2056,8 @@ public async Task SerializeNullableEnumWith3_0()
{
"enum": [
null
]
],
"nullable": true
},
{
"enum": [
Expand Down Expand Up @@ -2099,7 +2106,8 @@ public async Task SerializeNullableTypeWith3_0()
{
"enum": [
null
]
],
"nullable": true
}
""";

Expand Down
Loading