gpt4 book ai didi

c# - 比较两个相同结构的任意 JToken-s

转载 作者:太空狗 更新时间:2023-10-29 21:08:22 26 4
gpt4 key购买 nike

我想比较两个相同类型和结构的任意 JTokens(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

  • 如果能对比较 JArraysJObjects 有一些想法,那就太好了。这应该是一些递归比较,因为 JArrays 可能由其他 JArraysJObjects 组成,反之亦然。任何想法将不胜感激。
  • 但是了解如何比较更简单的类型也会很有帮助。我想知道如何将 JToken 转换为实际类型(而不是知道如何从逻辑上做到这一点)。
  • JValue 实现了 IComparable,但我不知道如何将简单类型的 JToken 转换为 JValue。了解这一点也会有所帮助。

最佳答案

在 Linq-to-JSON 中, JValue 表示原始值(字符串、数字、 bool 值等)。它实现了 IComparable<JValue> ,因此 Json.NET 负责为您对原始值进行排序。

在此基础上,您将需要递归地下降两个 JToken并行的对象层次结构。当您遇到具有不同 .Net 类型或不同属性(如果不是 JValue )或具有不同值(如果是 JValue )的第一个标记时,您需要返回比较值。

请记住以下几点:

  • 比较方法应该是自反的、反对称的和传递的。
  • 不同 .Net 类型的容器 token 需要以某种一致的方式按类型排序。
  • JArray 的子标记和 JConstructor已订购。
  • JObject 的子标记不是,因此需要以某种稳定、对称的方式对它们进行比较。按属性名称 的顺序遍历两者似乎可行。
  • 没有明显的方法来比较JRaw ,所以不要尝试,让异常被抛出。

下面是一个原型(prototype)实现:

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>
}
}

轻微测试 prototype fiddle .

关于c# - 比较两个相同结构的任意 JToken-s,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33022993/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com