diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs
index 5b1c69ac..89a6806d 100644
--- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs
+++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs
@@ -2,7 +2,6 @@
using System.Linq.Dynamic.Core.NewtonsoftJson.Config;
using System.Linq.Dynamic.Core.NewtonsoftJson.Extensions;
using System.Linq.Dynamic.Core.Validation;
-using System.Linq.Expressions;
using Newtonsoft.Json.Linq;
namespace System.Linq.Dynamic.Core.NewtonsoftJson;
@@ -550,7 +549,7 @@ public static JArray Select(this JArray source, NewtonsoftJsonParsingConfig conf
if (source.Count == 0)
{
- return new JArray();
+ return [];
}
var queryable = ToQueryable(source, config);
@@ -591,6 +590,43 @@ public static JArray Select(this JArray source, Type resultType, string selector
}
#endregion Select
+ #region SelectMany
+ ///
+ /// Projects each element of a sequence to an and combines the resulting sequences into one sequence.
+ ///
+ /// A sequence of values to project.
+ /// A projection string expression to apply to each element.
+ /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings.
+ /// A whose elements are the result of invoking a one-to-many projection function on each element of the input sequence.
+ public static JArray SelectMany(this JArray source, string selector, params object?[] args)
+ {
+ return SelectMany(source, NewtonsoftJsonParsingConfig.Default, selector, args);
+ }
+
+ ///
+ /// Projects each element of a sequence to an and combines the resulting sequences into one sequence.
+ ///
+ /// A sequence of values to project.
+ /// The .
+ /// A projection string expression to apply to each element.
+ /// An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings.
+ /// A whose elements are the result of invoking a one-to-many projection function on each element of the input sequence.
+ public static JArray SelectMany(this JArray source, NewtonsoftJsonParsingConfig config, string selector, params object?[] args)
+ {
+ Check.NotNull(source);
+ Check.NotNull(config);
+ Check.NotNullOrEmpty(selector);
+
+ if (source.Count == 0)
+ {
+ return [];
+ }
+
+ var queryable = ToQueryable(source, config);
+ return ToJArray(() => queryable.SelectMany(config, selector, args));
+ }
+ #endregion SelectMany
+
#region Single
///
/// Returns the only element of a sequence that satisfies a specified condition, and throws an exception if there
diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs
index 48ccc581..a14a468e 100644
--- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs
+++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs
@@ -4,7 +4,6 @@
using System.Linq.Dynamic.Core.SystemTextJson.Extensions;
using System.Linq.Dynamic.Core.SystemTextJson.Utils;
using System.Linq.Dynamic.Core.Validation;
-using System.Linq.Expressions;
using System.Text.Json;
namespace System.Linq.Dynamic.Core.SystemTextJson;
@@ -676,7 +675,7 @@ public static JsonDocument Select(this JsonDocument source, string selector, par
/// The .
/// A projection string expression to apply to each element.
/// An object array that contains zero or more objects to insert into the predicate as parameters.
- /// An whose elements are the result of invoking a projection string on each element of source.
+ /// An whose elements are the result of invoking a projection string on each element of source.
public static JsonDocument Select(this JsonDocument source, SystemTextJsonParsingConfig config, string selector, params object?[] args)
{
Check.NotNull(source);
@@ -721,6 +720,38 @@ public static JsonDocument Select(this JsonDocument source, Type resultType, str
}
#endregion Select
+ #region SelectMany
+ ///
+ /// Projects each element of a sequence to an and combines the resulting sequences into one sequence.
+ ///
+ /// The source
+ /// A projection string expression to apply to each element.
+ /// An object array that contains zero or more objects to insert into the predicate as parameters.
+ /// A whose elements are the result of invoking a one-to-many projection function on each element of the input sequence.
+ public static JsonDocument SelectMany(this JsonDocument source, string selector, params object?[] args)
+ {
+ return SelectMany(source, SystemTextJsonParsingConfig.Default, selector, args);
+ }
+
+ ///
+ /// Projects each element of a sequence to an and combines the resulting sequences into one sequence.
+ ///
+ /// The source
+ /// The .
+ /// A projection string expression to apply to each element.
+ /// An object array that contains zero or more objects to insert into the predicate as parameters.
+ /// A whose elements are the result of invoking a one-to-many projection function on each element of the input sequence.
+ public static JsonDocument SelectMany(this JsonDocument source, SystemTextJsonParsingConfig config, string selector, params object?[] args)
+ {
+ Check.NotNull(source);
+ Check.NotNull(config);
+ Check.NotNullOrEmpty(selector);
+
+ var queryable = ToQueryable(source, config);
+ return ToJsonDocumentArray(() => queryable.SelectMany(config, selector, args));
+ }
+ #endregion SelectMany
+
#region Single
///
/// Returns the only element of a sequence, and throws an exception if there
diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs
index debfb4b7..dafa0373 100644
--- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs
+++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs
@@ -323,6 +323,37 @@ public void Select_ResultType()
array.Should().ContainInOrder(1, 4, 9);
}
+ [Fact]
+ public void SelectMany()
+ {
+ // Arrange
+ var json =
+ """
+ [{
+ "PhoneNumbers": [
+ { "Number": "123" },
+ { "Number": "456" }
+ ]
+ },
+ {
+ "PhoneNumbers": [
+ { "Number": "789" },
+ { "Number": "012" }
+ ]
+ }]
+ """;
+ var source = JArray.Parse(json);
+
+ // Act
+ var result = source
+ .SelectMany("PhoneNumbers")
+ .Select("Number");
+
+ // Assert
+ var array = result.Select(x => x.Value());
+ array.Should().BeEquivalentTo("123", "456", "789", "012");
+ }
+
[Fact]
public void Single()
{
diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs
index ca337755..7041ad7c 100644
--- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs
+++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs
@@ -320,6 +320,37 @@ public void Select()
array.Should().BeEquivalentTo("John", "Doe");
}
+ [Fact]
+ public void SelectMany()
+ {
+ // Arrange
+ var json =
+ """
+ [{
+ "PhoneNumbers": [
+ { "Number": "123" },
+ { "Number": "456" }
+ ]
+ },
+ {
+ "PhoneNumbers": [
+ { "Number": "789" },
+ { "Number": "012" }
+ ]
+ }]
+ """;
+ var source = JsonDocument.Parse(json);
+
+ // Act
+ var result = source
+ .SelectMany("PhoneNumbers")
+ .Select("Number");
+
+ // Assert
+ var array = result.RootElement.EnumerateArray().Select(x => x.GetString());
+ array.Should().BeEquivalentTo("123", "456", "789", "012");
+ }
+
[Fact]
public void Single()
{