Example #1
0
        static JsonDocumentBuilder _FromDiff(JsonElement source, JsonElement target)
        {
            JsonElementEqualityComparer comparer = JsonElementEqualityComparer.Instance;

            if (source.ValueKind != JsonValueKind.Object || target.ValueKind != JsonValueKind.Object)
            {
                return(new JsonDocumentBuilder(target));
            }
            var builder = new JsonDocumentBuilder(JsonValueKind.Object);

            foreach (var property in source.EnumerateObject())
            {
                JsonElement value;
                if (target.TryGetProperty(property.Name, out value))
                {
                    if (!comparer.Equals(property.Value, value))
                    {
                        builder.AddProperty(property.Name, _FromDiff(property.Value, value));
                    }
                }
                else
                {
                    builder.AddProperty(property.Name, new JsonDocumentBuilder(JsonValueKind.Null));
                }
            }

            foreach (var property in target.EnumerateObject())
            {
                JsonElement value;
                if (!source.TryGetProperty(property.Name, out value))
                {
                    builder.AddProperty(property.Name, new JsonDocumentBuilder(property.Value));
                }
            }

            return(builder);
        }
Example #2
0
        static JsonDocumentBuilder _FromDiff(JsonElement source,
                                             JsonElement target,
                                             string path)
        {
            var builder = new JsonDocumentBuilder(JsonValueKind.Array);

            JsonElementEqualityComparer comparer = JsonElementEqualityComparer.Instance;

            if (comparer.Equals(source, target))
            {
                return(builder);
            }

            if (source.ValueKind == JsonValueKind.Array && target.ValueKind == JsonValueKind.Array)
            {
                int common = Math.Min(source.GetArrayLength(), target.GetArrayLength());
                for (int i = 0; i < common; ++i)
                {
                    var buffer = new StringBuilder(path);
                    buffer.Append("/");
                    buffer.Append(i.ToString());
                    var temp_diff = _FromDiff(source[i], target[i], buffer.ToString());
                    foreach (var item in temp_diff.EnumerateArray())
                    {
                        builder.AddArrayItem(item);
                    }
                }
                // Element in source, not in target - remove
                for (int i = source.GetArrayLength(); i-- > target.GetArrayLength();)
                {
                    var buffer = new StringBuilder(path);
                    buffer.Append("/");
                    buffer.Append(i.ToString());
                    var valBuilder = new JsonDocumentBuilder(JsonValueKind.Object);
                    valBuilder.AddProperty("op", new JsonDocumentBuilder("remove"));
                    valBuilder.AddProperty("path", new JsonDocumentBuilder(buffer.ToString()));
                    builder.AddArrayItem(valBuilder);
                }
                // Element in target, not in source - add,
                for (int i = source.GetArrayLength(); i < target.GetArrayLength(); ++i)
                {
                    var a      = target[i];
                    var buffer = new StringBuilder(path);
                    buffer.Append("/");
                    buffer.Append(i.ToString());
                    var valBuilder = new JsonDocumentBuilder(JsonValueKind.Object);
                    valBuilder.AddProperty("op", new JsonDocumentBuilder("add"));
                    valBuilder.AddProperty("path", new JsonDocumentBuilder(buffer.ToString()));
                    valBuilder.AddProperty("value", new JsonDocumentBuilder(a));
                    builder.AddArrayItem(valBuilder);
                }
            }
            else if (source.ValueKind == JsonValueKind.Object && target.ValueKind == JsonValueKind.Object)
            {
                foreach (var a in source.EnumerateObject())
                {
                    var buffer = new StringBuilder(path);
                    buffer.Append("/");
                    buffer.Append(JsonPointer.Escape(a.Name));

                    JsonElement element;
                    if (target.TryGetProperty(a.Name, out element))
                    {
                        var temp_diff = _FromDiff(a.Value, element, buffer.ToString());
                        foreach (var item in temp_diff.EnumerateArray())
                        {
                            builder.AddArrayItem(item);
                        }
                    }
                    else
                    {
                        var valBuilder = new JsonDocumentBuilder(JsonValueKind.Object);
                        valBuilder.AddProperty("op", new JsonDocumentBuilder("remove"));
                        valBuilder.AddProperty("path", new JsonDocumentBuilder(buffer.ToString()));
                        builder.AddArrayItem(valBuilder);
                    }
                }
                foreach (var a in target.EnumerateObject())
                {
                    JsonElement element;
                    if (!source.TryGetProperty(a.Name, out element))
                    {
                        var buffer = new StringBuilder(path);
                        buffer.Append("/");
                        buffer.Append(JsonPointer.Escape(a.Name));
                        var valBuilder = new JsonDocumentBuilder(JsonValueKind.Object);
                        valBuilder.AddProperty("op", new JsonDocumentBuilder("add"));
                        valBuilder.AddProperty("path", new JsonDocumentBuilder(buffer.ToString()));
                        valBuilder.AddProperty("value", new JsonDocumentBuilder(a.Value));
                        builder.AddArrayItem(valBuilder);
                    }
                }
            }
            else
            {
                var valBuilder = new JsonDocumentBuilder(JsonValueKind.Object);
                valBuilder.AddProperty("op", new JsonDocumentBuilder("replace"));
                valBuilder.AddProperty("path", new JsonDocumentBuilder(path));
                valBuilder.AddProperty("value", new JsonDocumentBuilder(target));
                builder.AddArrayItem(valBuilder);
            }

            return(builder);
        }
Example #3
0
        static void ApplyPatch(ref JsonDocumentBuilder target,
                               JsonElement patch)
        {
            JsonElementEqualityComparer comparer = JsonElementEqualityComparer.Instance;

            Debug.Assert(target != null);

            if (patch.ValueKind != JsonValueKind.Array)
            {
                throw new ArgumentException("Patch must be an array");
            }

            foreach (var operation in patch.EnumerateArray())
            {
                JsonElement opElement;
                if (!operation.TryGetProperty("op", out opElement))
                {
                    throw new ArgumentException("Invalid patch");
                }
                string op = opElement.GetString() ?? throw new InvalidOperationException("Operation cannot be null");

                JsonElement pathElement;
                if (!operation.TryGetProperty("path", out pathElement))
                {
                    throw new ArgumentException(op, "Invalid patch");
                }
                string path = pathElement.GetString() ?? throw new InvalidOperationException("Operation cannot be null");;

                JsonPointer location;
                if (!JsonPointer.TryParse(path, out location))
                {
                    throw new ArgumentException(op, "Invalid patch");
                }

                if (op == "test")
                {
                    JsonElement value;
                    if (!operation.TryGetProperty("value", out value))
                    {
                        throw new ArgumentException(op, "Invalid patch");
                    }

                    JsonDocumentBuilder tested;
                    if (!location.TryGetValue(target, out tested))
                    {
                        throw new ArgumentException(op, "Invalid patch");
                    }

                    using (var doc = tested.ToJsonDocument())
                    {
                        if (!comparer.Equals(doc.RootElement, value))
                        {
                            throw new JsonPatchException(op, "Test failed");
                        }
                    }
                }
                else if (op == "add")
                {
                    JsonElement value;
                    if (!operation.TryGetProperty("value", out value))
                    {
                        throw new ArgumentException(op, "Invalid patch");
                    }
                    var valueBuilder = new JsonDocumentBuilder(value);
                    if (!location.TryAddIfAbsent(ref target, valueBuilder)) // try insert without replace
                    {
                        if (!location.TryReplace(ref target, valueBuilder)) // try insert without replace
                        {
                            throw new JsonPatchException(op, "Add failed");
                        }
                    }
                }
                else if (op == "remove")
                {
                    if (!location.TryRemove(ref target))
                    {
                        throw new JsonPatchException(op, "Add failed");
                    }
                }
                else if (op == "replace")
                {
                    JsonElement value;
                    if (!operation.TryGetProperty("value", out value))
                    {
                        throw new ArgumentException(op, "Invalid patch");
                    }
                    var valueBuilder = new JsonDocumentBuilder(value);
                    if (!location.TryReplace(ref target, valueBuilder))
                    {
                        throw new JsonPatchException(op, "Replace failed");
                    }
                }
                else if (op == "move")
                {
                    JsonElement fromElement;
                    if (!operation.TryGetProperty("from", out fromElement))
                    {
                        throw new ArgumentException(op, "Invalid patch");
                    }
                    string from = fromElement.GetString() ?? throw new InvalidOperationException("From element cannot be null");;

                    JsonPointer fromPointer;
                    if (!JsonPointer.TryParse(from, out fromPointer))
                    {
                        throw new ArgumentException(op, "Invalid patch");
                    }

                    JsonDocumentBuilder value;
                    if (!fromPointer.TryGetValue(target, out value))
                    {
                        throw new JsonPatchException(op, "Move failed");
                    }

                    if (!fromPointer.TryRemove(ref target))
                    {
                        throw new JsonPatchException(op, "Move failed");
                    }
                    if (!location.TryAddIfAbsent(ref target, value))
                    {
                        if (!location.TryReplace(ref target, value)) // try insert without replace
                        {
                            throw new JsonPatchException(op, "Move failed");
                        }
                    }
                }
                else if (op == "copy")
                {
                    JsonElement fromElement;
                    if (!operation.TryGetProperty("from", out fromElement))
                    {
                        throw new ArgumentException(op, "Invalid patch");
                    }
                    string      from = fromElement.GetString() ?? throw new InvalidOperationException("from cannot be null");
                    JsonPointer fromPointer;
                    if (!JsonPointer.TryParse(from, out fromPointer))
                    {
                        throw new ArgumentException(op, "Invalid patch");
                    }

                    JsonDocumentBuilder value;
                    if (!fromPointer.TryGetValue(target, out value))
                    {
                        throw new JsonPatchException(op, "Copy failed");
                    }
                    if (!location.TryAddIfAbsent(ref target, value))
                    {
                        if (!location.TryReplace(ref target, value)) // try insert without replace
                        {
                            throw new JsonPatchException(op, "Move failed");
                        }
                    }
                }
            }
        }