forked from fluentassertions/fluentassertions.json
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathJTokenExtensions.cs
More file actions
104 lines (89 loc) · 3.3 KB
/
JTokenExtensions.cs
File metadata and controls
104 lines (89 loc) · 3.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json.Linq;
namespace FluentAssertions.Json.Common;
internal static class JTokenExtensions
{
private static readonly JTokenComparer Comparer = new();
/// <summary>
/// Recursively sorts the properties of JObject instances by name and
/// the elements of JArray instances by their string representation,
/// producing a normalized JToken for consistent comparison
/// </summary>
public static JToken Normalize(this JToken token)
{
return token switch
{
JObject obj => new JObject(obj.Properties().OrderBy(p => p.Name, StringComparer.Ordinal).Select(p => new JProperty(p.Name, Normalize(p.Value)))),
JArray array => new JArray(array.Select(Normalize).OrderBy(x => x, Comparer)),
_ => token
};
}
private sealed class JTokenComparer : IComparer<JToken>
{
public int Compare(JToken x, JToken y)
{
if (ReferenceEquals(x, y))
{
return 0;
}
if (x is null)
{
return -1;
}
if (y is null)
{
return 1;
}
var typeComparison = x.Type.CompareTo(y.Type);
if (typeComparison != 0)
{
return typeComparison;
}
return x switch
{
JArray a => Compare(a, (JArray)y),
JObject o => Compare(o, (JObject)y),
JProperty p => Compare(p, (JProperty)y),
JValue v => Compare(v, (JValue)y),
JConstructor c => Compare(c, (JConstructor)y),
_ => string.CompareOrdinal(x.ToString(), y.ToString())
};
}
private static int Compare(JValue x, JValue y) => Comparer<object>.Default.Compare(x.Value, y.Value);
private static int Compare(JConstructor x, JConstructor y)
{
var nameComparison = string.CompareOrdinal(x.Name, y.Name);
return nameComparison != 0 ? nameComparison : Compare(x, (JContainer)y);
}
private static int Compare(JContainer x, JContainer y)
{
var countComparison = x.Count.CompareTo(y.Count);
if (countComparison != 0)
{
return countComparison;
}
return x
.Select((t, i) => Comparer.Compare(t, y[i]))
.FirstOrDefault(itemComparison => itemComparison != 0);
}
private static int Compare(JObject x, JObject y)
{
var countComparison = x.Count.CompareTo(y.Count);
if (countComparison != 0)
{
return countComparison;
}
return x.Properties()
.OrderBy(p => p.Name, StringComparer.Ordinal)
.Zip(y.Properties().OrderBy(p => p.Name, StringComparer.Ordinal), (px, py) => Compare(px, py))
.FirstOrDefault(itemComparison => itemComparison != 0);
}
private static int Compare(JProperty x, JProperty y)
{
var nameComparison = string.CompareOrdinal(x.Name, y.Name);
return nameComparison != 0 ? nameComparison : Comparer.Compare(x.Value, y.Value);
}
}
}