确定JSON对象是否包含在另一个JSON对象中

3pvhb19x  于 2023-05-08  发布在  其他
关注(0)|答案(1)|浏览(247)

一段时间以前,我必须创建一个方法,来确定第一个JSON对象是否包含在第二个JSON对象中。然后我创建了一个非常简单的方法:

public static partial class JsonOperations
{
    [GeneratedRegex(@"\[\d+\]", RegexOptions.Compiled)]
    private static partial Regex FindArrayIndexers();

    private static IEnumerable<string> GetJsonValuesIdentifiers(IEnumerable<JValue> values)
    {
        return values.Select(p => $"{FindArrayIndexers().Replace(p.Path, string.Empty)}:{p.Value?.ToString()}");
    }

    public static bool IsIncludedIn(this JObject first, JObject second)
    {
        var firstIdentifiers = GetJsonValuesIdentifiers(first.Descendants().OfType<JValue>());
        var secondIdentifiers = GetJsonValuesIdentifiers(second.Descendants().OfType<JValue>());

        return firstIdentifiers.All(secondIdentifiers.Contains);
    }
}

它是如何工作的?我得到了JSON对象的后代JValue,所以简单的对象,如int,string等。稍后,我获取每个JValueJSON Path,并使用REGEX从其中删除数组索引器。例如:.type.arr[0].xyz转换为.type.arr.xyz,因此这些值的顺序无关紧要。后来我把它和值结合起来,得到了标识符,例如。.type.arr.xyz:some-string-value。最后我只检查第一个JSON对象中的所有标识符是否包含在第二个JSON对象中。
它似乎工作正常,但我注意到错误,导致严重的安全问题,在我的情况下。
想象一个像这样的对象:

{
   "geolocation":[
      {
         "lat":-32.364,
         "lng":158.207
      },
      {
         "lat":-35.364,
         "lng":153.207
      }
   ]
}

第二个:

{
   "geolocation":[
      {
         "lat":-32.364,
         "lng":153.207 <-- It's exchanged
      },
      {
         "lat":-35.364,
         "lng":158.207 <-- Exchanged with this
      }
   ]
}

我提供的方法会说第一个对象包含在第二个对象中,但这不是真的。是的,我已经提到的“标识符”都包括在内,但是“整体来看”,在这种情况下,我们看到,数组的两个对象表示不同的坐标。
此外,一般假设比较的JSON总是具有相同的结构。如果不是,则第一不能包括在第二中。
我希望你能理解这个问题,这很难为我解释,但如果你有任何问题请问。

zlhcx6iw

zlhcx6iw1#

成功的关键是通过分组为JSON标识符提供更多的上下文。这是完整的工作代码:

public static partial class JsonOperations
{
    [GeneratedRegex(@"\[\d+\]", RegexOptions.Compiled)]
    private static partial Regex FindArrayIndexers();

    private static IEnumerable<IGrouping<string, string>> GetGroupedIdentifiers(IEnumerable<JValue> values)
    {
        // Group by path to last ] characters, so to the last array indexer.
        // Doing this we avoid mixing properties from different objects inside one array but with the same properties with the same values
        return values.GroupBy(p => p.Path.IndexOf(']') != -1 ? p.Path[0..(p.Path.LastIndexOf(']') + 1)] : p.Path,
            v => $"{FindArrayIndexers().Replace(v.Path, string.Empty)}:{v.Value?.ToString()}");
    }

    public static bool IsIncludedIn(this JObject first, JObject second)
    {
        var firstGroupedIdentifiers = GetGroupedIdentifiers(first.Descendants().OfType<JValue>());
        var secondGroupedIdentifiers = GetGroupedIdentifiers(second.Descendants().OfType<JValue>());

        foreach (var firstGrouping in firstGroupedIdentifiers)
        {
            // sg - second grouping
            // fgId - identifier from first grouping
            // sgId - identifier from second grouping
            // Work: whether there is any grouping from secondGroupedIdentifiers where all of first grouping (currently iterated) identifiers are contained in second grouping
            var appropriateGroupingExists = secondGroupedIdentifiers.Any(sg =>
                firstGrouping.Select(fgId => fgId).All(fgId => sg.Select(sgId => sgId).Contains(fgId)));

            if (!appropriateGroupingExists)
                return false;
        }

        return true;
    }
}

我已经测试了问题中提供的JSON,并由以下人员生成:https://json-generator.com
请注意问题中包含的假设。这段代码只在JSON结构相同的情况下工作(根据假设)。如果他们不是,它就不会工作。

相关问题