Fix #16
- When unpatching arrays, modifications need to be done before additions/deletes - When performing array diffs and nothing matches, we should do object diffs starting from the front of both arrats, instead of the back.pull/19/head
parent
901ec82281
commit
9bd9d2f302
|
@ -259,7 +259,7 @@ namespace JsonDiffPatchDotNet.UnitTests
|
|||
[Test]
|
||||
public void Diff_EfficientArrayDiffHugeArrays_NoStackOverflow()
|
||||
{
|
||||
const int ARRAY_SIZE = 1000;
|
||||
const int arraySize = 1000;
|
||||
Func<int, int, JToken> hugeArrayFunc = (startIndex, count) =>
|
||||
{
|
||||
var builder = new StringBuilder("[");
|
||||
|
@ -273,8 +273,8 @@ namespace JsonDiffPatchDotNet.UnitTests
|
|||
};
|
||||
|
||||
var jdp = new JsonDiffPatch();
|
||||
var left = hugeArrayFunc(0, ARRAY_SIZE);
|
||||
var right = hugeArrayFunc(ARRAY_SIZE / 2, ARRAY_SIZE);
|
||||
var left = hugeArrayFunc(0, arraySize);
|
||||
var right = hugeArrayFunc(arraySize / 2, arraySize);
|
||||
|
||||
JToken diff = jdp.Diff(left, right);
|
||||
var restored = jdp.Patch(left, diff);
|
||||
|
|
|
@ -57,7 +57,8 @@ namespace JsonDiffPatchDotNet.UnitTests
|
|||
public void Unpatch_ObjectApplyEditText_Success()
|
||||
{
|
||||
var jdp = new JsonDiffPatch();
|
||||
const string value = @"bla1h111111111111112312weldjidjoijfoiewjfoiefjefijfoejoijfiwoejfiewjfiwejfowjwifewjfejdewdwdewqwertyqwertifwiejifoiwfei";
|
||||
const string value =
|
||||
@"bla1h111111111111112312weldjidjoijfoiewjfoiefjefijfoejoijfiwoejfiewjfiwejfowjwifewjfejdewdwdewqwertyqwertifwiejifoiwfei";
|
||||
var left = JObject.Parse(@"{ ""p"" : """ + value + @""" }");
|
||||
var right = JObject.Parse(@"{ ""p"" : ""blah1"" }");
|
||||
var patch = jdp.Diff(left, right);
|
||||
|
@ -73,7 +74,7 @@ namespace JsonDiffPatchDotNet.UnitTests
|
|||
[Test]
|
||||
public void Unpatch_ObjectApplyEditTextEfficient_Success()
|
||||
{
|
||||
var options = new Options { MinEfficientTextDiffLength = 1, TextDiff = TextDiffMode.Efficient };
|
||||
var options = new Options {MinEfficientTextDiffLength = 1, TextDiff = TextDiffMode.Efficient};
|
||||
var jdp = new JsonDiffPatch(options);
|
||||
var left = JObject.Parse(@"{ ""p"" : ""The quick brown fox jumps over the lazy dog."" }");
|
||||
var right = JObject.Parse(@"{ ""p"" : ""That quick brown fox jumped over a lazy dog."" }");
|
||||
|
@ -84,7 +85,8 @@ namespace JsonDiffPatchDotNet.UnitTests
|
|||
Assert.IsNotNull(unpatched, "Patched object");
|
||||
Assert.AreEqual(1, unpatched.Properties().Count(), "Property");
|
||||
Assert.AreEqual(JTokenType.String, unpatched.Property("p").Value.Type, "String Type");
|
||||
Assert.AreEqual("The quick brown fox jumps over the lazy dog.", unpatched.Property("p").Value.ToString(), "String value");
|
||||
Assert.AreEqual("The quick brown fox jumps over the lazy dog.", unpatched.Property("p").Value.ToString(),
|
||||
"String value");
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -100,15 +102,15 @@ namespace JsonDiffPatchDotNet.UnitTests
|
|||
Assert.IsNotNull(patched, "Patched object");
|
||||
Assert.AreEqual(1, patched.Properties().Count(), "Property");
|
||||
Assert.AreEqual(JTokenType.Object, patched.Property("i").Value.Type);
|
||||
Assert.AreEqual(1, ((JObject)patched.Property("i").Value).Properties().Count());
|
||||
Assert.AreEqual(JTokenType.Boolean, ((JObject)patched.Property("i").Value).Property("p").Value.Type);
|
||||
Assert.IsFalse(((JObject)patched.Property("i").Value).Property("p").Value.ToObject<bool>());
|
||||
Assert.AreEqual(1, ((JObject) patched.Property("i").Value).Properties().Count());
|
||||
Assert.AreEqual(JTokenType.Boolean, ((JObject) patched.Property("i").Value).Property("p").Value.Type);
|
||||
Assert.IsFalse(((JObject) patched.Property("i").Value).Property("p").Value.ToObject<bool>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Unpatch_ArrayUnpatchAdd_Success()
|
||||
{
|
||||
var jdp = new JsonDiffPatch(new Options { ArrayDiff = ArrayDiffMode.Efficient });
|
||||
var jdp = new JsonDiffPatch(new Options {ArrayDiff = ArrayDiffMode.Efficient});
|
||||
var left = JToken.Parse(@"[1,2,3]");
|
||||
var right = JToken.Parse(@"[1,2,3,4]");
|
||||
var patch = jdp.Diff(left, right);
|
||||
|
@ -121,7 +123,7 @@ namespace JsonDiffPatchDotNet.UnitTests
|
|||
[Test]
|
||||
public void Unpatch_ArrayUnpatchRemove_Success()
|
||||
{
|
||||
var jdp = new JsonDiffPatch(new Options { ArrayDiff = ArrayDiffMode.Efficient });
|
||||
var jdp = new JsonDiffPatch(new Options {ArrayDiff = ArrayDiffMode.Efficient});
|
||||
var left = JToken.Parse(@"[1,2,3]");
|
||||
var right = JToken.Parse(@"[1,2]");
|
||||
var patch = jdp.Diff(left, right);
|
||||
|
@ -134,7 +136,7 @@ namespace JsonDiffPatchDotNet.UnitTests
|
|||
[Test]
|
||||
public void Unpatch_ArrayUnpatchModify_Success()
|
||||
{
|
||||
var jdp = new JsonDiffPatch(new Options { ArrayDiff = ArrayDiffMode.Efficient });
|
||||
var jdp = new JsonDiffPatch(new Options {ArrayDiff = ArrayDiffMode.Efficient});
|
||||
var left = JToken.Parse(@"[1,3,{""p"":false}]");
|
||||
var right = JToken.Parse(@"[1,4,{""p"": [1] }]");
|
||||
var patch = jdp.Diff(left, right);
|
||||
|
@ -147,7 +149,7 @@ namespace JsonDiffPatchDotNet.UnitTests
|
|||
[Test]
|
||||
public void Unpatch_ArrayUnpatchComplex_Success()
|
||||
{
|
||||
var jdp = new JsonDiffPatch(new Options { ArrayDiff = ArrayDiffMode.Efficient });
|
||||
var jdp = new JsonDiffPatch(new Options {ArrayDiff = ArrayDiffMode.Efficient});
|
||||
var left = JToken.Parse(@"{""p"": [1,2,[1],false,""11111"",3,{""p"":false},10,10] }");
|
||||
var right = JToken.Parse(@"{""p"": [1,2,[1,3],false,""11112"",3,{""p"":true},10,10] }");
|
||||
var patch = jdp.Diff(left, right);
|
||||
|
@ -160,10 +162,12 @@ namespace JsonDiffPatchDotNet.UnitTests
|
|||
[Test]
|
||||
public void Unpatch_ArrayUnpatchMoving_Success()
|
||||
{
|
||||
var jdp = new JsonDiffPatch(new Options { ArrayDiff = ArrayDiffMode.Efficient });
|
||||
var jdp = new JsonDiffPatch(new Options {ArrayDiff = ArrayDiffMode.Efficient});
|
||||
var left = JToken.Parse(@"[0,1,2,3,4,5,6,7,8,9,10]");
|
||||
var right = JToken.Parse(@"[10,0,1,7,2,4,5,6,88,9,3]");
|
||||
var patch = JToken.Parse(@"{ ""8"": [88], ""_t"": ""a"", ""_3"": ["""", 10, 3], ""_7"": ["""", 3, 3], ""_8"": [8, 0, 0], ""_10"": ["""", 0, 3] }");
|
||||
var patch =
|
||||
JToken.Parse(
|
||||
@"{ ""8"": [88], ""_t"": ""a"", ""_3"": ["""", 10, 3], ""_7"": ["""", 3, 3], ""_8"": [8, 0, 0], ""_10"": ["""", 0, 3] }");
|
||||
|
||||
var patched = jdp.Unpatch(right, patch);
|
||||
|
||||
|
@ -173,7 +177,7 @@ namespace JsonDiffPatchDotNet.UnitTests
|
|||
[Test]
|
||||
public void Unpatch_ArrayPatchMovingNonConsecutive_Success()
|
||||
{
|
||||
var jdp = new JsonDiffPatch(new Options { ArrayDiff = ArrayDiffMode.Efficient });
|
||||
var jdp = new JsonDiffPatch(new Options {ArrayDiff = ArrayDiffMode.Efficient});
|
||||
var left = JToken.Parse(@"[0,1,3,4,5]");
|
||||
var right = JToken.Parse(@"[0,4,3,1,5]");
|
||||
var patch = JToken.Parse(@"{""_t"": ""a"", ""_2"": ["""", 2, 3],""_3"": ["""", 1, 3]}");
|
||||
|
@ -186,7 +190,7 @@ namespace JsonDiffPatchDotNet.UnitTests
|
|||
[Test]
|
||||
public void Unpatch_ArrayPatchMoveDeletingNonConsecutive_Success()
|
||||
{
|
||||
var jdp = new JsonDiffPatch(new Options { ArrayDiff = ArrayDiffMode.Efficient });
|
||||
var jdp = new JsonDiffPatch(new Options {ArrayDiff = ArrayDiffMode.Efficient});
|
||||
var left = JToken.Parse(@"[0,1,3,4,5]");
|
||||
var right = JToken.Parse(@"[0,5,3]");
|
||||
var patch = JToken.Parse(@"{""_t"": ""a"", ""_1"": [ 1, 0, 0], ""_3"": [4,0, 0],""_4"": [ """", 1, 3 ]}");
|
||||
|
@ -195,5 +199,31 @@ namespace JsonDiffPatchDotNet.UnitTests
|
|||
|
||||
Assert.AreEqual(left.ToString(), patched.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Unpatch_Bug16Exception_Success()
|
||||
{
|
||||
var jdp = new JsonDiffPatch(new Options { ArrayDiff = ArrayDiffMode.Efficient });
|
||||
var left = JToken.Parse("{\r\n \"rootRegion\": {\r\n \"rows\": [\r\n \"auto\"\r\n ],\r\n \"members\": [\r\n {\r\n \"row\": 2\r\n }\r\n ]\r\n }\r\n}");
|
||||
var right = JToken.Parse("{\r\n \"rootRegion\": {\r\n \"rows\": [\r\n \"auto\",\r\n \"auto\"\r\n ],\r\n \"members\": [\r\n {\r\n \"row\": 3\r\n },\r\n {\r\n \"name\": \"label-header\"\r\n }\r\n ]\r\n }\r\n}");
|
||||
var patch = jdp.Diff(left, right);
|
||||
|
||||
var patched = jdp.Unpatch(right, patch);
|
||||
|
||||
Assert.AreEqual(left.ToString(), patched.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Unpatch_Bug16SilentFail_Success()
|
||||
{
|
||||
var jdp = new JsonDiffPatch(new Options { ArrayDiff = ArrayDiffMode.Efficient });
|
||||
var left = JToken.Parse("{\r\n \"members\": [\r\n {\r\n \"name\": \"text-box\",\r\n \"version\": \"1.0.0\",\r\n \"required\": false,\r\n \"isArray\": false,\r\n \"row\": 2,\r\n \"rowSpan\": 1,\r\n \"column\": 0,\r\n \"columnSpan\": 1,\r\n \"readOnly\": false,\r\n \"properties\": [\r\n {\r\n \"destPath\": \"ng-model\",\r\n \"srcPath\": \"cmt\"\r\n }\r\n ],\r\n \"parent\": \"Acknowledge Unit (111)\"\r\n },\r\n {\r\n \"name\": \"component-label\",\r\n \"version\": \"1.0.0\",\r\n \"label\": \"COMMAND_DIALOG_COMMENT\",\r\n \"required\": false,\r\n \"isArray\": false,\r\n \"row\": 1,\r\n \"rowSpan\": 1,\r\n \"column\": 0,\r\n \"columnSpan\": 1,\r\n \"readOnly\": false,\r\n \"properties\": [],\r\n \"parent\": \"Acknowledge Unit (111)\"\r\n }\r\n ]\r\n \r\n}");
|
||||
var right = JToken.Parse("{\r\n \"members\": [\r\n {\r\n \"name\": \"text-box\",\r\n \"version\": \"1.0.0\",\r\n \"required\": false,\r\n \"isArray\": false,\r\n \"row\": 3,\r\n \"rowSpan\": 1,\r\n \"column\": 0,\r\n \"columnSpan\": 1,\r\n \"readOnly\": false,\r\n \"properties\": [\r\n {\r\n \"destPath\": \"ng-model\",\r\n \"srcPath\": \"cmt\"\r\n }\r\n ],\r\n \"parent\": \"Acknowledge Unit (111)\"\r\n },\r\n {\r\n \"name\": \"component-label\",\r\n \"version\": \"1.0.0\",\r\n \"label\": \"COMMAND_DIALOG_COMMENT\",\r\n \"required\": false,\r\n \"isArray\": false,\r\n \"row\": 2,\r\n \"rowSpan\": 1,\r\n \"column\": 0,\r\n \"columnSpan\": 1,\r\n \"readOnly\": false,\r\n \"properties\": [],\r\n \"parent\": \"Acknowledge Unit (111)\"\r\n },\r\n {\r\n \"name\": \"label-header\",\r\n \"version\": \"1.0.0\",\r\n \"column\": 0,\r\n \"row\": 0,\r\n \"columnSpan\": 1,\r\n \"rowSpan\": 1,\r\n \"properties\": [],\r\n \"addedArgs\": {},\r\n \"parent\": \"Acknowledge Unit (111)\",\r\n \"label\": \"test\"\r\n }\r\n ]\r\n }");
|
||||
var patch = jdp.Diff(left, right);
|
||||
|
||||
var patched = jdp.Unpatch(right, patch);
|
||||
|
||||
Assert.IsTrue(JToken.DeepEquals(left.ToString(), patched.ToString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -622,6 +622,12 @@ namespace JsonDiffPatchDotNet
|
|||
}
|
||||
}
|
||||
|
||||
// first modify entries
|
||||
foreach (var op in toModify)
|
||||
{
|
||||
JToken p = Unpatch(right[int.Parse(op.Name)], op.Value);
|
||||
right[int.Parse(op.Name)] = p;
|
||||
}
|
||||
|
||||
// remove items, in reverse order to avoid sawing our own floor
|
||||
toRemove.Sort((x, y) => int.Parse(x.Name).CompareTo(int.Parse(y.Name)));
|
||||
|
@ -638,12 +644,6 @@ namespace JsonDiffPatchDotNet
|
|||
right.Insert(int.Parse(op.Name), ((JArray)op.Value)[0]);
|
||||
}
|
||||
|
||||
foreach (var op in toModify)
|
||||
{
|
||||
JToken p = Unpatch(right[int.Parse(op.Name)], op.Value);
|
||||
right[int.Parse(op.Name)] = p;
|
||||
}
|
||||
|
||||
return right;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace JsonDiffPatchDotNet
|
||||
|
@ -23,28 +22,19 @@ namespace JsonDiffPatchDotNet
|
|||
internal static Lcs Get(List<JToken> left, List<JToken> right)
|
||||
{
|
||||
var matrix = LcsInternal(left, right);
|
||||
var result = Backtrack(matrix, left, right, left.Count(), right.Count());
|
||||
var result = Backtrack(matrix, left, right, left.Count, right.Count);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int[,] LcsInternal(List<JToken> left, List<JToken> right)
|
||||
{
|
||||
var arr = new int[left.Count() + 1, right.Count() + 1];
|
||||
var arr = new int[left.Count + 1, right.Count + 1];
|
||||
|
||||
for (int i = 0; i <= right.Count(); i++)
|
||||
for (int i = 1; i <= left.Count; i++)
|
||||
{
|
||||
arr[0, i] = 0;
|
||||
}
|
||||
for (int i = 0; i <= left.Count(); i++)
|
||||
{
|
||||
arr[i, 0] = 0;
|
||||
}
|
||||
|
||||
for (int i = 1; i <= left.Count(); i++)
|
||||
{
|
||||
for (int j = 1; j <= right.Count(); j++)
|
||||
for (int j = 1; j <= right.Count; j++)
|
||||
{
|
||||
if (left[i - 1].Equals(right[j - 1]))
|
||||
if (JToken.DeepEquals(left[i - 1], right[j - 1]))
|
||||
{
|
||||
arr[i, j] = arr[i - 1, j - 1] + 1;
|
||||
}
|
||||
|
@ -61,30 +51,30 @@ namespace JsonDiffPatchDotNet
|
|||
private static Lcs Backtrack(int[,] matrix, List<JToken> left, List<JToken> right, int li, int ri)
|
||||
{
|
||||
var result = new Lcs();
|
||||
for (int i = li, j = ri; i > 0 && j > 0;)
|
||||
for (int i = 1, j = 1; i <= li && j <= ri;)
|
||||
{
|
||||
// If the JSON tokens at the same position are both Objects or both Arrays, we just say they
|
||||
// are the same even if they are not, because we can package smaller deltas than an entire
|
||||
// object or array replacement by doing object to object or array to array diff.
|
||||
if (left[i - 1].Equals(right[j - 1])
|
||||
|| (left[i - 1].Type == JTokenType.Object && right[j - 1].Type == JTokenType.Object)
|
||||
|| (left[i - 1].Type == JTokenType.Array && right[j - 1].Type == JTokenType.Array))
|
||||
if (JToken.DeepEquals(left[i - 1], right[j - 1])
|
||||
|| left[i - 1].Type == JTokenType.Object && right[j - 1].Type == JTokenType.Object
|
||||
|| left[i - 1].Type == JTokenType.Array && right[j - 1].Type == JTokenType.Array)
|
||||
{
|
||||
result.Sequence.Insert(0, left[i - 1]);
|
||||
result.Indices1.Insert(0, i - 1);
|
||||
result.Indices2.Insert(0, j - 1);
|
||||
i--;
|
||||
j--;
|
||||
result.Sequence.Add(left[i - 1]);
|
||||
result.Indices1.Add(i - 1);
|
||||
result.Indices2.Add(j - 1);
|
||||
i++;
|
||||
j++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (matrix[i, j - 1] > matrix[i - 1, j])
|
||||
{
|
||||
j--;
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
i--;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue