0

I can merge JSON objects in JSON.NET but;

I would like to remove JSON object from JSON object like this:

{
"name":"Mike",
"surname":"Dow",
"children":["James","John"],
"data":{"A1":"B1","A2":"B2","A3":"B3"},
"data2":{"A1":"B1","A2":"B2","A3":"B3","A4":"B4"},
"data3":{"A1":{"B1":"C1","B2":"C2"}}
}

minus

{
"name":"Mike",
"children":["James"],
"data":{"A1":"B1"},
"data2":{"A3":"B3","A4":"B4"},
"data3":{"A1":{"B2":"C2"}}
}

equals

{
"surname":"Dow",
"children":["John"],
"data":{"A2":"B2","A3":"B3"},
"data2":{"A1":"B1","A2":"B2"},
"data3":{"A1":{"B1":"C1"}}
}

Is it possible with JSON.NET?

izel
  • 15
  • 1
  • 4

1 Answers1

0

You don't specify how you want to handle array matching. If you simply match array indices, then you can combine JToken.SelectToken with JToken.Path to match values in the subtrahend with values in the minuend:

        var minuend = JToken.Parse(minuendJson);
        var subtrahend = JToken.Parse(subtrahendJson);
        foreach (var toRemove in subtrahend.DescendantsAndSelf().OfType<JValue>().Select(t => minuend.SelectToken(t.Path)).Where(t => t != null).ToList())
            toRemove.RemoveFromLowestPossibleParent();

Using the extension methods:

public static class JsonExtensions
{
    public static void RemoveFromLowestPossibleParent(this JToken node)
    {
        if (node == null)
            throw new ArgumentNullException();
        var contained = node.AncestorsAndSelf().Where(t => t.Parent is JArray || t.Parent is JObject).FirstOrDefault();
        if (contained != null)
            contained.Remove();
    }

    public static IEnumerable<JToken> DescendantsAndSelf(this JToken node)
    {
        if (node == null)
            return Enumerable.Empty<JToken>();
        var container = node as JContainer;
        if (container != null)
            return container.DescendantsAndSelf();
        else
            return new [] { node };
    }
}

If you want to match "leaf" array values by value rather than by index, you could do something like this:

public static class JsonExtensions
{
    public static JToken MatchToken(this JToken target, JToken source)
    {
        var sourceArray = source.Parent as JArray;
        if (sourceArray != null)
        {
            var targetArray = target.SelectToken(sourceArray.Path) as JArray;
            if (targetArray != null)
            {
                // There may be duplicated values in the source and target arrays. If so, get the relative index of the
                // incoming value in the list of duplicates in the source, and return the corresponding value in the list
                // of duplicates in the target.
                var sourceIndices = Enumerable.Range(0, sourceArray.Count).Where(i => JToken.DeepEquals(sourceArray[i], source)).ToList();
                var targetIndices = Enumerable.Range(0, targetArray.Count).Where(i => JToken.DeepEquals(targetArray[i], source)).ToList();
                var matchIndex = sourceIndices.IndexOf(sourceArray.IndexOf(source));
                Debug.Assert(matchIndex >= 0);// Should be found inside its parent.
                if (matchIndex >= 0 && matchIndex < targetIndices.Count)
                    return targetArray[targetIndices[matchIndex]];
                return null;
            }
        }

        return target.SelectToken(source.Path);
    }
}

And then

        var minuend = JToken.Parse(minuendJson);
        var subtrahend = JToken.Parse(subtrahendJson);
        foreach (var toRemove in subtrahend.DescendantsAndSelf().OfType<JValue>().Select(t => minuend.MatchToken(t)).Where(t => t != null).ToList())
            toRemove.RemoveFromLowestPossibleParent();
dbc
  • 104,963
  • 20
  • 228
  • 340