How to compare two Json objects using C# - c#

I have two Json objects as below need to be compared. I am using Newtonsoft libraries for Json parsing.
string InstanceExpected = jsonExpected;
string InstanceActual = jsonActual;
var InstanceObjExpected = JObject.Parse(InstanceExpected);
var InstanceObjActual = JObject.Parse(InstanceActual);
And I am using Fluent Assertions to compare it. But the problem is Fluent assertion fails only when the attribute count/names are not matching. If the json values are different it passes. I require to fail when values are different.
InstanceObjActual.Should().BeEquivalentTo(InstanceObjExpected);
For example I have the actual and expected json to compare as below. And using the above way of comparing make them Pass which is wrong.
{
"Name": "20181004164456",
"objectId": "4ea9b00b-d601-44af-a990-3034af18fdb1%>"
}
{
"Name": "AAAAAAAAAAAA",
"objectId": "4ea9b00b-d601-44af-a990-3034af18fdb1%>"
}

I did a bit more digging and was able to find out why the OP's test code doesn't run as expected. I was able to fix it by installing and using the FluentAssertions.Json nuget package.
One important thing:
Be sure to include using FluentAssertions.Json otherwise false
positives may occur.
Test code is the following:
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);
}
}
Running the test:

Consider using the JToken.DeepEquals() method provided by Newtonsoft. It would look somewhat like this, regardless of which testing framework you're using:
Console.WriteLine(JToken.DeepEquals(InstanceObjActual, InstanceObjExpected));
// false

Made a non-recursive method which will remove twins - idea is to remove same elements from very similar JSONs, so that there will remain only different nodes in each object:
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);
}
Complete code + parser on my GitHub (profile or below).
Older CSV version which also sorts input mentioned in my question here How to compare big JSON's? (new one does not, so it could be very slow when one of objects has reversed order - it would be easier to sort during parsing or at least compare both neighbours of twins as first search step)

One option is to deserialize the json strings into C# objects and compare them.
This approach requires more work comparing to using JToken.DeepEquals (as suggested by #JessedeWit), but has the advantage of giving better error messages if your tests fail (see screenshot below).
Your json string can be modelled into the following class:
public class Entity
{
[JsonProperty("Name")]
public string Name { get; set; }
[JsonProperty("objectId")]
public string ObjectId { get; set; }
}
In your test, deserialize the json strings into objects and compare them:
[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: I used NUnit and FluentAssertions in my test method. Running the test:

After you deserialize the json to C# object the correct way is to implement the IComparable interface in the deserialized class and compare the 2 objects.
So:
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;
}
}

I needed two objects for Audit Logging and I wrote a code like below. It worked great for me.
https://github.com/wbish/jsondiffpatch.net
public static bool DeepCompare(this object obj, object another)
{
var diffObj = new JsonDiffPatch();
if (ReferenceEquals(obj, another)) return true;
if ((obj == null) || (another == null)) return false;
if (obj.GetType() != another.GetType()) return false;
var objJson = JsonConvert.SerializeObject(obj);
var anotherJson = JsonConvert.SerializeObject(another);
var result = diffObj.Diff(objJson, anotherJson);
return result == null;
}

Related

How to select an element's value from a JArray

I'm having a bit of trouble with C# (VS 2017, .Net 4.6) code. It'd be great if someone could help. I have a JSON file:
{
"makerCommission": 10,
"takerCommission": 10,
"buyerCommission": 0,
"updateTime": 1540015045989,
"balances": [
{
"asset": "BTC",
"free": "0.22222222",
"locked": "0.00000000"
},
{
"asset": "LTC",
"free": "2.00000000",
"locked": "3.00000000"
},
{
"asset": "ETH",
"free": "4.00000000",
"locked": "5.00000000"
}
]
}
I would like to retrieve the "free" value of any coin using:
result = (dynamic)JArray.Parse(MyData)
I do not want to retrieve all free values. How can I get 0.22222222, if I pick BTC?
First of all, your overall JSON does not represent an array, it represents an object which contains an array. So you would need to use JObject.Parse instead of JArray.Parse.
You can use the following LINQ-to-JSON code to find a specific asset in the array and then get the free value from it:
JObject obj = JObject.Parse(json); // Parse the JSON to a JObject
string free = obj["balances"] // Navigate down to the "balances" array
.Where(jt => (string)jt["asset"] == "BTC") // Find the object(s) containing the asset you want
.Select(jt => (string)jt["free"]) // From those get the "free" value
.FirstOrDefault(); // Take the first of those
Fiddle: https://dotnetfiddle.net/uFjSib
You are almost there. You have a JSON object, that contains the array, so you need to parse first the Object and then access the array using the name balances.
var result = (dynamic)JObject.Parse(MyData);
Console.WriteLine(result.balances); // output JArray ...
You can then get the value from the array like this:
Console.WriteLine(String.Format("{0} - {1}", result.balances[2].asset, result.balances[2].free));
The output is the third element from the array:
ETH - 4.00000000
In order to get only the BTC entry you need to filter the array:
foreach (var balance in result.balances)
{
if (balance.asset == "BTC")
{
Console.WriteLine(balance.free);
}
}
A better approach would be to create a class that represents the object structure and parse it. The filtering then will be easier using LINQ:
public class BalanceItem
{
public string asset { get; set; }
public double free { get; set; }
public double locked { get; set; }
}
public class Items
{
public List<BalanceItem> balances { get; set; }
}
var items = JsonConvert.DeserializeObject<Items>(MyData);
var btc = items.balances.FirstOrDefault (b => b.asset == "BTC");
if (btc != null)
{
Console.WriteLine(btc.free);
}
The output is: 0.22222222
Alternatively, you could use JToken
The following script gives you the wanted value. Maybe not the conceptually finest approach, but really intuitive.
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public class Program
{
public static void Main()
{
string json = #"
{
""makerCommission"": 10,
""takerCommission"": 10,
""buyerCommission"": 0,
""updateTime"": 1540015045989,
""balances"": [
{
""asset"": ""BTC"",
""free"": ""0.22222222"",
""locked"": ""0.00000000""
},
{
""asset"": ""LTC"",
""free"": ""2.00000000"",
""locked"": ""3.00000000""
},
{
""asset"": ""ETH"",
""free"": ""4.00000000"",
""locked"": ""5.00000000""
}
]
}";
var value = "";
JToken token = JToken.Parse(json);
var balances = token.SelectToken("balances");
foreach(var balance in balances){
if((string)balance.SelectToken("asset") == "BTC"){
value = (string)balance.SelectToken("free");
}
}
Console.WriteLine("value: " + value);
}
}
Inspired by this post

JSON.NET Skipping root element [duplicate]

I'm consuming a WCF service that returns JSON results wrapped inside the 'd' root element. The JSON response looks like this:
{"d":[
{
"__type":"DiskSpaceInfo:#Diagnostics.Common",
"AvailableSpace":38076567552,
"Drive":"C:\\",
"TotalSpace":134789197824
},
{
"__type":"DiskSpaceInfo:#Diagnostics.Common",
"AvailableSpace":166942183424,
"Drive":"D:\\",
"TotalSpace":185149157376
}
]}
I don't want to use dynamic typing, I have my class Diagnostics.Common.DiskSpaceInfo that I want to use when deserializing.
I am using Json.NET (Netwonsoft JSON).
The question is how to tell it to ignore the root element (that 'd' element) and parse what is inside.
The best solution I have so far is to use an anonymous type:
DiskSpaceInfo[] result = JsonConvert.DeserializeAnonymousType(json, new
{
d = new DiskSpaceInfo[0]
}).d;
this actually works but I don't like it very much. Is there another way? What I would like is something like:
DiskSpaceInfo[] result = JsonConvert.Deserialize(json, skipRoot: true);
or something like that...
If you know what to search like in this case "d" which is a root node then you can do the following.
JObject jo = JObject.Parse(json);
DiskSpaceInfo[] diskSpaceArray = jo.SelectToken("d", false).ToObject<DiskSpaceInfo[]>();
If you simply want to ignore the root class which you do not know then you can use the "#Giu Do" solution just that you can use test2.ToObject<DiskSpaceInfo[]>(); instead of the Console.Write(test2);
JObject o = JObject.Parse(json);
if (o != null)
{
var test = o.First;
if (test != null)
{
var test2 = test.First;
if (test2 != null)
{
DiskSpaceInfo[] diskSpaceArray = test2.ToObject<DiskSpaceInfo[]>();
}
}
}
Following on from previous answers here, I would like to suggest using your own static utility class. This is reusable and will allow you to get the syntax you are looking for.
public static class JsonUtil
{
public static T Deserialize<T>(string json, bool ignoreRoot) where T : class
{
return ignoreRoot
? JObject.Parse(json)?.Properties()?.First()?.Value?.ToObject<T>()
: JObject.Parse(json)?.ToObject<T>();
}
}
You would invoke it like this:
var resultA = JsonUtil.Deserialize<DiskSpaceInfo[]>(json, ignoreRoot: true);
or
var resultB = JsonUtil.Deserialize<DiskSpaceInfoRoot>(json, ignoreRoot: false);
By Newtonsoft, I suppose you use JSon.net, here is my solution, I used Linq to JSon available in this framework :
using System;
using Newtonsoft.Json.Linq;
namespace JSonTest
{
class Program
{
static void Main(string[] args)
{
string json = #"{""d"":[
{
""__type"":""DiskSpaceInfo:#Diagnostics.Common"",
""AvailableSpace"":38076567552,
""Drive"":""C:\\"",
""TotalSpace"":134789197824
},
{
""__type"":""DiskSpaceInfo:#Diagnostics.Common"",
""AvailableSpace"":166942183424,
""Drive"":""D:\\"",
""TotalSpace"":185149157376
}
]}";
JObject o = JObject.Parse(json);
if (o != null)
{
var test = o.First;
if (test != null)
{
var test2 = test.First;
if (test2 != null)
{
Console.Write(test2);
}
}
}
Console.Read();
}
}
}
I have used the property First because you need to find the first node after d, which is the first node of the json you received.
You just have to create a function that reproduice the Main, don't forget to check if the objects are not null to avoid NullReferenceException.

Json.Net DeserializeObject failing with OData.Delta - integers only

This problem is affecting my ASP.Net WebApi Patch method which looks a lot like this:
public MyModel Patch(int id, [FromBody]Delta<MyModel> newRecord){/*stuff here*/}
But it's not WebApi that's the problem - the failure is between Json.Net and OData.Delta.
The problem is JsonConvert.DeserializeObject does not see integers of OData.Delta objects and I'm wondering if there's a workaround or fix I can apply.
UPDATE: Have written code (see right down below) in the Json.Net library that will fix this. Just need it to be included in the next update (if James Newton-King allows it)
UPDATE 2: After further testing, I've decided the best course of action is to stop using OData.Delta and write my own (see answer)
Unit tests to prove the problem exists (using statements moved below for clarity)
Test 1: Fails with an int (Int32):
class TestObjWithInt
{
public int Int { get; set; }
}
[TestMethod]
public void IsApplied_When_IntIsDeserializedToDelta()
{
string testData = "{\"Int\":1}";
var deserializedDelta = JsonConvert.DeserializeObject<Delta<TestObjWithInt>>(testData);
var result = deserializedDelta.GetChangedPropertyNames().Contains("Int");
Assert.IsTrue(result);
}
Test 2: Succeeds with a long (Int64)
class TestObjWithLong
{
public long Long { get; set; }
}
[TestMethod]
public void IsApplied_When_LongIsDeserializedToDelta()
{
string testData = "{\"Long\":1}";
var deserializedDelta = JsonConvert.DeserializeObject<Delta<TestObjWithLong>>(testData);
var result = deserializedDelta.GetChangedPropertyNames().Contains("Long");
Assert.IsTrue(result);
}
And just to be sure that deserialization works to begin with, these two tests both pass.
[TestMethod]
public void IsApplied_When_LongIsDeserializedToTestObject()
{
string testData = "{\"Long\":1}";
var deserializedObject = JsonConvert.DeserializeObject<TestObjWithLong>(testData);
var result = deserializedObject.Long == 1;
Assert.IsTrue(result);
}
[TestMethod]
public void IsApplied_When_IntIsDeserializedToTestObject()
{
string testData = "{\"Int\":1}";
var deserializedObject = JsonConvert.DeserializeObject<TestObjWithInt>(testData);
var result = deserializedObject.Int == 1;
Assert.IsTrue(result);
}
I found this OData bug report which sounds like a similar issue but its old and closed so probably not.
Any help would be great.
Using statements (from the top of the test file):
using System;
using System.Linq;
using System.Web.Http.OData;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
Solution if accepted by James Newton-King - change to release 6.0.6.
Replace JsonSerializerInternalReader.cs line 1581:
contract.TrySetMember(newObject, memberName, value);
with:
bool done = false;
while (!(done = done || contract.TrySetMember(newObject, memberName, value)))
{
switch (reader.TokenType)
{
case JsonToken.Integer:
if (value is long && ((long)value) <= Int32.MaxValue && ((long)value) >= Int32.MinValue)
value = Convert.ToInt32(value);
//Add else if (...) to cast to other data types here (none additional required to date).
else
done = true;
break;
default:
done = true;
break;
}
}
OData.Delta<T> does not work with Json.Net for any number Types other than Int64. The easiest approach is to write a replacement for OData.Delta<T> (which I've done on company time so I can't post it in its entirety sorry) containing methods like this:
private bool TrySetInt32(object value, PropertyInfo propertyInfo, bool isNullable)
{
var done = false;
if (value is Int32)
{
propertyInfo.SetValue(_obj, value);
done = true;
}
else if (value == null)
{
if (isNullable)
{
propertyInfo.SetValue(_obj, value);
done = true;
}
}
else if (value is Int64) //Json.Net - fallback for numbers is an Int64
{
var val = (Int64)value;
if (val <= Int32.MaxValue && val >= Int32.MinValue)
{
done = true;
propertyInfo.SetValue(_obj, Convert.ToInt32(val));
}
}
else
{
Int32 val;
done = Int32.TryParse(value.ToString(), out val);
if (done)
propertyInfo.SetValue(_obj, val);
}
return done;
}
The class can be a dynamic generic like this:
public sealed class Patchable<T> : DynamicObject where T : class, new()
With a working variable like this:
T _obj = new T();
In the overridden TrySetMember method, we need to check the underlying type of the property using reflection and call the appropriate TrySet... method like this:
if (underlyingType == typeof(Int16))
done = TrySetInt16(value, propertyInfo, isNullable);
else if (underlyingType == typeof(Int32))
done = TrySetInt32(value, propertyInfo, isNullable);
If the value is set successfully we can add the property name to a list that we can then use for patching the original record like this:
if (done)
_changedPropertyNames.Add(propertyInfo.Name);
public void Patch(T objectToPatch)
{
foreach (var propertyName in _changedPropertyNames)
{
var propertyInfo = _obj.GetType().GetProperty(propertyName);
propertyInfo.SetValue(objectToPatch, propertyInfo.GetValue(_obj));
}
}
68 unit tests later, it all seems to work pretty well. Here's an example:
class TestObjWithInt32
{
public Int32 Int32 { get; set; }
public Int32? SetNullable { get; set; }
public Int32? UnsetNullable { get; set; }
}
[TestMethod]
public void IsApplied_When_Int32IsDeserializedToPatchable()
{
string testData = "{\"Int32\":1,\"SetNullable\":1}";
var deserializedPatchable = JsonConvert.DeserializeObject<Patchable<TestObjWithInt32>>(testData);
var result = deserializedPatchable.ChangedPropertyNames.Contains("Int32");
Assert.IsTrue(result);
var patchedObject = new TestObjWithInt32();
Assert.AreEqual<Int32>(0, patchedObject.Int32);
deserializedPatchable.Patch(patchedObject);
Assert.AreEqual<Int32>(1, patchedObject.Int32);
Assert.IsNull(patchedObject.UnsetNullable);
Assert.IsNotNull(patchedObject.SetNullable);
}
This is my implementation for this issue based on Rob solution:
public sealed class Patchable<T> : DynamicObject where T : class {
private readonly IDictionary<PropertyInfo, object> changedProperties = new Dictionary<PropertyInfo, object>();
public override bool TrySetMember(SetMemberBinder binder, object value) {
var pro = typeof (T).GetProperty(binder.Name);
if (pro != null)
changedProperties.Add(pro, value);
return base.TrySetMember(binder, value);
}
public void Patch(T delta) {
foreach (var t in changedProperties)
t.Key.SetValue(
delta,
t.Key.PropertyType.IsEnum ? Enum.Parse(t.Key.PropertyType, t.Value.ToString()) : Convert.ChangeType(t.Value, t.Key.PropertyType));
}
}
I removed the requisite of an empty constructor in generic type parameter using the dictionary instead of a temporal object.
Thanks Rob ;)

Deserializing JSON - how to ignore the root element?

I'm consuming a WCF service that returns JSON results wrapped inside the 'd' root element. The JSON response looks like this:
{"d":[
{
"__type":"DiskSpaceInfo:#Diagnostics.Common",
"AvailableSpace":38076567552,
"Drive":"C:\\",
"TotalSpace":134789197824
},
{
"__type":"DiskSpaceInfo:#Diagnostics.Common",
"AvailableSpace":166942183424,
"Drive":"D:\\",
"TotalSpace":185149157376
}
]}
I don't want to use dynamic typing, I have my class Diagnostics.Common.DiskSpaceInfo that I want to use when deserializing.
I am using Json.NET (Netwonsoft JSON).
The question is how to tell it to ignore the root element (that 'd' element) and parse what is inside.
The best solution I have so far is to use an anonymous type:
DiskSpaceInfo[] result = JsonConvert.DeserializeAnonymousType(json, new
{
d = new DiskSpaceInfo[0]
}).d;
this actually works but I don't like it very much. Is there another way? What I would like is something like:
DiskSpaceInfo[] result = JsonConvert.Deserialize(json, skipRoot: true);
or something like that...
If you know what to search like in this case "d" which is a root node then you can do the following.
JObject jo = JObject.Parse(json);
DiskSpaceInfo[] diskSpaceArray = jo.SelectToken("d", false).ToObject<DiskSpaceInfo[]>();
If you simply want to ignore the root class which you do not know then you can use the "#Giu Do" solution just that you can use test2.ToObject<DiskSpaceInfo[]>(); instead of the Console.Write(test2);
JObject o = JObject.Parse(json);
if (o != null)
{
var test = o.First;
if (test != null)
{
var test2 = test.First;
if (test2 != null)
{
DiskSpaceInfo[] diskSpaceArray = test2.ToObject<DiskSpaceInfo[]>();
}
}
}
Following on from previous answers here, I would like to suggest using your own static utility class. This is reusable and will allow you to get the syntax you are looking for.
public static class JsonUtil
{
public static T Deserialize<T>(string json, bool ignoreRoot) where T : class
{
return ignoreRoot
? JObject.Parse(json)?.Properties()?.First()?.Value?.ToObject<T>()
: JObject.Parse(json)?.ToObject<T>();
}
}
You would invoke it like this:
var resultA = JsonUtil.Deserialize<DiskSpaceInfo[]>(json, ignoreRoot: true);
or
var resultB = JsonUtil.Deserialize<DiskSpaceInfoRoot>(json, ignoreRoot: false);
By Newtonsoft, I suppose you use JSon.net, here is my solution, I used Linq to JSon available in this framework :
using System;
using Newtonsoft.Json.Linq;
namespace JSonTest
{
class Program
{
static void Main(string[] args)
{
string json = #"{""d"":[
{
""__type"":""DiskSpaceInfo:#Diagnostics.Common"",
""AvailableSpace"":38076567552,
""Drive"":""C:\\"",
""TotalSpace"":134789197824
},
{
""__type"":""DiskSpaceInfo:#Diagnostics.Common"",
""AvailableSpace"":166942183424,
""Drive"":""D:\\"",
""TotalSpace"":185149157376
}
]}";
JObject o = JObject.Parse(json);
if (o != null)
{
var test = o.First;
if (test != null)
{
var test2 = test.First;
if (test2 != null)
{
Console.Write(test2);
}
}
}
Console.Read();
}
}
}
I have used the property First because you need to find the first node after d, which is the first node of the json you received.
You just have to create a function that reproduice the Main, don't forget to check if the objects are not null to avoid NullReferenceException.

Ignore custom children with Json .Net

i have a json response like this:
{"response_values":[110,{"id":14753,"name":"piter"},{"id":14753,"name":"rabbit"}]}
and i have a simple class
public class Class1
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
and when i trying to convert json to list of the objects with this method:
public T Cast<T>(string json)
{
var result = default(T);
var jsonObject = JObject.Parse(json);
if (jsonObject != null)
{
var responseToken = jsonObject["response"];
result = responseToken.ToObject<T>();
}
return result;
}
like this
...
var jsonString = GetJson();
var items = Cast<List<Class1>>();
...
i have an exceiption, because value "110" is integer. How can i skip this value?
You always have this option if you expect the values to ignore to always be at the beginning:
if (jsonObject != null)
{
var responseToken = parsed["response_values"].SkipWhile(j => j.Type != JTokenType.Object);
if (responseToken.Count() > 0) result = responseToken.ToObject<T>();
}
You may prefer to use Skip(1) instead of SkipWhile if it's always the first value. Alternatively, you can use Where to ignore or select tokens anywhere in the message.
Of course, you can play around with this approach (changing things) depending on exactly what you expect to be returned in success scenarios.

Categories