diff --git a/src/Microsoft.OpenApi/Models/OpenApiConstants.cs b/src/Microsoft.OpenApi/Models/OpenApiConstants.cs
index db8aa1d62..3cfc9f357 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiConstants.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiConstants.cs
@@ -755,6 +755,11 @@ public static class OpenApiConstants
///
public const string ExamplesExtension = "x-examples";
+ ///
+ /// Extension: x-jsonschema-examples
+ ///
+ public const string JsonSchemaExamplesExtension = "x-jsonschema-examples";
+
///
/// Field: version3_0_0
///
diff --git a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs
index 814dc1923..b94ab74eb 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs
@@ -615,7 +615,10 @@ private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version
writer.WriteOptionalObject(OpenApiConstants.ExternalDocs, ExternalDocs, callback);
// example
- writer.WriteOptionalObject(OpenApiConstants.Example, Example, (w, e) => w.WriteAny(e));
+ writer.WriteOptionalObject(
+ OpenApiConstants.Example,
+ version < OpenApiSpecVersion.OpenApi3_1 ? GetCompatibilityExample() : Example,
+ (w, e) => w.WriteAny(e));
// deprecated
writer.WriteProperty(OpenApiConstants.Deprecated, Deprecated, false);
@@ -741,6 +744,7 @@ private void WriteV3CompatibilityKeywords(IOpenApiWriter writer, Action nodeWriter.WriteAny(s));
}
internal void WriteAsItemsProperties(IOpenApiWriter writer)
@@ -954,7 +958,7 @@ private void SerializeAsV2(
writer.WriteOptionalObject(OpenApiConstants.ExternalDocs, ExternalDocs, (w, s) => s.SerializeAsV2(w));
// example
- writer.WriteOptionalObject(OpenApiConstants.Example, Example, (w, e) => w.WriteAny(e));
+ writer.WriteOptionalObject(OpenApiConstants.Example, GetCompatibilityExample(), (w, e) => w.WriteAny(e));
// x-nullable extension
SerializeNullable(writer, OpenApiSpecVersion.OpenApi2_0);
@@ -985,6 +989,8 @@ private void SerializeAsV2(
writer.WriteOptionalMap(OpenApiConstants.PatternPropertiesExtension, PatternProperties, (w, s) => s.SerializeAsV2(w));
}
+ writer.WriteOptionalCollection(OpenApiConstants.JsonSchemaExamplesExtension, GetCompatibilityExamplesExtension(), (nodeWriter, s) => nodeWriter.WriteAny(s));
+
// extensions
writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi2_0);
@@ -1019,6 +1025,26 @@ private bool TrySerializeTypeProperty(IOpenApiWriter writer, OpenApiSpecVersion
return false;
}
+ private JsonNode? GetCompatibilityExample()
+ {
+ return Example ?? Examples?.FirstOrDefault();
+ }
+
+ private IEnumerable? GetCompatibilityExamplesExtension()
+ {
+ if (Examples is null || Examples.Count == 0)
+ {
+ return null;
+ }
+
+ if (Example is not null)
+ {
+ return Examples;
+ }
+
+ return Examples.Count > 1 ? Examples.Skip(1) : null;
+ }
+
private static bool IsPowerOfTwo(int x)
{
return x != 0 && (x & (x - 1)) == 0;
diff --git a/src/Microsoft.OpenApi/PublicAPI.Unshipped.txt b/src/Microsoft.OpenApi/PublicAPI.Unshipped.txt
index e763311ca..524d86c60 100644
--- a/src/Microsoft.OpenApi/PublicAPI.Unshipped.txt
+++ b/src/Microsoft.OpenApi/PublicAPI.Unshipped.txt
@@ -1,2 +1,3 @@
#nullable enable
const Microsoft.OpenApi.OpenApiConstants.OaiLicenseIdentifier = "x-oai-license-identifier" -> string!
+const Microsoft.OpenApi.OpenApiConstants.JsonSchemaExamplesExtension = "x-jsonschema-examples" -> string!
diff --git a/src/Microsoft.OpenApi/Reader/V2/OpenApiSchemaDeserializer.cs b/src/Microsoft.OpenApi/Reader/V2/OpenApiSchemaDeserializer.cs
index af7f33d9c..25d36eff1 100644
--- a/src/Microsoft.OpenApi/Reader/V2/OpenApiSchemaDeserializer.cs
+++ b/src/Microsoft.OpenApi/Reader/V2/OpenApiSchemaDeserializer.cs
@@ -267,6 +267,10 @@ internal static partial class OpenApiV2Deserializer
"example",
(o, n, _, _) => o.Example = n
},
+ {
+ OpenApiConstants.JsonSchemaExamplesExtension,
+ (o, n, _, c) => o.Examples = n.CreateListOfAny(c)
+ },
{
OpenApiConstants.PatternPropertiesExtension,
(o, n, t, c) => o.PatternProperties = n.CreateMap(LoadSchema, t, c)
diff --git a/src/Microsoft.OpenApi/Reader/V3/OpenApiSchemaDeserializer.cs b/src/Microsoft.OpenApi/Reader/V3/OpenApiSchemaDeserializer.cs
index 48dde9d5e..9b85fcf51 100644
--- a/src/Microsoft.OpenApi/Reader/V3/OpenApiSchemaDeserializer.cs
+++ b/src/Microsoft.OpenApi/Reader/V3/OpenApiSchemaDeserializer.cs
@@ -280,6 +280,10 @@ internal static partial class OpenApiV3Deserializer
"example",
(o, n, _, _) => o.Example = n
},
+ {
+ OpenApiConstants.JsonSchemaExamplesExtension,
+ (o, n, _, c) => o.Examples = n.CreateListOfAny(c)
+ },
{
"deprecated",
(o, n, _, _) =>
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs
index 14ec467aa..1b2a313d5 100644
--- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs
@@ -1746,6 +1746,82 @@ public async Task SerializePatternPropertiesAsExtensionInEarlierVersions(OpenApi
Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual)));
}
+ [Theory]
+ [InlineData(OpenApiSpecVersion.OpenApi2_0)]
+ [InlineData(OpenApiSpecVersion.OpenApi3_0)]
+ public async Task SerializeSingleExampleAsExampleInV2V3WhenExampleUnset(OpenApiSpecVersion version)
+ {
+ var expected = """
+ {
+ "example": "example value"
+ }
+ """;
+ var schema = new OpenApiSchema
+ {
+ Examples =
+ [
+ JsonValue.Create("example value")!
+ ]
+ };
+
+ var actual = await schema.SerializeAsJsonAsync(version);
+
+ Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual)));
+ }
+
+ [Theory]
+ [InlineData(OpenApiSpecVersion.OpenApi2_0)]
+ [InlineData(OpenApiSpecVersion.OpenApi3_0)]
+ public async Task SerializeMultipleExamplesInV2V3WhenExampleUnset(OpenApiSpecVersion version)
+ {
+ var expected = """
+ {
+ "example": "example value",
+ "x-jsonschema-examples": [
+ 42
+ ]
+ }
+ """;
+ var schema = new OpenApiSchema
+ {
+ Examples =
+ [
+ JsonValue.Create("example value")!,
+ JsonValue.Create(42)!
+ ]
+ };
+
+ var actual = await schema.SerializeAsJsonAsync(version);
+
+ Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual)));
+ }
+
+ [Theory]
+ [InlineData(OpenApiSpecVersion.OpenApi3_1)]
+ public async Task SerializeExamplesAsJsonSchemaKeywordInV31AndLater(OpenApiSpecVersion version)
+ {
+ var expected = """
+ {
+ "examples": [
+ "example value",
+ 42
+ ]
+ }
+ """;
+ var schema = new OpenApiSchema
+ {
+ Examples =
+ [
+ JsonValue.Create("example value")!,
+ JsonValue.Create(42)!
+ ]
+ };
+
+ var actual = await schema.SerializeAsJsonAsync(version);
+
+ Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual)));
+ }
+
[Theory]
[InlineData(OpenApiSpecVersion.OpenApi2_0)]
[InlineData(OpenApiSpecVersion.OpenApi3_0)]
@@ -1837,6 +1913,76 @@ public void DeserializePatternPropertiesExtensionInV3AssignsPatternPropertiesPro
Assert.True(schema.Extensions is null || !schema.Extensions.ContainsKey("x-jsonschema-patternProperties"));
}
+ [Fact]
+ public void DeserializeExamplesExtensionInV2AssignsExamplesProperty()
+ {
+ var jsonContent = """
+ {
+ "swagger": "2.0",
+ "info": { "title": "Test", "version": "1.0" },
+ "paths": {},
+ "definitions": {
+ "TestSchema": {
+ "type": "string",
+ "example": "primary example",
+ "x-jsonschema-examples": [
+ "secondary example",
+ 42
+ ]
+ }
+ }
+ }
+ """;
+
+ var readResult = OpenApiDocument.Parse(jsonContent, "json");
+
+ Assert.Empty(readResult.Diagnostic.Errors);
+ var schema = readResult.Document.Components.Schemas["TestSchema"];
+ Assert.Equal("primary example", schema.Example?.GetValue());
+ Assert.NotNull(schema.Examples);
+ Assert.Collection(
+ schema.Examples,
+ example => Assert.Equal("secondary example", example.GetValue()),
+ example => Assert.Equal(42, example.GetValue()));
+ Assert.True(schema.Extensions is null || !schema.Extensions.ContainsKey(OpenApiConstants.JsonSchemaExamplesExtension));
+ }
+
+ [Fact]
+ public void DeserializeExamplesExtensionInV3AssignsExamplesProperty()
+ {
+ var jsonContent = """
+ {
+ "openapi": "3.0.0",
+ "info": { "title": "Test", "version": "1.0" },
+ "paths": {},
+ "components": {
+ "schemas": {
+ "TestSchema": {
+ "type": "string",
+ "example": "primary example",
+ "x-jsonschema-examples": [
+ "secondary example",
+ 42
+ ]
+ }
+ }
+ }
+ }
+ """;
+
+ var readResult = OpenApiDocument.Parse(jsonContent, "json");
+
+ Assert.Empty(readResult.Diagnostic.Errors);
+ var schema = readResult.Document.Components.Schemas["TestSchema"];
+ Assert.Equal("primary example", schema.Example?.GetValue());
+ Assert.NotNull(schema.Examples);
+ Assert.Collection(
+ schema.Examples,
+ example => Assert.Equal("secondary example", example.GetValue()),
+ example => Assert.Equal(42, example.GetValue()));
+ Assert.True(schema.Extensions is null || !schema.Extensions.ContainsKey(OpenApiConstants.JsonSchemaExamplesExtension));
+ }
+
[Fact]
public void DeserializeContainsExtensionsInV3AssignsContainsProperties()
{