11

I'm trying to discover if two JSON strings are equal.

This is what I previously tried

var obj1 = Json.Decode("{\"ValueA\":1,\"ValueB\":2}")
var obj2 = Json.Decode("{\"ValueB\":2,\"ValueA\":1}")

// But then there seems to be no way to compare the two objects?

Surely there must exist an elegant simple way to what I thought would be a common task?

Ally
  • 4,894
  • 8
  • 37
  • 45
  • 1
    possible duplicate of [Object comparison in JavaScript](http://stackoverflow.com/questions/1068834/object-comparison-in-javascript) (since you're not actually comparing JSON at this point, but JSO) – Brad Christie Feb 04 '14 at 14:55
  • @BradChristie What you linked to is purely JavaScript I think, I'm looking for a C# solution. – Ally Feb 04 '14 at 14:56
  • 1
    Touche. I'm apparently not quite awake yet. In that case, you can deserialize, then reserialize using a common library and compare. Otherwise, going to have to implement a deep object copy. – Brad Christie Feb 04 '14 at 14:58
  • Ok, was hoping it wouldn't come to that. I'm wondering if there's a simple way without third party libraries? – Ally Feb 04 '14 at 15:00
  • possible duplicate [Comparing C# object][1] [1]: http://stackoverflow.com/q/4585778/765498 – JavaScripter Feb 04 '14 at 15:01
  • 1
    @Ally: You're already using `Json.Decode`. You can `Encode` them back to strings (which should do so uniformly) and compare, which would avoid another library. – Brad Christie Feb 04 '14 at 15:01
  • @BradChristie Ah yes, that's quite clever with it's simplicity actually. Didn't think to do that. Cheers. – Ally Feb 04 '14 at 15:03
  • Check this http://stackoverflow.com/questions/4585778/comparing-c-sharp-objects-using-json – Sameer Feb 04 '14 at 15:05
  • @Ally: Except I'm actually finding that won't work. it still serializes back to the same strings (same property [mis]order. Hrmf. – Brad Christie Feb 04 '14 at 15:06
  • @BradChristie Ah, that's a shame, and also odd. – Ally Feb 04 '14 at 15:07
  • I would say to deserialize these objects into a coresponding class in C# (which has its `Eqals()` ovveridden), but I do not if your json objects will always have the same structure – Mario Stoilov Feb 04 '14 at 15:13
  • The other way I can immediatly think of is to make to make two Dictionary and put the keys and values there and compare the dictionaries afterwards – Mario Stoilov Feb 04 '14 at 15:15
  • 1
    @MarioStoilov That wouldn't not work for complex objects as the dictionary value would just be another complex JSON. – rae1 Feb 04 '14 at 15:16
  • @rae1 Ah, yes, your right :( – Mario Stoilov Feb 04 '14 at 15:17
  • @MarioStoilov It would be a problem to create classes for every different json result I think with my implementation. I like your idea of converting to a Dictionary, do you have an example of how to do that? I haven't had much luck with my previous attempts. EDIT: rae1 Points out this wouldn't actually be suitable. – Ally Feb 04 '14 at 15:17
  • @Ally: Cast your `Json.Decode` to `IDictionary`. I believe it internally creates one anyways (when `` is provided) – Brad Christie Feb 04 '14 at 15:19
  • @Ally As rae1 stated above, the Dictionary idea would fail for complex JSON objects. I do not have an example, but the idea is to use this method afterwards -> http://msdn.microsoft.com/en-us/library/vstudio/bb920248%28v=vs.90%29.aspx – Mario Stoilov Feb 04 '14 at 15:20
  • @BradChristie Casting to `IDictionary` seems to throw an error for me, I think this is where I struggled before. – Ally Feb 04 '14 at 15:21
  • I think the solution is back at implementing a deep object compare, either in JS or C#. – rae1 Feb 04 '14 at 15:23
  • Thanks for the feedback on it. I guess a deep object compare is the way to go then (can't do this in JS for my situation). Still it doesn't seem right that there isn't a simple way to handle that case. – Ally Feb 04 '14 at 15:26
  • A quick and dirty solution was posted by @BradChristie, however if you need to know not only that they are different, but also what part is different, you should create a POCO to deserialize into, then write your own equality (.Equals) operators. – Pete Garafano Feb 04 '14 at 15:57
  • @PeteGarafano I don't think a POCO object would be suitable. I'm testing the response from a web api, where the response could represent many different object types. – Ally Feb 04 '14 at 16:04
  • @Ally a web API should be returning structured data. When testing it is even more important to use a DTO, since a malformed response should have explicit deserialization errors. DTO's are very commonly used to deserialize json data returned by APIs. There can be a lot of overhead initially creating the DTO, however in the long run it typically makes things easier. Remember, that chances are very high that the server is serializing a DTO/POCO of some sort in response to a call to the API. – Pete Garafano Feb 04 '14 at 16:16
  • @PeteGarafano I hear you, your right, it's definitely a good idea to do everything with explicit POCO objects, although I want to avoid doing it that way for unit testing. The main reason is so I can write unit tests quickly, they do not need to be efficient, also having something where I can just say compare this JSON string to what I actually got reduces the number of lines of code I need for the tests and thus reduces the complexity. – Ally Feb 04 '14 at 16:36

3 Answers3

17

Another way to compare json - Comparing JSON with JToken.DeepEquals

JObject o1 = new JObject
 {
    { "Integer", 12345 },
    { "String", "A string" },
    { "Items", new JArray(1, 2) }
 };

JObject o2 = new JObject
 {
    { "Integer", 12345 },
    { "String", "A string" },
    { "Items", new JArray(1, 2) }
 };

Console.WriteLine(JToken.DeepEquals(o1, o2));
RL89
  • 1,866
  • 5
  • 22
  • 39
1

You could use the Compare .NET Objects lib to check if the two object instances are equal. It knows how to compare lists, dictionaries, etc. and deep compares the whole object graph. It also supports detailed reporting of what is different and has many more features you might want to use in the future.

Dejan
  • 9,150
  • 8
  • 69
  • 117
0

I was able to compare two JSON to some extent using the below code. For primitive classes I was able to get result to a good extent.

I hope with some more help and tweaking the below can be made more robust

    static void Main(string[] args)
    {
        var o = new
        {
            ValueA = "",//Comparison Works
            ValueB = "",//Comparison Works
            ValueC = new { ValueD = "", ValueE = "" },//Comparison Works
            ValueF = new[] { new { ValueG = "", ValueH = "" } },//Fails if the array values are out of order
            ValueI = new SortedDictionary<object, object>()//Comparison works
        };

        var t = JsonConvert.DeserializeAnonymousType(
            "{\"ValueA\":1,\"ValueB\":2, \"ValueC\":{\"ValueE\":2,\"ValueD\":1}, \"ValueF\":[{\"ValueG\":10,\"ValueH\":25}],\"ValueI\":{\"Test1\":\"Val1\",\"Test2\":\"Val1\"}}", o);

        var q = JsonConvert.DeserializeAnonymousType(
            "{\"ValueB\":2,\"ValueA\":1, \"ValueC\":{\"ValueD\":1,\"ValueE\":2}, \"ValueF\":[{\"ValueH\":25,\"ValueG\":10}],\"ValueI\":{\"Test2\":\"Val1\",\"Test1\":\"Val1\"}}", o);

        var prop = t.GetType().GetProperties();

        var match = true;

        foreach (var item in prop)
        {
            var type = item.PropertyType;

            if (type.IsArray)
            {
                var v1 = item.GetValue(t) as Array;
                var v2 = item.GetValue(q) as Array;
                if ((v1 != null && v2 != null))
                {
                    if ((v1.Length != v2.Length))
                    {
                        match = false;
                        break;
                    }
                    for (int i = 0; i < v1.Length; i++)
                    {
                        if (!v1.GetValue(i).Equals(v2.GetValue(i)))
                        {
                            match = false;
                            break;
                        }
                    }
                }
                else if ((v1 == null && v2 != null) || (v1 != null && v2 == null))
                {
                    match = false;
                    break;
                }
            }
            else if (type.Name.Contains("Dictionary"))
            {
                var v1 = (SortedDictionary<object, object>)item.GetValue(t);
                var v2 = item.GetValue(q) as SortedDictionary<object, object>;
                foreach (var ar in v1)
                {
                    if (!v2.Contains(ar))
                    {
                        match = false;
                        break;
                    }
                }
            }
            else if (!item.GetValue(t).Equals(item.GetValue(q)))
            {
                var v1 = item.GetValue(t);
                var v2 = item.GetValue(q);
                match = v1.ToString().Equals(v2.ToString());
                match = false;
                break;
            }
        }

        if (!match)
        {
            Console.WriteLine("Objects do not match");
        }
    }
Sandesh
  • 2,966
  • 1
  • 20
  • 34