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?);
    }
}
相关推荐
罗超驿8 分钟前
SpringMVC 请求参数接收详解|单个参数、多参数、对象参数、@RequestParam参数重命名
java·后端·postman·javaee
__zRainy__29 分钟前
Node系列 · ORM:Sequelize 简介
数据库·后端·node.js
AskHarries1 小时前
OpenClaw 如何查看历史对话和响应时间?
后端
n8n1 小时前
Spring Boot 3.2+ 新特性、Spring AI 核心架构与 API Key 安全管理技术文档
后端
fireworks991 小时前
接口鉴权(401与403)
java·后端
n8n1 小时前
Java 21 虚拟线程(Virtual Threads)
后端
Csvn2 小时前
🐍 Day 5: Python 函数详解 — 参数、作用域与一等公民
人工智能·后端
wno7042 小时前
Spring Boot中使用Servlet
spring boot·后端·servlet
老郑聊AI业财智造2 小时前
Spring AI 技术架构与源码分析
java·人工智能·后端·spring·架构·软件工程
省长2 小时前
别人绕过我的网关直接调用资源服务怎么办?使用 Sa-Token 解决:网关转发鉴权、RPC调用鉴权
java·后端·开源