C# JSON属性排序、比较 Newtonsoft.Json

用法目的

  1. 对比两个JSON对象是否完全相等
  2. 用于参数匹配

排序 实现代码:

cs 复制代码
// JsonHelper.cs
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

public static class JsonHelper
{
    
    /// <summary>
    /// 对JSON属性进行升序排序
    /// </summary>
    public static JToken GetSortedObject(JToken obj)
    {
        if (obj is JArray jArray)
        {
            var jArray2 = new JArray();
            foreach (var item in jArray)
            {
                jArray2.Add(GetSortedObject(item));
            }
            return jArray2;
        }
        else if (obj is JObject jObject)
        {
            var sortedDic = new SortedDictionary<string, JToken>();
            foreach (var jProperty in jObject.Properties())
            {
                if (jProperty.Value is JArray || jProperty.Value is JObject)
                {
                    sortedDic.Add(jProperty.Name, GetSortedObject(jProperty.Value));
                }
                else
                {
                    sortedDic.Add(jProperty.Name, jProperty.Value);
                }
            }

            var newJObject = new JObject();
            foreach ((var key, var value) in sortedDic)
            {
                newJObject.Add(key, value);
            }

            return newJObject;
        }
        else
        {
            return obj;
        }
    }
}

比较实现代码

cs 复制代码
// JsonHelper.cs
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

public static class JsonHelper
{
    
    public static bool JValueEquals(JToken? obj1, JToken? obj2)
    {
        if (obj1 is JArray jArray && obj2 is JArray jArray2)
        {
            var jarr2_items = jArray2.ToList();
            foreach (var item1 in jArray)
            {
                var item2 = jarr2_items.PickItem(x => JValueEquals(item1, x));
                if (item2 is null)
                {
                    return false;
                }
            }

            return true;
        }
        else if (obj1 is JObject jObject && obj2 is JObject jObject2)
        {
            var j2_props = jObject2.Properties().ToList();
            foreach (var p1 in jObject.Properties())
            {
                var p2 = j2_props.FirstOrDefault(x => x.Name == p1.Name);
                if (p2 is null || !JValueEquals(p1.Value, p2.Value))
                {
                    return false;
                }
            }

            return true;
        }
        else if (obj1 is JValue v1 && obj2 is JValue v2)
        {
            if ((v1.Type == JTokenType.Float && (v2.Type == JTokenType.Float || v2.Type == JTokenType.Integer))
                || v2.Type == JTokenType.Float && v1.Type == JTokenType.Integer)
            {
                if (v1.Value == v2.Value)
                {
                    return true;
                }

                if (v1.Value == null || v2.Value == null)
                {
                    return false;
                }

                var d1 = v1.Type == JTokenType.Integer ? (long)v1.Value : (double)v1.Value;
                var d2 = v2.Type == JTokenType.Integer ? (long)v2.Value : (double)v2.Value;

                return DoubleEqual(d1, d2);
            }

            if (v1.Type == JTokenType.Integer && v2.Type == JTokenType.Integer)
            {
                if (v1.Value == v2.Value)
                {
                    return true;
                }

                if (v1.Value == null || v2.Value == null)
                {
                    return false;
                }

                return (long)v1.Value == (long)v2.Value;
            }

            return Object.Equals(v1.Value, v2.Value);
        }

        return Object.Equals(obj1, obj2);
    }
    
    /// <summary>
    /// 浮点数相等
    /// </summary>
    /// <param name="d"></param>
    /// <param name="d2"></param>
    /// <returns></returns>
    public static bool DoubleEqual(double? d, double? d2)
    {
        // https://learn.microsoft.com/zh-cn/dotnet/api/system.double.epsilon?view=net-7.0
        // ReSharper disable once CompareOfFloatsByEqualityOperator
        return d == d2 || (d.HasValue && d2.HasValue && Math.Abs(d.Value - d2.Value) < (2.2250738585072014E-308));
    }
}

// IListExtension.cs
using System.Collections.ObjectModel;
public static class IListExtension
{
   public static T? PickItem<T>(this IList<T> list, Predicate<T> predicate)
    {
        T data;
        for (int i = 0; i < list.Count; i++)
        {
            if (predicate(list[i]))
            {
                data = list[i];
                list.RemoveAt(i);
                return data;
            }
        }

        return default(T?);
    }
}
相关推荐
weixin_446260853 小时前
HACO:面向动态部署环境的对冲式智能计算可靠多智能体调度框架
后端·python·flask
ttwuai4 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
用户8356290780514 小时前
Python 实现 Excel 页面布局与打印设置自动化
后端·python
用户9931441579844 小时前
微服务框架中获取用户信息
后端
xuanWb4 小时前
手写一个 LLM API 网关:Anthropic 与 OpenAI 协议转换的完整实现
后端
苍何5 小时前
给 Codex 换皮肤这门生意,被我开源了
后端
用户8356290780515 小时前
Python 实现 Excel 命名范围(Named Range)的创建与管理
后端·python
程序员David5 小时前
我让 Claude 从架构文档一路干到代码,踩了三个坑才摸清边界
后端
Zane19945 小时前
并发 vs 并行:别再傻傻分不清了,一文讲透 Java 并发编程的第一课
java·后端