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?);
    }
}
相关推荐
考虑考虑9 分钟前
Maven 依赖范围(Scope)
java·后端·maven
张小洛16 分钟前
Spring AOP 设计解密:代理对象生成、拦截器链调度与注解适配全流程源码解析
java·后端·spring·spring aop·aop
00后程序员21 分钟前
iOS 性能测试工具全流程:主流工具实战对比与适用场景
后端
普通程序员24 分钟前
Gemini CLI 新手安装与使用指南
前端·人工智能·后端
我叫黑大帅35 分钟前
Sequelize:让你和数据库唠嗑像聊微信一样简单 😎
后端·node.js
wuxuanok2 小时前
Web后端开发-分层解耦
java·笔记·后端·学习
31535669132 小时前
ClipReader:一个剪贴板英语单词阅读器
前端·后端
ladymorgana2 小时前
【Spring Boot】HikariCP 连接池 YAML 配置详解
spring boot·后端·mysql·连接池·hikaricp
neoooo3 小时前
别慌,Java只有值传递——一次搞懂“为啥我改了它还不变”!
java·后端·spring