You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决JSON对比复杂对象时null与空值视为不等的问题

解决复杂对象深度对比时Null与空值视为不等的问题

问题背景

我有两个复杂类对象需要判断是否相等,当前采用的步骤是:

  1. 使用Newtonsoft将对象转为JSON字符串
  2. 将JSON转换为JObject
  3. 调用JObject.DeepEquals方法进行深度对比

但实际场景中,一个对象的部分属性为null,另一个对应属性为空值(比如空字符串、空集合),导致JObject.DeepEquals始终判定二者不等,而从业务逻辑上这两种情况应视为相等。需要找到方案统一处理null与空值,实现正确的相等判定。

示例类结构:

public class Example 
{ 
    public List<ClassA> classA { get; set; } 
    public List<ClassB> classB { get; set; } 
    public List<ClassC> ClassC { get; set; }
    public List<ClassD> classD { get; set; } 
    public string identifier { get; set; } 
}

public class ClassA
{
    public int value { get; set; }
    public string text { get; set; }
    public DateTime date { get; set; }
}

public class ClassB
{
    public string text1 { get; set; }
    public string text2 { get; set; }
    public DateTime date1 { get; set; }
    public List<CSettings> setting { get; set; }
    // 其他属性...
}

解决方案

方案一:序列化时统一处理Null与空值

利用Newtonsoft.Json的序列化配置,将null值与空值统一为同一表现形式,再生成JObject进行对比。

自定义序列化配置

var serializerSettings = new JsonSerializerSettings
{
    // 忽略null值,或设置为Populate填充默认值
    NullValueHandling = NullValueHandling.Ignore,
    DefaultValueHandling = DefaultValueHandling.Populate,
    Converters = new List<JsonConverter>
    {
        new NullCollectionConverter()
    }
};

// 自定义转换器:将null集合转为空集合
public class NullCollectionConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(List<>);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
        {
            return Activator.CreateInstance(objectType);
        }
        return serializer.Deserialize(reader, objectType);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var collection = value as IEnumerable;
        if (collection == null || !collection.GetEnumerator().MoveNext())
        {
            writer.WriteNull(); // 空集合序列化为null,也可改为序列化空集合
        }
        else
        {
            serializer.Serialize(writer, value);
        }
    }
}

执行对比

var jObj1 = JObject.FromObject(obj1, JsonSerializer.Create(serializerSettings));
var jObj2 = JObject.FromObject(obj2, JsonSerializer.Create(serializerSettings));

bool isEqual = JObject.DeepEquals(jObj1, jObj2);

方案二:提前标准化对象的Null/空值

在序列化前,递归遍历对象所有属性,将null转换为对应类型的空值(或反之),再执行原对比流程。

标准化方法实现

public static void NormalizeNullValues(object obj)
{
    if (obj == null) return;
    var type = obj.GetType();

    // 处理值类型与字符串
    if (type.IsValueType || type == typeof(string))
    {
        return;
    }

    // 处理集合
    if (obj is IEnumerable enumerable)
    {
        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
        {
            foreach (var item in enumerable)
            {
                NormalizeNullValues(item);
            }
        }
        return;
    }

    // 处理类属性
    foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
    {
        if (!prop.CanWrite) continue;

        var value = prop.GetValue(obj);
        if (value == null)
        {
            if (prop.PropertyType == typeof(string))
            {
                prop.SetValue(obj, string.Empty);
            }
            else if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(List<>))
            {
                var emptyList = Activator.CreateInstance(prop.PropertyType);
                prop.SetValue(obj, emptyList);
            }
            // 可扩展处理其他类型,比如Dictionary等
        }
        else
        {
            NormalizeNullValues(value);
        }
    }
}

执行对比

// 先标准化两个对象
NormalizeNullValues(obj1);
NormalizeNullValues(obj2);

// 原流程对比
var jObj1 = JObject.FromObject(obj1);
var jObj2 = JObject.FromObject(obj2);
bool isEqual = JObject.DeepEquals(jObj1, jObj2);

方案三:自定义深度对比逻辑

跳过序列化步骤,直接递归对比对象属性,在对比时将null与对应空值视为相等。

自定义对比方法

public static bool DeepEqualsWithNullNormalization(object obj1, object obj2)
{
    if (obj1 == obj2) return true;
    if (obj1 == null || obj2 == null)
    {
        return IsEmptyValue(obj1) && IsEmptyValue(obj2);
    }

    var type1 = obj1.GetType();
    var type2 = obj2.GetType();
    if (type1 != type2) return false;

    // 处理字符串:null与空字符串视为相等
    if (type1 == typeof(string))
    {
        return string.IsNullOrEmpty((string)obj1) && string.IsNullOrEmpty((string)obj2);
    }

    // 处理值类型
    if (type1.IsValueType)
    {
        return Equals(obj1, obj2);
    }

    // 处理集合
    if (obj1 is IEnumerable enum1 && obj2 is IEnumerable enum2)
    {
        var enum1Enumerator = enum1.GetEnumerator();
        var enum2Enumerator = enum2.GetEnumerator();

        while (enum1Enumerator.MoveNext())
        {
            if (!enum2Enumerator.MoveNext()) return false;
            if (!DeepEqualsWithNullNormalization(enum1Enumerator.Current, enum2Enumerator.Current))
            {
                return false;
            }
        }
        return !enum2Enumerator.MoveNext();
    }

    // 处理类属性
    foreach (var prop in type1.GetProperties(BindingFlags.Public | BindingFlags.Instance))
    {
        var val1 = prop.GetValue(obj1);
        var val2 = prop.GetValue(obj2);
        if (!DeepEqualsWithNullNormalization(val1, val2))
        {
            return false;
        }
    }

    return true;
}

// 判断是否为空值(null、空字符串、空集合等)
private static bool IsEmptyValue(object obj)
{
    if (obj == null) return true;
    if (obj is string str) return string.IsNullOrEmpty(str);
    if (obj is IEnumerable enumObj) return !enumObj.GetEnumerator().MoveNext();
    // 值类型默认值是否视为空,可根据业务调整
    return obj.Equals(Activator.CreateInstance(obj.GetType()));
}

执行对比

bool isEqual = DeepEqualsWithNullNormalization(obj1, obj2);

内容的提问来源于stack exchange,提问作者Laxmanan S

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 10:02:03