Skip to content

Commit

Permalink
Add SelectMany extension method for Json (#859)
Browse files Browse the repository at this point in the history
* Add SelectMany extension method for Json

* --
  • Loading branch information
StefH authored Dec 7, 2024
1 parent 7176add commit 3ea784d
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -591,6 +590,43 @@ public static JArray Select(this JArray source, Type resultType, string selector
}
#endregion Select

#region SelectMany
/// <summary>
/// Projects each element of a sequence to an <see cref="JArray"/> and combines the resulting sequences into one sequence.
/// </summary>
/// <param name="source">A sequence of values to project.</param>
/// <param name="selector">A projection string expression to apply to each element.</param>
/// <param name="args">An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings.</param>
/// <returns>A <see cref="JArray"/> whose elements are the result of invoking a one-to-many projection function on each element of the input sequence.</returns>
public static JArray SelectMany(this JArray source, string selector, params object?[] args)
{
return SelectMany(source, NewtonsoftJsonParsingConfig.Default, selector, args);
}

/// <summary>
/// Projects each element of a sequence to an <see cref="JArray"/> and combines the resulting sequences into one sequence.
/// </summary>
/// <param name="source">A sequence of values to project.</param>
/// <param name="config">The <see cref="NewtonsoftJsonParsingConfig"/>.</param>
/// <param name="selector">A projection string expression to apply to each element.</param>
/// <param name="args">An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings.</param>
/// <returns>A <see cref="JArray"/> whose elements are the result of invoking a one-to-many projection function on each element of the input sequence.</returns>
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
/// <summary>
/// Returns the only element of a sequence that satisfies a specified condition, and throws an exception if there
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -676,7 +675,7 @@ public static JsonDocument Select(this JsonDocument source, string selector, par
/// <param name="config">The <see cref="SystemTextJsonParsingConfig"/>.</param>
/// <param name="selector">A projection string expression to apply to each element.</param>
/// <param name="args">An object array that contains zero or more objects to insert into the predicate as parameters.</param>
/// <returns>An <see cref="JsonElement"/> whose elements are the result of invoking a projection string on each element of source.</returns>
/// <returns>An <see cref="JsonDocument"/> whose elements are the result of invoking a projection string on each element of source.</returns>
public static JsonDocument Select(this JsonDocument source, SystemTextJsonParsingConfig config, string selector, params object?[] args)
{
Check.NotNull(source);
Expand Down Expand Up @@ -721,6 +720,38 @@ public static JsonDocument Select(this JsonDocument source, Type resultType, str
}
#endregion Select

#region SelectMany
/// <summary>
/// Projects each element of a sequence to an <see cref="JsonDocument"/> and combines the resulting sequences into one sequence.
/// </summary>
/// <param name="source">The source <see cref="JsonDocument"/></param>
/// <param name="selector">A projection string expression to apply to each element.</param>
/// <param name="args">An object array that contains zero or more objects to insert into the predicate as parameters. </param>
/// <returns>A <see cref="JsonDocument"/> whose elements are the result of invoking a one-to-many projection function on each element of the input sequence.</returns>
public static JsonDocument SelectMany(this JsonDocument source, string selector, params object?[] args)
{
return SelectMany(source, SystemTextJsonParsingConfig.Default, selector, args);
}

/// <summary>
/// Projects each element of a sequence to an <see cref="JsonDocument"/> and combines the resulting sequences into one sequence.
/// </summary>
/// <param name="source">The source <see cref="JsonDocument"/></param>
/// <param name="config">The <see cref="SystemTextJsonParsingConfig"/>.</param>
/// <param name="selector">A projection string expression to apply to each element.</param>
/// <param name="args">An object array that contains zero or more objects to insert into the predicate as parameters.</param>
/// <returns>A <see cref="JsonDocument"/> whose elements are the result of invoking a one-to-many projection function on each element of the input sequence.</returns>
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
/// <summary>
/// Returns the only element of a sequence, and throws an exception if there
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>());
array.Should().BeEquivalentTo("123", "456", "789", "012");
}

[Fact]
public void Single()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down

0 comments on commit 3ea784d

Please sign in to comment.