【问题标题】:LINQ Except method but for JSON in C#LINQ Except 方法但用于 C# 中的 JSON
【发布时间】:2023-02-26 10:29:06
【问题描述】:

您可能知道 C# Except() 方法,该方法从第一个集合中删除第二个集合中包含的元素。我必须为 JSON 编写类比方法。

那就是我所做的:

public static JsonDocument Except(this JsonDocument firstJson, JsonDocument secondJson)
    {
        if (firstJson.RootElement.ValueKind != secondJson.RootElement.ValueKind)
            throw new JsonException($"The '{nameof(firstJson)}' and '{nameof(secondJson)}' must be the same kind of value");

        switch (firstJson.RootElement.ValueKind)
        {
            case JsonValueKind.Object:
                var result = JsonNode.Parse("{}")!;

                var firstJsonProperties = firstJson.RootElement.EnumerateObject();
                foreach (var firstJsonProperty in firstJsonProperties)
                {
                    if (!secondJson.RootElement.TryGetProperty(firstJsonProperty.Name, out JsonElement secondJsonPropertyValue)
                        || secondJsonPropertyValue.ValueKind != firstJsonProperty.Value.ValueKind)
                    {
                        result[firstJsonProperty.Name] = firstJsonProperty.Value.AsNode();
                        continue;
                    }
                    else if (firstJsonProperty.Value.ValueKind == JsonValueKind.Object)
                    {
                        var objectExceptionResult = Except(firstJsonProperty.Value.ToJsonDocument(), secondJsonPropertyValue.ToJsonDocument());
                        if (objectExceptionResult.RootElement.AsNode()!.AsObject().Any())
                            result[firstJsonProperty.Name] = objectExceptionResult.RootElement.AsNode();
                    }
                    else if (firstJsonProperty.Value.ValueKind == JsonValueKind.Array)
                    {
                        var arrayExceptionResult = Except(firstJsonProperty.Value.ToJsonDocument(), secondJsonPropertyValue.ToJsonDocument());
                        if (arrayExceptionResult.RootElement.AsNode()!.AsArray().Any())
                            result[firstJsonProperty.Name] = arrayExceptionResult.RootElement.AsNode();
                    }
                }

                return result.ToJsonDocument();

            case JsonValueKind.Array:
                var result2 = new JsonArray();

                var firstJsonElements = firstJson.RootElement.EnumerateArray();
                var secondJsonElements = secondJson.RootElement.EnumerateArray();

                foreach (var firstJsonElement in firstJsonElements)
                {
                    foreach (var secondJsonElement in secondJsonElements)
                    {
                        if (firstJsonElement.ValueKind != secondJsonElement.ValueKind)
                            continue;

                        if (firstJsonElement.ValueKind == JsonValueKind.Object || firstJsonElement.ValueKind == JsonValueKind.Array)
                        {
                            var exceptionResult = Except(firstJsonElement.ToJsonDocument(), secondJsonElement.ToJsonDocument());
                            if (!firstJsonElement.IsEquivalentTo(exceptionResult.RootElement))
                            {
                                if (exceptionResult.RootElement.AsNode()!.AsObject().Any())
                                    result2.Add(exceptionResult);

                                break;
                            }
                            else if (secondJsonElement.IsEquivalentTo(secondJsonElements.Last()))
                            {
                                result2.Add(firstJsonElement);
                            }
                        }
                    }

                    if (firstJsonElement.ValueKind != JsonValueKind.Object && firstJsonElement.ValueKind != JsonValueKind.Array
                    && !secondJsonElements.Any(p => p.ToString() == firstJsonElement.ToString()))
                        result2.Add(firstJsonElement);
                }

                return result2.ToJsonDocument();

            default:
                if (!firstJson.RootElement.IsEquivalentTo(secondJson.RootElement))
                    return firstJson;

                break;
        }

        return firstJson;
    }

这段代码并不漂亮。但更糟糕的事情发生了。它有时不起作用。

对于像这样的简单 JSON 数据:

var firstJson = JsonDocument.Parse(@"{
   ""x"":""haha"",
   ""a"":{
      ""z"":1,
      ""b"":3
   },
   ""haff"": [
     1,
     2,
    {
    ""a"": 4,
""b"": 5
}
    ]
}");

var secondJson = JsonDocument.Parse(@"{
   ""x"": 1024,
   ""b"":""afa"",
   ""a"":{
      ""z"":3,
      ""a"":4
   },
   ""haff"": [
     1,
     2,
    {
    ""a"": 5
}
    ]
}");

它工作得很好。但是当第一个和第二个 JSON 都是数组并且第一个的元素少于第二个时,就会发生不好的事情。然后并不是所有适当的元素都从第一个 JSON 中删除。

我真的不知道为什么会这样。你知道这段代码出了什么问题吗?或者,也许您知道可以使用提供此功能的 NuGet 包?

如果您需要更多详细信息,请评论我的问题。

注意:我在此代码中使用 Json.More.Net NuGet 包。

通常,代码应:

  1. 如果该属性存储简单结构(字符串、整数等),那么它是键值对,如果它也包含在第二个 JSON 中,则应删除该属性
  2. 如果属性存储数组,则将从数组中删除所有也包含在第二个 JSON 中的适当数组中的元素。
  3. 如果属性存储对象,则将从该对象中删除属性,这些属性也包含在第二个 JSON 中的适当对象中。
  4. 如果从数组或对象中删除所有数据,它也应删除整个对象或数组。

    这是调用 Except(firstJson, secondJson) 的结果(上面定义的变量):

    {
       "x":"haha",
       "a":{
          "b":3
       },
       "haff":[
          {
             "b":5
          }
       ]
    }
    

    当调用Except(secondJson, firstJson)(所以我用 secondJson 交换了 firstJson)时,结果将如下所示:

    {
       "x":1024,
       "b":"afa",
       "a":{
          "a":4
       }
    }
    

    看起来很简单,但请记住,数组可以包含对象,而对象又包含另一个数组,数组又包含另一个对象等。

【问题讨论】:

  • 没有人会阅读你的代码。这个资源太多了。如果您需要一些代码审查,这里有一个专门的论坛。因此,除了数组或对象之外,您的问题还不清楚您要做什么?整个对象还是只有属性?
  • 我添加了关于代码应该做什么的描述。顺便说一句,我在 codereview 论坛上,我的问题被删除了,因为它不是完全有效的代码。
  • 感谢 jsons,但我还是不明白你想要什么。你能发布你想要获得的最终 json 吗?
  • 您是在比较属性名称还是值?
  • 我已经编辑了我的问题。我正在比较属性名称和值。如果属性名称匹配,那么我检查该属性存储的数据类型是否也匹配。

标签: c# json


【解决方案1】:

我更喜欢使用 Newtonsoft.Json。这是我的解决方案,这段代码对我来说更清楚

using Newtonsoft.Json;

var result = ExceptJsons(secondJsonString, firstJsonString);

public static JObject ExceptJsons(string firstJsonString, string secondJsonString)
{
    var firstJsonObj = JObject.Parse(firstJsonString);
    var secondJsonObj = JObject.Parse(secondJsonString);

    var fjp = GetPropPathes(firstJsonObj);
    var sjp = GetPropPathes(secondJsonObj);

    var result = fjp.Except(sjp).ToList();

    var ToRemove = firstJsonObj.DescendantsAndSelf().OfType<JProperty>().Where(x => !result.Contains(((JProperty)x).Path))
    .Where(x => !(((JProperty)x).Value.Type == JTokenType.Object || ((JProperty)x).Value.Type == JTokenType.Array)).ToList();

    for (var i = ToRemove.Count() - 1; i >= 0; i--)
    {
        if (secondJsonObj.SelectToken(ToRemove[i].Path).Type == ToRemove[i].Value.Type)
            ToRemove[i].Remove();
    }

    ExceptValues(firstJsonObj, secondJsonObj);
    var fvl = firstJsonObj.Properties().ToList();
    for (var j = fvl.Count() - 1; j >= 0; j--)
    {

        if (fvl[j].Value.Type == JTokenType.Array)
        {
            if (fvl[j].Value.Count() == 0) fvl[j].Remove();
        }
        else if (fvl[j].Value.Type == JTokenType.Object)
        {
            if (((JObject)fvl[j].Value).Properties().Count() == 0) fvl[j].Remove();
        }
    }

    return firstJsonObj;
}

public static void ExceptValues(JObject firstJsonObj, JObject secondJsonObj)
{
    var firstValues = GetValues(firstJsonObj).ToList();
    var secondValues = GetValues(secondJsonObj).ToList();
    for (var i = 0; i < firstValues.Count(); i++)
    {
        var secondValue = secondValues.Where(x => x["path"] == firstValues[i]["path"]).FirstOrDefault();
        var newItems = firstValues[i]["items"].Distinct().Except(secondValues[i]["items"].Distinct());

        string p = (string)firstValues[i]["path"];

        var fv = (JArray)firstJsonObj.SelectToken(p);

        for (var j = fv.Count() - 1; j >= 0; j--)
        {
            if (fv[j].Type == JTokenType.Array)
            {
                if (fv[j].Count() > 0) continue;
            }
            else if (fv[j].Type == JTokenType.Object)
            {
                if (((JObject)fv[j]).Properties().Count() > 0) continue;
            }

            fv[j].Remove();
        }

        foreach (var item in newItems) fv.Add(item);

        if (fv.Count() == 0) firstJsonObj.Remove(fv.Path);
    }
}

public static List<string> GetPropPathes(JObject jsonObj)
{
    return jsonObj.DescendantsAndSelf().OfType<JProperty>()
         .Where(x => !(((JProperty)x).Value.Type == JTokenType.Array
         || ((JProperty)x).Value.Type == JTokenType.Object))
         .Select(x => x.Path).ToList();
}

public static List<JObject> GetValues(JObject jsonObj)
{
    return jsonObj.DescendantsAndSelf().OfType<JArray>().Select(y => new JObject
    {
        ["path"] = y.Path,
        ["items"] = new JArray(y.Where(x =>
         !(x.Type == JTokenType.Array || x.Type == JTokenType.Object)))
    }).ToList();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-23
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 2019-07-14
    • 2015-03-30
    • 1970-01-01
    相关资源
    最近更新 更多