it-swarm.com.de

JSON-Unterschiede mithilfe von Newtonsoft in C # suchen und zurückgeben?

Ich möchte eine Liste der JSON-Teile erhalten, die bei einem Vergleich mit Newtonsoft nicht übereinstimmen.

Ich habe diesen Code, der Folgendes vergleicht:

JObject xpctJSON = JObject.Parse(expectedJSON);
JObject actJSON = JObject.Parse(actualJSON);

bool res = JToken.DeepEquals(xpctJSON, actJSON);

Kann aber nichts finden, was die Unterschiede zurückgibt.

28
user167908

Nur um zukünftige Anfragen zu erleichtern. Es gibt ein Nice-Json-Diff-Tool, auf das ich gestoßen bin. Es funktioniert einwandfrei für diff/patch von Json-Strukturen:

jsondiffpatch.net Es gibt auch ein Nuget-Paket dafür. 

die Verwendung ist unkompliziert.

var jdp = new JsonDiffPatch();
JToken diffResult = jdp.Diff(leftJson, rightJson);
14
Achilles

Hier ist eine rekursive Version, die ich geschrieben habe. Sie rufen CompareObjects mit zwei JObjects auf und geben eine Liste der Unterschiede zurück. Sie rufen CompareArrays mit zwei JArrays auf und vergleichen die Arrays. Arrays und Objekte können ineinander verschachtelt sein.

UPDATE: @nttakr weist im folgenden Kommentar darauf hin, dass es sich bei dieser Methode eigentlich um einen partiellen Differenzalgorithmus handelt. Es informiert Sie nur über Unterschiede aus der Sicht der Quellliste. Wenn ein Schlüssel in der Quelle nicht vorhanden ist, aber in der Zielliste vorhanden ist, wird dieser Unterschied ignoriert. Dies ist beabsichtigt für meine Testanforderungen. Auf diese Weise können Sie nur die gewünschten Elemente testen, ohne sie vor dem Vergleich aus dem Ziel löschen zu müssen.

    /// <summary>
    /// Deep compare two NewtonSoft JObjects. If they don't match, returns text diffs
    /// </summary>
    /// <param name="source">The expected results</param>
    /// <param name="target">The actual results</param>
    /// <returns>Text string</returns>

    private static StringBuilder CompareObjects(JObject source, JObject target)
    {
        StringBuilder returnString = new StringBuilder();
        foreach (KeyValuePair<string, JToken> sourcePair in source)
        {
            if (sourcePair.Value.Type == JTokenType.Object)
            {
                if (target.GetValue(sourcePair.Key) == null)
                {
                    returnString.Append("Key " + sourcePair.Key
                                        + " not found" + Environment.NewLine);
                }
                else if (target.GetValue(sourcePair.Key).Type != JTokenType.Object) {
                    returnString.Append("Key " + sourcePair.Key
                                        + " is not an object in target" + Environment.NewLine);
                }                    
                else
                {
                    returnString.Append(CompareObjects(sourcePair.Value.ToObject<JObject>(),
                        target.GetValue(sourcePair.Key).ToObject<JObject>()));
                }
            }
            else if (sourcePair.Value.Type == JTokenType.Array)
            {
                if (target.GetValue(sourcePair.Key) == null)
                {
                    returnString.Append("Key " + sourcePair.Key
                                        + " not found" + Environment.NewLine);
                }
                else
                {
                    returnString.Append(CompareArrays(sourcePair.Value.ToObject<JArray>(),
                        target.GetValue(sourcePair.Key).ToObject<JArray>(), sourcePair.Key));
                }
            }
            else
            {
                JToken expected = sourcePair.Value;
                var actual = target.SelectToken(sourcePair.Key);
                if (actual == null)
                {
                    returnString.Append("Key " + sourcePair.Key
                                        + " not found" + Environment.NewLine);
                }
                else
                {
                    if (!JToken.DeepEquals(expected, actual))
                    {
                        returnString.Append("Key " + sourcePair.Key + ": "
                                            + sourcePair.Value + " !=  "
                                            + target.Property(sourcePair.Key).Value
                                            + Environment.NewLine);
                    }
                }
            }
        }
        return returnString;
    }

    /// <summary>
    /// Deep compare two NewtonSoft JArrays. If they don't match, returns text diffs
    /// </summary>
    /// <param name="source">The expected results</param>
    /// <param name="target">The actual results</param>
    /// <param name="arrayName">The name of the array to use in the text diff</param>
    /// <returns>Text string</returns>

    private static StringBuilder CompareArrays(JArray source, JArray target, string arrayName = "")
    {
        var returnString = new StringBuilder();
        for (var index = 0; index < source.Count; index++)
        {

            var expected = source[index];
            if (expected.Type == JTokenType.Object)
            {
                var actual = (index >= target.Count) ? new JObject() : target[index];
                returnString.Append(CompareObjects(expected.ToObject<JObject>(),
                    actual.ToObject<JObject>()));
            }
            else
            {

                var actual = (index >= target.Count) ? "" : target[index];
                if (!JToken.DeepEquals(expected, actual))
                {
                    if (String.IsNullOrEmpty(arrayName))
                    {
                        returnString.Append("Index " + index + ": " + expected
                                            + " != " + actual + Environment.NewLine);
                    }
                    else
                    {
                        returnString.Append("Key " + arrayName
                                            + "[" + index + "]: " + expected
                                            + " != " + actual + Environment.NewLine);
                    }
                }
            }
        }
        return returnString;
    }
12
Walter

Dies ist eine relativ alte Frage, bietet jedoch eine der möglichen Lösungen, um dieses Problem zu lösen. Angenommen, das gewünschte Ergebnis ist genau die Eigenschaftswerte, die geändert werden

   string sourceJsonString = "{'name':'John Doe','age':'25','hitcount':34}";
   string targetJsonString = "{'name':'John Doe','age':'26','hitcount':30}";

   JObject sourceJObject = JsonConvert.DeserializeObject<JObject>(sourceJsonString);
   JObject targetJObject = JsonConvert.DeserializeObject<JObject>(targetJsonString);

   if (!JToken.DeepEquals(sourceJObject, targetJObject))
   {
     foreach (KeyValuePair<string, JToken> sourceProperty in sourceJObject)
     {
         JProperty targetProp = targetJObject.Property(sourceProperty.Key);

          if (!JToken.DeepEquals(sourceProperty.Value, targetProp.Value))
          {
              Console.WriteLine(string.Format("{0} property value is changed", sourceProperty.Key));
          }
          else
          {
              Console.WriteLine(string.Format("{0} property value didn't change", sourceProperty.Key));
          }
      }
   }
   else
   {
      Console.WriteLine("Objects are same");
   }  

Hinweis: Dies wurde nicht für sehr tiefe Hierarchien getestet.

12
Pravin

Beachten Sie die folgenden Bibliotheken:

using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

Ich bin nicht ganz sicher, ob ich Ihre Frage richtig verstehe ... Ich gehe davon aus, dass Sie versuchen herauszufinden, welche Schlüssel in der eigentlichen JSON fehlen.

Wenn Sie nur an den fehlenden KEYS interessiert sind, hilft Ihnen der folgende Code. Wenn nicht, geben Sie bitte ein Beispiel für die Unterschiede, die Sie zu identifizieren versuchen.

  public IEnumerable<JProperty> DoCompare(string expectedJSON, string actualJSON)
    {
        // convert JSON to object
        JObject xptJson = JObject.Parse(expectedJSON);
        JObject actualJson = JObject.Parse(actualJSON);

        // read properties
        var xptProps = xptJson.Properties().ToList();
        var actProps = actualJson.Properties().ToList();

        // find missing properties
        var missingProps = xptProps.Where(expected => actProps.Where(actual => actual.Name == expected.Name).Count() == 0);

        return missingProps;
    }

HINWEIS: Wenn diese Methode ein leeres IEnumerable zurückgibt, verfügt der ACTUAL-JSON über alle erforderlichen Schlüssel gemäß der Struktur des erwarteten JSON.

ANMERKUNG: Der eigentliche JSON könnte noch weitere Schlüssel enthalten, die der erwartete JSON nicht benötigt.

um meine notizen weiter zu erklären ...

nehmen Sie an, Ihre erwartete JSON ist:

{ Id: 1, Name: "Item One", Value: "Sample" }

und dass Ihr ACTUAL JSON ist:

{ Id: 1, Name: "Item One", SomeProp: "x" }

die obige Funktion zeigt Ihnen an, dass die Value-Taste fehlt, erwähnt aber nichts über die SomeProp-Taste ... es sei denn, Sie tauschen die Eingabeparameter aus.

3
WaseemS

Meine Lösung basiert auf den Ideen der vorherigen Antworten:

public static JObject FindDiff(this JToken Current, JToken Model)
{
    var diff = new JObject();
    if (JToken.DeepEquals(Current, Model)) return diff;

    switch(Current.Type)
    {
        case JTokenType.Object:
            {
                var current = Current as JObject;
                var model = Model as JObject;
                var addedKeys = current.Properties().Select(c => c.Name).Except(model.Properties().Select(c => c.Name));
                var removedKeys = model.Properties().Select(c => c.Name).Except(current.Properties().Select(c => c.Name));
                var unchangedKeys = current.Properties().Where(c => JToken.DeepEquals(c.Value, Model[c.Name])).Select(c => c.Name);
                foreach (var k in addedKeys)
                {
                    diff[k] = new JObject
                    {
                        ["+"] = Current[k]
                    };
                }
                foreach (var k in removedKeys)
                {
                    diff[k] = new JObject
                    {
                        ["-"] = Model[k]
                    };
                }
                var potentiallyModifiedKeys = current.Properties().Select(c => c.Name).Except(addedKeys).Except(unchangedKeys);
                foreach (var k in potentiallyModifiedKeys)
                {
                    diff[k] = FindDiff(current[k], model[k]);
                }
            }
            break;
        case JTokenType.Array:
            {
                var current = Current as JArray;
                var model = Model as JArray;
                diff["+"] = new JArray(current.Except(model));
                diff["-"] = new JArray(model.Except(current));
            }
            break;
        default:
            diff["+"] = Current;
            diff["-"] = Model;
            break;
    }

    return diff;
}
1
Dmitry Polyakov