-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add a property to a json object (#50)
- Loading branch information
Showing
4 changed files
with
137 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Text.Json.Nodes; | ||
using System.Threading.Tasks; | ||
using Json.More; | ||
using Json.Path; | ||
|
||
|
||
namespace Streamistry; | ||
public class JsonObjectPropertyAppender<TInputMain, TInputSecondary> : Zipper<TInputMain, TInputSecondary, TInputMain> | ||
where TInputMain : JsonNode | ||
where TInputSecondary : JsonNode | ||
{ | ||
|
||
public JsonObjectPropertyAppender(IChainablePort<TInputMain> mainUpstream, IChainablePort<TInputSecondary> secondUpstream, string path) | ||
: base(mainUpstream, secondUpstream, (x, y) => AppendProperty(x, y, path)) | ||
{ } | ||
|
||
private static TInputMain? AppendProperty(TInputMain? main, TInputSecondary? secondary, string path) | ||
{ | ||
if (main is null) | ||
return null; | ||
if (secondary is null) | ||
return main; | ||
var segments = path.Split('.'); | ||
AddOrReplaceProperty(main, string.Join('.',segments.Take(segments.Length - 1)), path.Split('.').Last(), secondary); | ||
return main; | ||
} | ||
|
||
private static void AddOrReplaceProperty(JsonNode root, string jsonPath, string propertyName, JsonNode value) | ||
{ | ||
var path = JsonPath.Parse(jsonPath); | ||
var result = path.Evaluate(root); | ||
|
||
if (result.Matches != null && result.Matches.Count > 0) | ||
{ | ||
foreach (var match in result.Matches) | ||
{ | ||
if (match.Value is JsonObject jsonObject) | ||
jsonObject[propertyName] = value; | ||
} | ||
} | ||
else | ||
{ | ||
CreatePathAndAddProperty(root, jsonPath, propertyName, value); | ||
} | ||
} | ||
|
||
private static void CreatePathAndAddProperty(JsonNode root, string jsonPath, string propertyName, JsonNode value) | ||
{ | ||
var segments = jsonPath.TrimStart('$').Split('.', StringSplitOptions.RemoveEmptyEntries); | ||
JsonNode current = root; | ||
|
||
foreach (var segment in segments) | ||
{ | ||
if (current is JsonObject obj) | ||
{ | ||
if (!obj.ContainsKey(segment)) | ||
{ | ||
var newObject = new JsonObject(); | ||
obj[segment] = newObject; | ||
current = newObject; | ||
} | ||
else | ||
{ | ||
current = obj[segment]!; | ||
} | ||
} | ||
else | ||
{ | ||
throw new InvalidOperationException("Invalid path or JSON structure."); | ||
} | ||
} | ||
|
||
if (current is JsonObject finalObj) | ||
{ | ||
finalObj[propertyName] = value; | ||
} | ||
else | ||
{ | ||
throw new InvalidOperationException("Final path segment is not an object."); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Text.Json.Nodes; | ||
using System.Threading.Tasks; | ||
|
||
namespace Streamistry; | ||
public class JsonValueMapper<TInput> : Mapper<TInput, JsonValue> | ||
{ | ||
public JsonValueMapper(IChainablePort<TInput> upstream, Func<TInput?, string>? toString = null) | ||
: base(upstream, value => toString is null ? JsonValue.Create(value) : JsonValue.Create(toString.Invoke(value))) | ||
{ | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Text.Json.Nodes; | ||
using System.Threading.Tasks; | ||
using NUnit.Framework; | ||
using Streamistry.Pipes.Parsers; | ||
using Streamistry.Pipes.Sinks; | ||
using Streamistry.Pipes.Sources; | ||
|
||
namespace Streamistry.Testing; | ||
public class JsonObjectPropertyAppenderTests | ||
{ | ||
[Test] | ||
public void X() | ||
{ | ||
var persons = new EnumerableSource<string>([ | ||
JsonTests.JsonFirst, JsonTests.JsonSecond, JsonTests.JsonThird]); | ||
var birthdates = new EnumerableSource<DateOnly>([new DateOnly(1879, 3, 14), new DateOnly(1856, 7, 10), new DateOnly(1903, 12, 28)]); | ||
var pipeline = new Pipeline([persons, birthdates]); | ||
var personObject = new JsonObjectParser(persons); | ||
var birthdateValue = new JsonValueMapper<DateOnly>(birthdates, date => date.ToString("yyyy-MM-dd")); | ||
var appender = new JsonObjectPropertyAppender<JsonObject, JsonValue>(personObject, birthdateValue, "$.user.birthdate"); | ||
var sink = new MemorySink<JsonObject>(appender); | ||
pipeline.Start(); | ||
|
||
Assert.That(sink.State.Count, Is.EqualTo(3)); | ||
Assert.That(sink.State.First()!["user"]!["birthdate"], Is.Not.Null); | ||
Assert.That(sink.State.First()!["user"]!["birthdate"]!.GetValue<string>(), Is.EqualTo("1879-03-14")); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters