提前致谢。感谢您的帮助。
我想比较两个相同类型和结构的任意JToken(NewtonSoft的Json.Net)。
static int CompareTokens(JToken x, JToken y); // possible output: 0 / 1 / -1
主要目标 是能够使用此方法对两个Json字符串进行排序,以便即使开始时它们具有相同的数据,但顺序不同,最后它们是两个完全相同的字符串。因此排序标准并不重要,重要的是该标准始终相同。并且应该考虑每个小数据元素。
JToken可以是以下几种类型之一:Array, Boolean, Date, Float, Guid, Integer, Null, Object, Property, String, TimeSpan, Uri。我没有考虑比较Bytes, Comment, Constructor, None, Undefined, Raw。
Array, Boolean, Date, Float, Guid, Integer, Null, Object, Property, String, TimeSpan, Uri
Bytes, Comment, Constructor, None, Undefined, Raw
这是一个非常棘手的问题。如果我想出办法,我会加+100。对不起,我的英语。
在Linq-to- JSON中,JValue表示原始值(字符串,数字,布尔值等)。它实现了IComparable<JValue>,因此Json.NET会为您处理原始值的排序。
JValue
IComparable<JValue>
在此基础上,您将需要递归JToken并行地降低两个对象层次结构。当您遇到具有不同.Net类型,不同属性(如果不是JValue)或具有不同值(如果a JValue)的第一个标记时,您需要返回比较值。
JToken
请注意以下几点:
JArray
JConstructor
JObject
JRaw
以下是原型实现:
public class JTokenComparer : IComparer<JToken> { public static JTokenComparer Instance { get { return instance; } } static JTokenComparer instance; static JTokenComparer() { instance = new JTokenComparer(); } readonly Dictionary<Type, KeyValuePair<int, IComparer<JToken>>> dict; JTokenComparer() { dict = new Dictionary<Type, KeyValuePair<int, IComparer<JToken>>> { // Order chosen semi-arbitrarily. Putting values first seems reasonable though. {typeof(JValue), new KeyValuePair<int, IComparer<JToken>>(0, new JValueComparer()) }, {typeof(JProperty), new KeyValuePair<int, IComparer<JToken>>(1, new JPropertyComparer()) }, {typeof(JArray), new KeyValuePair<int, IComparer<JToken>>(2, new JArrayComparer()) }, {typeof(JObject), new KeyValuePair<int, IComparer<JToken>>(3, new JObjectComparer()) }, {typeof(JConstructor), new KeyValuePair<int, IComparer<JToken>>(4, new JConstructorComparer()) }, }; } #region IComparer<JToken> Members public int Compare(JToken x, JToken y) { if (x is JRaw || y is JRaw) throw new InvalidOperationException("Tokens of type JRaw cannot be sorted"); if (object.ReferenceEquals(x, y)) return 0; else if (x == null) return -1; else if (y == null) return 1; var typeData1 = dict[x.GetType()]; var typeData2 = dict[y.GetType()]; int comp; if ((comp = typeData1.Key.CompareTo(typeData2.Key)) != 0) return comp; if (typeData1.Value != typeData2.Value) throw new InvalidOperationException("inconsistent dictionary values"); // Internal error return typeData2.Value.Compare(x, y); } #endregion } abstract class JTokenComparerBase<TJToken> : IComparer<JToken> where TJToken : JToken { protected TJToken CheckType(JToken item) { if (item != null && item.GetType() != typeof(TJToken)) throw new ArgumentException(string.Format("Actual type {0} of token \"{1}\" does not match expected type {2}", item.GetType(), item, typeof(TJToken))); return (TJToken)item; } protected bool TryBaseCompare(TJToken x, TJToken y, out int comparison) { CheckType(x); CheckType(y); if (object.ReferenceEquals(x, y)) { comparison = 0; return true; } else if (x == null) { comparison = -1; return true; } else if (y == null) { comparison = 1; return true; } comparison = 0; return false; } protected abstract int CompareDerived(TJToken x, TJToken y); protected int TokenCompare(JToken x, JToken y) { var tx = CheckType(x); var ty = CheckType(y); int comp; if (TryBaseCompare(tx, ty, out comp)) return comp; return CompareDerived(tx, ty); } #region IComparer<JToken> Members int IComparer<JToken>.Compare(JToken x, JToken y) { return TokenCompare(x, y); } #endregion } abstract class JContainerOrderedComparerBase<TJToken> : JTokenComparerBase<TJToken> where TJToken : JContainer { protected int CompareItemsInOrder(TJToken x, TJToken y) { int comp; // Dictionary order: sort on items before number of items. for (int i = 0, n = Math.Min(x.Count, y.Count); i < n; i++) if ((comp = JTokenComparer.Instance.Compare(x[i], y[i])) != 0) return comp; if ((comp = x.Count.CompareTo(y.Count)) != 0) return comp; return 0; } } class JPropertyComparer : JTokenComparerBase<JProperty> { protected override int CompareDerived(JProperty x, JProperty y) { int comp; if ((comp = x.Name.CompareTo(y.Name)) != 0) return comp; return JTokenComparer.Instance.Compare(x.Value, y.Value); } } class JObjectComparer : JTokenComparerBase<JObject> { protected override int CompareDerived(JObject x, JObject y) { int comp; // Dictionary order: sort on items before number of items. // Order both property sequences to preserve reflexivity. foreach (var propertyComp in x.Properties().OrderBy(p => p.Name).Zip(y.Properties().OrderBy(p => p.Name), (xp, yp) => JTokenComparer.Instance.Compare(xp, yp))) if (propertyComp != 0) return propertyComp; if ((comp = x.Count.CompareTo(y.Count)) != 0) return comp; return 0; } } class JArrayComparer : JContainerOrderedComparerBase<JArray> { protected override int CompareDerived(JArray x, JArray y) { int comp; if ((comp = CompareItemsInOrder(x, y)) != 0) return comp; return 0; } } class JConstructorComparer : JContainerOrderedComparerBase<JConstructor> { protected override int CompareDerived(JConstructor x, JConstructor y) { int comp; if ((comp = x.Name.CompareTo(y.Name)) != 0) return comp; if ((comp = CompareItemsInOrder(x, y)) != 0) return comp; return 0; } } class JValueComparer : JTokenComparerBase<JValue> { protected override int CompareDerived(JValue x, JValue y) { return Comparer<JToken>.Default.Compare(x, y); // JValue implements IComparable<JValue> } }
经过严格测试的原型小提琴。