Trouver et retourner les différences JSON en utilisant newtonsoft dans C#?

j'aimerais obtenir une liste des pièces JSON qui ne correspondent pas en faisant une comparaison avec Newtonsoft.

j'ai ce code qui compare:

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

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

Mais ne peut pas trouver quelque chose qui renvoie la diff.

24
demandé sur Ondrej Svejdar 2014-07-22 02:44:02

4 réponses

c'est une question relativement ancienne, mais en affichant une des façons possibles de résoudre cela, en supposant que le résultat que vous voulez est exactement quelles valeurs de propriété sont changées

   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");
   }  

Note: Ceci n'a pas été testé pour une hiérarchie très profonde.

12
répondu Pravin 2015-08-10 18:14:37

Voici une version récursive que j'ai écrit. Vous appelez CompareObjects avec deux JObjects et il retourne une liste des différences. Vous appelez CompareArrays avec deux JArrays et il compare les tableaux. Les tableaux et les objets peuvent être imbriqués les uns dans les autres.

UPDATE: @nttakr souligne dans le commentaire ci-dessous que cette méthode est en fait un algorithme de différence partielle. Il ne vous parle que des différences du point de vue de la liste des sources. Si une clé n'existe pas dans la source mais existe dans la liste de cibles, cette différence sera ignorée. C'est par conception pour mes exigences de test. Cela vous permet de tester juste les articles que vous voulez sans avoir à les supprimer de la cible avant les comparaisons sont faites.

    /// <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;
    }
11
répondu Walter 2017-06-20 22:13:19

Juste pour aider les futures requêtes. Il y a un joli outil JSON diff que j'ai trouvé. Il fonctionne parfaitement pour les diff/patch de structures json:

jsondiffpatch.net Il y a aussi un paquet nuget pour ça.

l'usage est simple.

var jdp = new JsonDiffPatch();
JToken diffResult = jdp.Diff(leftJson, rightJson);
9
répondu Achilles 2017-11-14 12:08:41

notez les bibliothèques suivantes:

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

je ne suis pas entièrement sûr de comprendre votre question correctement. Je suppose que vous essayez d'identifier les clés qui manquent dans le vrai JSON.

si vous êtes simplement intéressé par les clés manquantes, le code ci-dessous vous aidera, sinon, veuillez fournir un exemple des types de différences que vous essayez d'identifier.

  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;
    }

notez que si cette méthode retourne un IEnumerable vide alors le réel JSON a toutes les clés nécessaires selon la structure de la JSON attendue.

NOTE: le JSON réel pourrait encore avoir plus de touches que le JSON prévu n'est pas nécessaire.

pour expliquer davantage mes notes...

supposons que votre JSON attendu est:

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

et que votre JSON est :

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

la fonction ci-dessus vous indiquera que la clé de valeur est manquante, mais ne mentionnera rien au sujet de la clé SomeProp...à moins que votre swap les paramètres d'entrée autour de.

3
répondu WaseemS 2014-11-27 21:17:21