< Summary

Information
Class: Chronicler.SerializationPayloadEditor
Assembly: Chronicler
File(s): /home/runner/work/Chronicler/Chronicler/src/Chronicler/Serialization/SerializationPayloadEditor.cs
Line coverage
100%
Covered lines: 82
Uncovered lines: 0
Coverable lines: 82
Total lines: 170
Line coverage: 100%
Branch coverage
97%
Covered branches: 37
Total branches: 38
Branch coverage: 97.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
RemoveJsonProperty(...)100%44100%
SetJsonValue(...)100%44100%
ParseJsonRoot(...)100%44100%
GetJsonParent(...)100%44100%
RemoveMemoryPackEntry(...)100%44100%
SetMemoryPackValue(...)100%44100%
ReadEnvelope(...)50%22100%
RemoveMemoryPackEntry(...)100%66100%
SetMemoryPackValue(...)100%66100%

File(s)

/home/runner/work/Chronicler/Chronicler/src/Chronicler/Serialization/SerializationPayloadEditor.cs

#LineLine coverage
 1using MemoryPack;
 2using System;
 3using System.Text.Json;
 4using System.Text.Json.Nodes;
 5
 6namespace Chronicler;
 7
 8/// <summary>
 9/// Provides methods for editing serialized payloads without fully deserializing them.
 10/// </summary>
 11public static class SerializationPayloadEditor
 12{
 13    #region JSON Editing
 14
 115    private static readonly JsonSerializerOptions JsonOptions = new()
 116    {
 117        IncludeFields = true
 118    };
 19
 20    /// <summary>
 21    /// Removes a property from a JSON payload at the specified path.
 22    /// </summary>
 23    /// <param name="json">The JSON payload.</param>
 24    /// <param name="path">The path to the property to remove.</param>
 25    /// <returns>The modified JSON payload.</returns>
 26    /// <exception cref="ArgumentException"></exception>
 27    public static string RemoveJsonProperty(string json, params string[] path)
 1228    {
 1229        if (path == null || path.Length == 0)
 230            throw new ArgumentException("A JSON property path is required.", nameof(path));
 31
 1032        JsonObject root = ParseJsonRoot(json);
 933        JsonObject parent = GetJsonParent(root, path);
 934        parent.Remove(path[^1]);
 935        return root.ToJsonString(JsonOptions);
 936    }
 37
 38    /// <summary>
 39    /// Sets a property value in a JSON payload at the specified path.
 40    /// </summary>
 41    /// <typeparam name="T">The type of the value to set.</typeparam>
 42    /// <param name="json">The JSON payload.</param>
 43    /// <param name="value">The value to set.</param>
 44    /// <param name="path">The path to the property to set.</param>
 45    /// <returns>The modified JSON payload.</returns>
 46    /// <exception cref="ArgumentException"></exception>
 47    public static string SetJsonValue<T>(string json, T value, params string[] path)
 748    {
 749        if (path == null || path.Length == 0)
 250            throw new ArgumentException("A JSON property path is required.", nameof(path));
 51
 552        JsonObject root = ParseJsonRoot(json);
 453        JsonObject parent = GetJsonParent(root, path);
 354        parent[path[^1]] = JsonSerializer.SerializeToNode(value, JsonOptions);
 355        return root.ToJsonString(JsonOptions);
 356    }
 57
 58    private static JsonObject ParseJsonRoot(string json)
 1559    {
 1560        JsonNode rootNode = JsonNode.Parse(json)
 1561            ?? throw new InvalidOperationException("Unable to parse JSON payload.");
 1462        return rootNode as JsonObject
 1463            ?? throw new InvalidOperationException("Expected JSON root object.");
 1364    }
 65
 66    private static JsonObject GetJsonParent(JsonObject root, string[] path)
 1367    {
 1368        JsonObject current = root;
 3069        for (int i = 0; i < path.Length - 1; i++)
 370        {
 371            current = current[path[i]] as JsonObject
 372                ?? throw new InvalidOperationException(
 373                    $"Expected JSON object at path segment '{path[i]}'.");
 274        }
 75
 1276        return current;
 1277    }
 78
 79    #endregion
 80
 81    #region MemoryPack Editing
 82
 83    /// <summary>
 84    /// Removes a MemoryPack entry from a serialized payload at the specified path.
 85    /// </summary>
 86    /// <param name="data">The serialized MemoryPack payload.</param>
 87    /// <param name="path">The path to the entry to remove.</param>
 88    /// <returns>The modified serialized MemoryPack payload.</returns>
 89    /// <exception cref="ArgumentException"></exception>
 90    public static byte[] RemoveMemoryPackEntry(byte[] data, params string[] path)
 1291    {
 1292        if (path == null || path.Length == 0)
 293            throw new ArgumentException("A MemoryPack entry path is required.", nameof(path));
 94
 1095        MemoryPackRecordEnvelope envelope = ReadEnvelope(data);
 1096        RemoveMemoryPackEntry(envelope, path, 0);
 997        return MemoryPackSerializer.Serialize(envelope);
 998    }
 99
 100    /// <summary>
 101    /// Sets a MemoryPack entry value in a serialized payload at the specified path.
 102    /// </summary>
 103    /// <typeparam name="T">The type of the value to set.</typeparam>
 104    /// <param name="data">The serialized MemoryPack payload.</param>
 105    /// <param name="value">The value to set.</param>
 106    /// <param name="path">The path to the entry to set.</param>
 107    /// <returns>The modified serialized MemoryPack payload.</returns>
 108    /// <exception cref="ArgumentException"></exception>
 109    public static byte[] SetMemoryPackValue<T>(byte[] data, T value, params string[] path)
 6110    {
 6111        if (path == null || path.Length == 0)
 2112            throw new ArgumentException("A MemoryPack entry path is required.", nameof(path));
 113
 4114        MemoryPackRecordEnvelope envelope = ReadEnvelope(data);
 4115        SetMemoryPackValue(envelope, path, 0, MemoryPackSerializer.Serialize(value));
 3116        return MemoryPackSerializer.Serialize(envelope);
 3117    }
 118
 119    private static MemoryPackRecordEnvelope ReadEnvelope(byte[] data)
 16120    {
 16121        return MemoryPackSerializer.Deserialize<MemoryPackRecordEnvelope>(data)
 16122            ?? new MemoryPackRecordEnvelope();
 16123    }
 124
 125    private static void RemoveMemoryPackEntry(MemoryPackRecordEnvelope envelope, string[] path, int depth)
 11126    {
 11127        if (depth == path.Length - 1)
 9128        {
 9129            envelope.Entries.Remove(path[depth]);
 9130            return;
 131        }
 132
 2133        if (!envelope.Entries.TryGetValue(path[depth], out byte[]? nestedData)
 2134            || nestedData == null)
 1135        {
 1136            throw new InvalidOperationException(
 1137                $"Unable to locate MemoryPack entry '{path[depth]}' at depth {depth}.");
 138        }
 139
 1140        MemoryPackRecordEnvelope nestedEnvelope = ReadEnvelope(nestedData);
 1141        RemoveMemoryPackEntry(nestedEnvelope, path, depth + 1);
 1142        envelope.Entries[path[depth]] = MemoryPackSerializer.Serialize(nestedEnvelope);
 10143    }
 144
 145    private static void SetMemoryPackValue(
 146        MemoryPackRecordEnvelope envelope,
 147        string[] path,
 148        int depth,
 149        byte[] serializedValue)
 5150    {
 5151        if (depth == path.Length - 1)
 3152        {
 3153            envelope.Entries[path[depth]] = serializedValue;
 3154            return;
 155        }
 156
 2157        if (!envelope.Entries.TryGetValue(path[depth], out byte[]? nestedData)
 2158            || nestedData == null)
 1159        {
 1160            throw new InvalidOperationException(
 1161                $"Unable to locate MemoryPack entry '{path[depth]}' at depth {depth}.");
 162        }
 163
 1164        MemoryPackRecordEnvelope nestedEnvelope = ReadEnvelope(nestedData);
 1165        SetMemoryPackValue(nestedEnvelope, path, depth + 1, serializedValue);
 1166        envelope.Entries[path[depth]] = MemoryPackSerializer.Serialize(nestedEnvelope);
 4167    }
 168
 169    #endregion
 170}