diff --git a/src/Microsoft.OpenApi/Extensions/DictionaryExtensions.cs b/src/Microsoft.OpenApi/Extensions/DictionaryExtensions.cs
new file mode 100644
index 000000000..30e5aea3a
--- /dev/null
+++ b/src/Microsoft.OpenApi/Extensions/DictionaryExtensions.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.OpenApi.Extensions
+{
+ ///
+ /// Dictionary extension methods.
+ ///
+ public static class DictionaryExtensions
+ {
+ ///
+ /// Returns a new dictionary with entries sorted by key using the default comparer.
+ ///
+ public static SortedDictionary Sort(
+ this Dictionary source)
+ where TKey : notnull
+ {
+ Utils.CheckArgumentNull(source);
+
+ return new SortedDictionary(source);
+ }
+
+ ///
+ /// Returns a new dictionary with entries sorted by key using a custom comparer.
+ ///
+ public static SortedDictionary Sort(
+ this Dictionary source,
+ IComparer comparer)
+ where TKey : notnull
+ {
+ Utils.CheckArgumentNull(source);
+ Utils.CheckArgumentNull(comparer);
+
+ return new SortedDictionary(source, comparer);
+ }
+ }
+}
diff --git a/test/Microsoft.OpenApi.Tests/Extensions/DictionaryExtensionsTests.cs b/test/Microsoft.OpenApi.Tests/Extensions/DictionaryExtensionsTests.cs
new file mode 100644
index 000000000..e00dba156
--- /dev/null
+++ b/test/Microsoft.OpenApi.Tests/Extensions/DictionaryExtensionsTests.cs
@@ -0,0 +1,108 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.OpenApi.Extensions;
+using Xunit;
+
+namespace Microsoft.OpenApi.Tests.Extensions
+{
+ public class DictionaryExtensionsTests
+ {
+ [Fact]
+ public void ShouldSortStringIntDictionaryInAscendingOrder()
+ {
+ var dict = new Dictionary { { "b", 2 }, { "a", 1 } };
+ var result = dict.Sort();
+ Assert.Equal(["a", "b"], result.Keys);
+ }
+
+ [Fact]
+ public void ShouldReturnEmptyDictionaryWhenSourceIsEmpty()
+ {
+ var dict = new Dictionary();
+ var result = dict.Sort();
+ Assert.Empty(result);
+ }
+
+ [Fact]
+ public void ShouldKeepOrderWhenDictionaryIsAlreadySorted()
+ {
+ var dict = new Dictionary { { "a", 1 }, { "b", 2 } };
+ var result = dict.Sort();
+ Assert.Equal(["a", "b"], result.Keys);
+ }
+
+ [Fact]
+ public void ShouldSortNumericKeysNaturally()
+ {
+ var dict = new Dictionary { { 10, "a" }, { 1, "b" } };
+ var result = dict.Sort();
+ Assert.Equal([1, 10], result.Keys);
+ }
+
+ [Fact]
+ public void ShouldSortDateTimeKeysInAscendingOrder()
+ {
+ var now = DateTime.Now;
+ var later = now.AddHours(1);
+
+ var dict = new Dictionary
+ {
+ [later] = "future",
+ [now] = "present"
+ };
+
+ var result = dict.Sort();
+ Assert.Equal([now, later], result.Keys);
+ }
+
+ [Fact]
+ public void ShouldSortWithCustomDescendingComparer()
+ {
+ var dict = new Dictionary { { "a", 1 }, { "b", 2 } };
+ var result = dict.Sort(Comparer.Create((x, y) => y.CompareTo(x)));
+ Assert.Equal(["b", "a"], result.Keys);
+ }
+
+ [Fact]
+ public void ShouldSortDictionaryWithComplexValueTypes()
+ {
+ var dict = new Dictionary>
+ {
+ { "z", new HashSet { "value1" } },
+ { "a", new HashSet { "value2" } }
+ };
+
+ var result = dict.Sort();
+ Assert.Equal(["a", "z"], result.Keys);
+ Assert.Equal(new HashSet { "value2" }, result["a"]);
+ }
+
+ [Fact]
+ public void ShouldSortDictionaryWithNullValues()
+ {
+ var dict = new Dictionary
+ {
+ { "b", null },
+ { "a", "value" }
+ };
+
+ var result = dict.Sort();
+ Assert.Equal(["a", "b"], result.Keys);
+ Assert.Null(result["b"]);
+ }
+
+ [Fact]
+ public void ShouldSortDictionaryOfDictionariesByOuterKey()
+ {
+ var dict = new Dictionary>
+ {
+ ["z"] = new Dictionary { { "x", "1" } },
+ ["a"] = new Dictionary { { "y", "2" } }
+ };
+
+ var result = dict.Sort();
+ Assert.Equal(["a", "z"], result.Keys);
+ Assert.Equal("2", result["a"]["y"]);
+ }
+ }
+}
diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt
index 755a9e17e..38d9b4c59 100644
--- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt
+++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt
@@ -142,6 +142,13 @@ namespace Microsoft.OpenApi.Expressions
}
namespace Microsoft.OpenApi.Extensions
{
+ public static class DictionaryExtensions
+ {
+ public static System.Collections.Generic.SortedDictionary Sort(this System.Collections.Generic.Dictionary source)
+ where TKey : notnull { }
+ public static System.Collections.Generic.SortedDictionary Sort(this System.Collections.Generic.Dictionary source, System.Collections.Generic.IComparer comparer)
+ where TKey : notnull { }
+ }
public static class EnumExtensions
{
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2075", Justification="Fields are never trimmed for enum types.")]