web-dev-qa-db-fra.com

Comment comparer deux objets Json à l'aide de C #

J'ai deux objets Json comme ci-dessous doivent être comparés. J'utilise les bibliothèques Newtonsoft pour l'analyse Json.

string InstanceExpected = jsonExpected;
string InstanceActual = jsonActual;
var InstanceObjExpected = JObject.Parse(InstanceExpected);
var InstanceObjActual = JObject.Parse(InstanceActual);

Et j'utilise Fluent Assertions pour le comparer. Mais le problème est que l'assertion Fluent échoue uniquement lorsque le nombre/les noms d'attributs ne correspondent pas. Si les valeurs json sont différentes, cela passe. J'ai besoin d'échouer lorsque les valeurs sont différentes.

InstanceObjActual.Should().BeEquivalentTo(InstanceObjExpected);

Par exemple, j'ai le json réel et prévu à comparer comme ci-dessous. Et en utilisant la façon de comparer ci-dessus, faites-les passer, ce qui est faux.

{
  "Name": "20181004164456",
  "objectId": "4ea9b00b-d601-44af-a990-3034af18fdb1%>"  
}

{
  "Name": "AAAAAAAAAAAA",
  "objectId": "4ea9b00b-d601-44af-a990-3034af18fdb1%>"  
}
6
Nandakumar1712

J'ai creusé un peu plus et j'ai pu découvrir pourquoi le code de test de l'OP ne fonctionne pas comme prévu. J'ai pu le réparer en installant et en utilisant le paquet nuget FluentAssertions.Json .

Une chose importante:

N'oubliez pas d'inclure using FluentAssertions.Json sinon des faux positifs peuvent se produire.

Le code de test est le suivant:

using FluentAssertions;
using FluentAssertions.Json;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NUnit.Framework;

[TestFixture]
public class JsonTests
{
    [Test]
    public void JsonObject_ShouldBeEqualAsExpected()
    {
        JToken expected = JToken.Parse(@"{ ""Name"": ""20181004164456"", ""objectId"": ""4ea9b00b-d601-44af-a990-3034af18fdb1%>"" }");
        JToken actual = JToken.Parse(@"{ ""Name"": ""AAAAAAAAAAAA"", ""objectId"": ""4ea9b00b-d601-44af-a990-3034af18fdb1%>"" }");

        actual.Should().BeEquivalentTo(expected);
    }
}

Exécution du test:

Unit test results

13
Rui Jarimba

Pensez à utiliser la méthode JToken.DeepEquals() fournie par Newtonsoft. Cela ressemblerait un peu à ceci, quel que soit le framework de test que vous utilisez:

Console.WriteLine(JToken.DeepEquals(InstanceObjActual, InstanceObjExpected));
// false
12
Jesse de Wit

Création d'une méthode non récursive qui supprimera les jumeaux - l'idée est de supprimer les mêmes éléments de JSON très similaires, de sorte qu'il ne restera que des nœuds différents dans chaque objet:

public void RemoveTwins(ref BreadthFirst bf1, ref BreadthFirst bf2) {
    JsonNode traversal = bf1.Next();
    Boolean removed = false;
    do {
        if (!removed) {
            if (bf2.Current != null) while (bf1.Level == bf2.Level && bf2.Next() != null) ;
            if (bf2.Current != null) while (bf1.Level != bf2.Level && bf2.Next() != null) ;
            else bf2.Current = bf2.root;
        }
        else traversal = bf1.Next();
        if (bf2.Level < 0) bf2.Current = bf2.Root;
        do {
            removed = bf1.NextAs(bf1.src, bf2, bf2.src);
            if (removed && bf1.Orphan && bf2.Orphan) {
                JsonNode same = bf1.Current.Parent;
                traversal = bf1.RemoveCurrent();
                same = bf2.Current.Parent;
                bf2.RemoveCurrent();
                bf1.UpdateLevel();
                bf2.UpdateLevel();
                if (traversal == null
                || bf1.Root == null || bf2.Root == null
                || (bf1.Level == 0 && bf1.Current.NodeBelow == null)) {
                    traversal = null;
                    break;
                }
            } else
            if (!removed) {
                break; 
            } else removed = false;
        } while (removed);
        if (!removed) traversal = bf1.Next();
    } while (traversal != null);
}

Code complet + analyseur sur mon GitHub (profil ou ci-dessous).
Ancienne version CSV qui trie également les entrées mentionnées dans ma question ici Comment comparer les gros JSON? (pas le nouveau, donc il pourrait être très lent lorsque l'un des objets a inversé l'ordre - il serait plus facile de trier pendant l'analyse ou au moins de comparer les deux voisins des jumeaux comme première étape de recherche)

2
Tom

Une option consiste à désérialiser les chaînes json en objets C # et à les comparer.

Cette approche nécessite plus de travail par rapport à l'utilisation de JToken.DeepEquals (comme suggéré par @JessedeWit), mais a l'avantage de donner de meilleurs messages d'erreur si vos tests échouent (voir capture d'écran ci-dessous).

Votre chaîne json peut être modélisée dans la classe suivante:

public class Entity
{
    [JsonProperty("Name")]
    public string Name { get; set; }

    [JsonProperty("objectId")]
    public string ObjectId { get; set; }
}

Dans votre test, désérialisez les chaînes json en objets et comparez-les:

[TestFixture]
public class JsonTests
{
    [Test]
    public void JsonString_ShouldBeEqualAsExpected()
    {
        string jsonExpected = @"{ ""Name"": ""20181004164456"", ""objectId"": ""4ea9b00b-d601-44af-a990-3034af18fdb1%>"" }";
        string jsonActual = @"{ ""Name"": ""AAAAAAAAAAAA"", ""objectId"": ""4ea9b00b-d601-44af-a990-3034af18fdb1%>"" }";

        Entity expectedObject = JsonConvert.DeserializeObject<Entity>(jsonExpected);
        Entity actualObject = JsonConvert.DeserializeObject<Entity>(jsonActual);

        actualObject.Should().BeEquivalentTo(expectedObject);
    }
}

PS: J'ai utilisé NUnit et FluentAssertions dans ma méthode de test. Exécution du test:

Unit test results

0
Rui Jarimba

Après avoir désérialisé l'objet json en C #, la bonne façon consiste à implémenter l'interface IComparable dans la classe désérialisée et à comparer les 2 objets.

Donc:

using System;
using System.Collections.Generic;

class MyObj : IComparable<MyObj>
{
    public string Name { get; set; }
    public string ObjectID { get; set; }

    public int CompareTo(MyObj other)
    {
        if ((this.Name.CompareTo(other.Name) == 0) &&
            (this.ObjectID.CompareTo(other.ObjectID) == 0))
        {
            return 0;
        }
        return -1;
    }
}
0
Itamar Kerbel