Skip to content

Commit 3ea784d

Browse files
authored
Add SelectMany extension method for Json (#859)
* Add SelectMany extension method for Json * --
1 parent 7176add commit 3ea784d

File tree

4 files changed

+133
-4
lines changed

4 files changed

+133
-4
lines changed

src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs

+38-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using System.Linq.Dynamic.Core.NewtonsoftJson.Config;
33
using System.Linq.Dynamic.Core.NewtonsoftJson.Extensions;
44
using System.Linq.Dynamic.Core.Validation;
5-
using System.Linq.Expressions;
65
using Newtonsoft.Json.Linq;
76

87
namespace System.Linq.Dynamic.Core.NewtonsoftJson;
@@ -550,7 +549,7 @@ public static JArray Select(this JArray source, NewtonsoftJsonParsingConfig conf
550549

551550
if (source.Count == 0)
552551
{
553-
return new JArray();
552+
return [];
554553
}
555554

556555
var queryable = ToQueryable(source, config);
@@ -591,6 +590,43 @@ public static JArray Select(this JArray source, Type resultType, string selector
591590
}
592591
#endregion Select
593592

593+
#region SelectMany
594+
/// <summary>
595+
/// Projects each element of a sequence to an <see cref="JArray"/> and combines the resulting sequences into one sequence.
596+
/// </summary>
597+
/// <param name="source">A sequence of values to project.</param>
598+
/// <param name="selector">A projection string expression to apply to each element.</param>
599+
/// <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>
600+
/// <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>
601+
public static JArray SelectMany(this JArray source, string selector, params object?[] args)
602+
{
603+
return SelectMany(source, NewtonsoftJsonParsingConfig.Default, selector, args);
604+
}
605+
606+
/// <summary>
607+
/// Projects each element of a sequence to an <see cref="JArray"/> and combines the resulting sequences into one sequence.
608+
/// </summary>
609+
/// <param name="source">A sequence of values to project.</param>
610+
/// <param name="config">The <see cref="NewtonsoftJsonParsingConfig"/>.</param>
611+
/// <param name="selector">A projection string expression to apply to each element.</param>
612+
/// <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>
613+
/// <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>
614+
public static JArray SelectMany(this JArray source, NewtonsoftJsonParsingConfig config, string selector, params object?[] args)
615+
{
616+
Check.NotNull(source);
617+
Check.NotNull(config);
618+
Check.NotNullOrEmpty(selector);
619+
620+
if (source.Count == 0)
621+
{
622+
return [];
623+
}
624+
625+
var queryable = ToQueryable(source, config);
626+
return ToJArray(() => queryable.SelectMany(config, selector, args));
627+
}
628+
#endregion SelectMany
629+
594630
#region Single
595631
/// <summary>
596632
/// Returns the only element of a sequence that satisfies a specified condition, and throws an exception if there

src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs

+33-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System.Linq.Dynamic.Core.SystemTextJson.Extensions;
55
using System.Linq.Dynamic.Core.SystemTextJson.Utils;
66
using System.Linq.Dynamic.Core.Validation;
7-
using System.Linq.Expressions;
87
using System.Text.Json;
98

109
namespace System.Linq.Dynamic.Core.SystemTextJson;
@@ -676,7 +675,7 @@ public static JsonDocument Select(this JsonDocument source, string selector, par
676675
/// <param name="config">The <see cref="SystemTextJsonParsingConfig"/>.</param>
677676
/// <param name="selector">A projection string expression to apply to each element.</param>
678677
/// <param name="args">An object array that contains zero or more objects to insert into the predicate as parameters.</param>
679-
/// <returns>An <see cref="JsonElement"/> whose elements are the result of invoking a projection string on each element of source.</returns>
678+
/// <returns>An <see cref="JsonDocument"/> whose elements are the result of invoking a projection string on each element of source.</returns>
680679
public static JsonDocument Select(this JsonDocument source, SystemTextJsonParsingConfig config, string selector, params object?[] args)
681680
{
682681
Check.NotNull(source);
@@ -721,6 +720,38 @@ public static JsonDocument Select(this JsonDocument source, Type resultType, str
721720
}
722721
#endregion Select
723722

723+
#region SelectMany
724+
/// <summary>
725+
/// Projects each element of a sequence to an <see cref="JsonDocument"/> and combines the resulting sequences into one sequence.
726+
/// </summary>
727+
/// <param name="source">The source <see cref="JsonDocument"/></param>
728+
/// <param name="selector">A projection string expression to apply to each element.</param>
729+
/// <param name="args">An object array that contains zero or more objects to insert into the predicate as parameters. </param>
730+
/// <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>
731+
public static JsonDocument SelectMany(this JsonDocument source, string selector, params object?[] args)
732+
{
733+
return SelectMany(source, SystemTextJsonParsingConfig.Default, selector, args);
734+
}
735+
736+
/// <summary>
737+
/// Projects each element of a sequence to an <see cref="JsonDocument"/> and combines the resulting sequences into one sequence.
738+
/// </summary>
739+
/// <param name="source">The source <see cref="JsonDocument"/></param>
740+
/// <param name="config">The <see cref="SystemTextJsonParsingConfig"/>.</param>
741+
/// <param name="selector">A projection string expression to apply to each element.</param>
742+
/// <param name="args">An object array that contains zero or more objects to insert into the predicate as parameters.</param>
743+
/// <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>
744+
public static JsonDocument SelectMany(this JsonDocument source, SystemTextJsonParsingConfig config, string selector, params object?[] args)
745+
{
746+
Check.NotNull(source);
747+
Check.NotNull(config);
748+
Check.NotNullOrEmpty(selector);
749+
750+
var queryable = ToQueryable(source, config);
751+
return ToJsonDocumentArray(() => queryable.SelectMany(config, selector, args));
752+
}
753+
#endregion SelectMany
754+
724755
#region Single
725756
/// <summary>
726757
/// Returns the only element of a sequence, and throws an exception if there

test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs

+31
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,37 @@ public void Select_ResultType()
323323
array.Should().ContainInOrder(1, 4, 9);
324324
}
325325

326+
[Fact]
327+
public void SelectMany()
328+
{
329+
// Arrange
330+
var json =
331+
"""
332+
[{
333+
"PhoneNumbers": [
334+
{ "Number": "123" },
335+
{ "Number": "456" }
336+
]
337+
},
338+
{
339+
"PhoneNumbers": [
340+
{ "Number": "789" },
341+
{ "Number": "012" }
342+
]
343+
}]
344+
""";
345+
var source = JArray.Parse(json);
346+
347+
// Act
348+
var result = source
349+
.SelectMany("PhoneNumbers")
350+
.Select("Number");
351+
352+
// Assert
353+
var array = result.Select(x => x.Value<string>());
354+
array.Should().BeEquivalentTo("123", "456", "789", "012");
355+
}
356+
326357
[Fact]
327358
public void Single()
328359
{

test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs

+31
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,37 @@ public void Select()
320320
array.Should().BeEquivalentTo("John", "Doe");
321321
}
322322

323+
[Fact]
324+
public void SelectMany()
325+
{
326+
// Arrange
327+
var json =
328+
"""
329+
[{
330+
"PhoneNumbers": [
331+
{ "Number": "123" },
332+
{ "Number": "456" }
333+
]
334+
},
335+
{
336+
"PhoneNumbers": [
337+
{ "Number": "789" },
338+
{ "Number": "012" }
339+
]
340+
}]
341+
""";
342+
var source = JsonDocument.Parse(json);
343+
344+
// Act
345+
var result = source
346+
.SelectMany("PhoneNumbers")
347+
.Select("Number");
348+
349+
// Assert
350+
var array = result.RootElement.EnumerateArray().Select(x => x.GetString());
351+
array.Should().BeEquivalentTo("123", "456", "789", "012");
352+
}
353+
323354
[Fact]
324355
public void Single()
325356
{

0 commit comments

Comments
 (0)