比较两个相同结构的任意JToken [英] Compare two arbitrary JToken-s of the same structure

查看:107
本文介绍了比较两个相同结构的任意JToken的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

先谢谢了.感谢您的帮助.

Thanks in advance. I appreciate any help.

我想比较两个相同类型和结构的任意JToken(NewtonSoft的Json.Net).

I would like to compare two arbitrary JTokens of the same type and structure (Json.Net from NewtonSoft).

static int CompareTokens(JToken x, JToken y);  
// possible output: 0 / 1 / -1

主要目标是能够使用此方法对两个Json字符串进行排序,以便即使它们一开始就具有相同的数据,但顺序不同,最后却是两个完全相同的字符串.因此排序标准并不重要,重要的是该标准始终相同.并且每个小数据元素都应考虑在内.

The main goal is to be able use this method to sort two Json strings, so that even if in the begining they had the same data, but in the different order, in the end these are two exactly same strings. So the sort criterion doesn't really matter, it just matters that this criterion is always the same. And each small element of data should be taken into account.

JToken可以是以下几种类型之一:Array, Boolean, Date, Float, Guid, Integer, Null, Object, Property, String, TimeSpan, Uri.我没有考虑比较Bytes, Comment, Constructor, None, Undefined, Raw.

JToken can be of one of next several types: Array, Boolean, Date, Float, Guid, Integer, Null, Object, Property, String, TimeSpan, Uri. I don't take into account comparing Bytes, Comment, Constructor, None, Undefined, Raw.

  • 获得一些关于比较 JArrays JObjects 的想法将是很棒的.这应该是一些递归比较,因为 JArrays 可能由其他 JArrays JObjects 组成,反之亦然.任何想法将不胜感激.
  • 但是了解比较简单类型的方法也将非常有帮助.我不知道知道如何将 JToken 转换为实际类型(而不是知道如何逻辑地进行转换).
  • JValue 已实现 IComparable ,但我不知道如何将简单类型的 JToken 转换为 JValue .了解这一点也将有所帮助.
  • It would be great to gain some idea about comparing JArrays and JObjects. That shoud be some recursive comparison, because JArrays may consist of other JArrays and JObjects and vice versa. Any idea would be appreciated.
  • But knowing about comparing simpler types would also be very helpful. I wonder rather about knowing how to convert from JToken to actual type (than about knowing how to do it logically).
  • JValue has IComparable implemented, but i didn't figure out how to convert simple typed JToken to JValue. Knowing about this would also be helpful.

这是一个非常棘手的问题.如果我想出办法,我会在上面加上+100.对不起,我的英语.

This is quite a havy question. If i figure out how to do it, i'll put a +100 on it. And sorry for my English.

推荐答案

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

In Linq-to-JSON, JValue represents a primitive value (string, number, boolean, and so on). It implements IComparable<JValue>, so Json.NET takes care of sorting primitive values for you.

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

Building off of that, you're going to need to recursively descend the two JToken object hierarchies in parallel. When you encounter the first token with a different .Net type, or different properties (if not a JValue), or with a different value (if a JValue), you need to return back the comparison value.

请注意以下几点:

  • 比较方法应该是自反的,反对称的和可传递的.
  • .net类型不同的容器令牌需要以某种一致的方式按类型排序.
  • JArrayJConstructor的子标记是有序的.
  • JObject的子标记不是,因此需要以某种稳定,对称的方式进行比较.都按属性名称的顺序走似乎很有效.
  • 没有明显的方法可以比较JRaw,因此请不要尝试,并引发异常.
  • A comparison method should be reflexive, antisymmetric and transitive.
  • Container tokens of different .Net type need to be ordered by type in some consistent manner.
  • the child tokens of JArray and JConstructor are ordered.
  • the child tokens of JObject are not, so they need to be compared in some stable, symmetric manner. Walking both in order of property name would seem to work.
  • There is no obvious way to compare JRaw, so don't try, and let an exception get thrown.

以下是原型实现:

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

经过严格测试的原型小提琴.

这篇关于比较两个相同结构的任意JToken的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆