FluentAssertions object graph comparison including JSON objects - c#

I have a simple class with a property of type object, which can contain JSON built from different sources
public class SimpleClass
{
public string Id { get; set; }
public object JSONData { get; set; }
}
I'm using FluentAssertions object graph comparison but this isn't working
private void Verify(SimpleClass actualOutput, SimpleClass expectedOutput)
{
actualOutput.Should().BeEquivalentTo(expectedOutput);
}
Depending on the source of the objects, I'm seeing messages like these
Expected member JSONData to be a
System.Collections.Generic.IDictionary`2[System.String,Newtonsoft.Json.Linq.JToken], but found a
System.Text.Json.JsonElement.
Expected member JSONData to be
System.String, but found
System.Text.Json.JsonElement.
The objects are equal when I convert them to strings
private void Verify(SimpleClass actualOutput, SimpleClass expectedOutput)
{
actualOutput.JSONData.ToString().Should().Be(expectedOutput.JSONData.ToString());
}
I don't want to compare individual properties like that, because I need to compare a collection of SimpleClass instances. Is there any way to make this work using object graph comparison? Can I configure FluentAssertions to do the string conversion during comparison?

[Fact]
public void CompareJson()
{
var jsonText = #"{
""Element1"": 123,
""Element2"": ""text""
}";
var x1 = new SimpleClass { Id = "X", JSONData = jsonText };
var x2 = new SimpleClass { Id = "X", JSONData = JsonConvert.DeserializeObject(x1.JSONData.ToString()) };
x1.Should().BeEquivalentTo(
x2,
o => o.Using<object>(ctx => ctx.Subject.ToString().Should().Be(ctx.Expectation.ToString())).When(
info => info.SelectedMemberPath.EndsWith(nameof(SimpleClass.JSONData))));
}

Related

JsonSerializer deserialize to anonymous type

How do I deserialize a JSON to an anonymous type?
I have this working code that's:
defining a JSON with Foo and Bar fields
defining a model as anonymous type with Foo property
deserializing the JSON using the generic method
[Fact]
public void Test1()
{
const string json = """
{
"Foo": "a",
"Bar": "b"
}
""";
var model = new
{
Foo = default(string),
};
var deserialized = Deserialize(json, model);
var a = deserialized.Foo;
a.Should().Be("a");
}
private T Deserialize<T>(string json, T model) => JsonSerializer.Deserialize<T>(json) ?? model;
I would like to do the same without the generic Deserialize method. Below the code:
[Fact]
public void Test2()
{
const string json = """
{
"Foo": "a",
"Bar": "b"
}
""";
var model = new
{
Foo = default(string),
};
var deserialized = JsonSerializer.Deserialize<??????>(json);
string a = deserialized.Foo;
a.Should().Be("a");
}
Not sure what to put instead of the ??????. Ideally I'd like to specify something like typeof(model) or model.GetType(), but those are not accepted by the compiler
Not sure that this will suit your actual use case, but for current one you can try using Dictionary:
var deserialized = JsonSerializer.Deserialize<Dictionary<string, string>>(json);
string a = deserialized["Foo"];
Or just introduce the class, with records (C# 9) and file keyword (C# 11) it should extremely simple and effortless:
file record RecordForTest1(string Foo, string Bar);
[Fact]
public void Test1()
{
// ...
var deserialized = JsonSerializer.Deserialize<RecordForTest1>(json);
}
You can use ExpandoObject.
var deserialized = JsonSerializer.Deserialize<ExpandoObject>(json);
string a = deserialized.Foo.ToString();
...
If you want you can also create extension method like below:
public static dynamic? DeserializeAsDynamic(this string json)
{
var result = JsonConvert.DeserializeObject<ExpandoObject>(json);
return result;
}
Sample usage:
dynamic obj = json.DeserializeAsDynamic();

Parse JSON Objects with unique strings as parents using JSON.Net

Let's say I have this example JSON:
"Test": {
"KIf42N7OJIke57Dj6dkh": {
"name": "test 1"
},
"xsQMe4WWMu19qdULspve": {
"name": "test 2"
}
}
I want to parse this into an Array of a custom class I have, which will be exampled below:
class Class1 {
public string Name { get; set; }
Class1(string name) {
Name = name;
}
}
How can I parse this using Json.NET's JObject.Parse?
You can achieve your goal with JPath query like this :
var myArray = JObject
.Parse(json)
.SelectTokens("$.Test..name")
.Values<string>()
.Select(s => new Class1(s))
.ToArray();
But probably not the best way to do it.
I personnaly prefere to create classes to represent the json structure and then apply transformations.
void Main()
{
var json = #"{""Test"": {
""KIf42N7OJIke57Dj6dkh"": {
""name"": ""test 1""
},
""xsQMe4WWMu19qdULspve"": {
""name"": ""test 2""
}
}
}";
var root = JsonConvert.DeserializeObject<Root>(json);
var array = root.Test.Select(i => i.Value).ToArray();
array.Dump();
}
public class Root
{
public Dictionary<string, Class1> Test { get; set; }
}
public class Class1
{
public string Name { get; set; }
public Class1(string name)
{
Name = name;
}
}
To begin with, your Json is missing starting/closing braces. The Json needs to have wrapping braces around the Test value.
{
'Test':
{
'KIf42N7OJIke57Dj6dkh': {'name': 'test 1'},
'xsQMe4WWMu19qdULspve': {'name': 'test 2'}
}
}
If you are missing it in the original Json, you could wrap the current input Json as following.
var correctedJson = $"{{{inputJsonString}}}";
If you want to parse the Json Objects to Array of Class1 without creating additional concrete data structures and using JPath Queries, you could use Anonymous Types for the purpose using the DeserializeAnonymousType Method proved by Json.Net. For example,
var sampleObject = new {Test = new Dictionary<string,Class1>()};
var data = JsonConvert.DeserializeAnonymousType(correctedJson,sampleObject);
var result = data.Test.Select(x=>x.Value).ToArray();
You could also achieve it using JPath Query or creating Concrete Data Structures as #Kalten as described in his answer.

Passing Object to DistributedCache set method?

I am using this package Microsoft.Extensions.Caching.Redis.
What is the way to pass tModel to set and get from cache.
public class TestModel
{
public int test1 { get; set; }
public string test2 { get; set; }
}
var tModel = new TestModel();
tModel.test1 = 1;
tModel.test2 = "abc";
_distributedCache.Set("model", tModel);
I would serialize the data to JSON and persist it as a string:
using Newtonsoft.Json;
// ...
// Set
var jsonData = JsonConvert.SerializeObject(tModel);
_distributedCache.SetString("model", jsonData);
// Get
var jsonData = _distributedCache.GetString("model");
var tModel = JsonConvert.DeserializeObject<TestModel>(jsonData);
Notice I used the GetString and SetString extension methods here, rather than the low-level Get and Set method which takes and returns bytes. There are also async methods available.

Json Deserialize returns null [duplicate]

So I'm trying to deserialize an object that has properties: $ref and $id. I have tried going between Dictionary as well as an object where I have specified namingconventions via JsonPropertyAttribute. Serialization works, but deserialization doesn't. The error I keep getting is:
Additional text found in JSON string
after finishing deserializing object.
Sample code where all three samples, fail.
[Serializable]
public class Ref
{
[JsonProperty(PropertyName = "$ref")]
public virtual string RefName { get; set; }
[JsonProperty(PropertyName = "$id")]
public virtual int Id { get; set; }
}
[Serializable]
public class Child
{
public virtual string Name { get; set; }
[JsonProperty(IsReference = true)]
public virtual Ref Father { get; set; }
}
class Program
{
static void Main(string[] args)
{
//Additional text found in JSON string after finishing deserializing object.
//Test 1
var reference = new Dictionary<string, object>();
reference.Add("$ref", "Persons");
reference.Add("$id", 1);
var child = new Dictionary<string, object>();
child.Add("_id", 2);
child.Add("Name", "Isabell");
child.Add("Father", reference);
var json = JsonConvert.SerializeObject(child);
var obj = JsonConvert.DeserializeObject<Dictionary<string, object>>(json); //Exception
//Test 2
var refOrg = new Ref {RefName = "Parents", Id = 1};
var refSer = JsonConvert.SerializeObject(refOrg);
var refDeser = JsonConvert.DeserializeObject<Ref>(refSer); //Exception
//Test 3
var childOrg = new Child {Father = refOrg, Name = "Isabell"};
var childSer = JsonConvert.SerializeObject(childOrg);
var childDeser = JsonConvert.DeserializeObject<Child>(refSer); //Exception
}
}
I have the same issue when trying to deserialize a swagger/json schema document. The solution is:
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.MetadataPropertyHandling = MetadataPropertyHandling.Ignore;
You'll find that $ref & $id are special properties used by Json.NET to manage multiple instances of objects in a single object graph.
By putting these keys into your dictionary the deserialize process is trying to parse these as references to objects that don't exist.
Try altering the keys.
This answer worked for me: Json.Net adding $id to EF objects despite setting PreserveReferencesHandling to "None"
In your implementation of DefaultContractResolver/IContractResolver, add this;
public override JsonContract ResolveContract(Type type) {
var contract = base.ResolveContract(type);
contract.IsReference = false;
return contract;
}

Convert JSON String To C# Object

Trying to convert a JSON string into an object in C#. Using a really simple test case:
JavaScriptSerializer json_serializer = new JavaScriptSerializer();
object routes_list = json_serializer.DeserializeObject("{ \"test\":\"some data\" }");
The problem is that routes_list never gets set; it's an undefined object. Any ideas?
Or, you can use the Newtownsoft.Json library as follows:
using Newtonsoft.Json;
...
var result = JsonConvert.DeserializeObject<T>(json);
Where T is your object type that matches your JSON string.
It looks like you're trying to deserialize to a raw object. You could create a Class that represents the object that you're converting to. This would be most useful in cases where you're dealing with larger objects or JSON Strings.
For instance:
class Test {
String test;
String getTest() { return test; }
void setTest(String test) { this.test = test; }
}
Then your deserialization code would be:
JavaScriptSerializer json_serializer = new JavaScriptSerializer();
Test routes_list =
(Test)json_serializer.DeserializeObject("{ \"test\":\"some data\" }");
More information can be found in this tutorial:
http://www.codeproject.com/Tips/79435/Deserialize-JSON-with-Csharp.aspx
You probably don't want to just declare routes_list as an object type. It doesn't have a .test property, so you really aren't going to get a nice object back. This is one of those places where you would be better off defining a class or a struct, or make use of the dynamic keyword.
If you really want this code to work as you have it, you'll need to know that the object returned by DeserializeObject is a generic dictionary of string,object. Here's the code to do it that way:
var json_serializer = new JavaScriptSerializer();
var routes_list = (IDictionary<string, object>)json_serializer.DeserializeObject("{ \"test\":\"some data\" }");
Console.WriteLine(routes_list["test"]);
If you want to use the dynamic keyword, you can read how here.
If you declare a class or struct, you can call Deserialize instead of DeserializeObject like so:
class MyProgram {
struct MyObj {
public string test { get; set; }
}
static void Main(string[] args) {
var json_serializer = new JavaScriptSerializer();
MyObj routes_list = json_serializer.Deserialize<MyObj>("{ \"test\":\"some data\" }");
Console.WriteLine(routes_list.test);
Console.WriteLine("Done...");
Console.ReadKey(true);
}
}
Using dynamic object with JavaScriptSerializer.
JavaScriptSerializer serializer = new JavaScriptSerializer();
dynamic item = serializer.Deserialize<object>("{ \"test\":\"some data\" }");
string test= item["test"];
//test Result = "some data"
Newtonsoft is faster than java script serializer. ... this one depends on the Newtonsoft NuGet package, which is popular and better than the default serializer.
one line code solution.
var myclass = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(Jsonstring);
Myclass oMyclass = Newtonsoft.Json.JsonConvert.DeserializeObject<Myclass>(Jsonstring);
You can accomplished your requirement easily by using Newtonsoft.Json library. I am writing down the one example below have a look into it.
Class for the type of object you receive:
public class User
{
public int ID { get; set; }
public string Name { get; set; }
}
Code:
static void Main(string[] args)
{
string json = "{\"ID\": 1, \"Name\": \"Abdullah\"}";
User user = JsonConvert.DeserializeObject<User>(json);
Console.ReadKey();
}
this is a very simple way to parse your json.
Here's a simple class I cobbled together from various posts.... It's been tested for about 15 minutes, but seems to work for my purposes. It uses JavascriptSerializer to do the work, which can be referenced in your app using the info detailed in this post.
The below code can be run in LinqPad to test it out by:
Right clicking on your script tab in LinqPad, and choosing "Query
Properties"
Referencing the "System.Web.Extensions.dll" in "Additional References"
Adding an "Additional Namespace Imports" of
"System.Web.Script.Serialization".
Hope it helps!
void Main()
{
string json = #"
{
'glossary':
{
'title': 'example glossary',
'GlossDiv':
{
'title': 'S',
'GlossList':
{
'GlossEntry':
{
'ID': 'SGML',
'ItemNumber': 2,
'SortAs': 'SGML',
'GlossTerm': 'Standard Generalized Markup Language',
'Acronym': 'SGML',
'Abbrev': 'ISO 8879:1986',
'GlossDef':
{
'para': 'A meta-markup language, used to create markup languages such as DocBook.',
'GlossSeeAlso': ['GML', 'XML']
},
'GlossSee': 'markup'
}
}
}
}
}
";
var d = new JsonDeserializer(json);
d.GetString("glossary.title").Dump();
d.GetString("glossary.GlossDiv.title").Dump();
d.GetString("glossary.GlossDiv.GlossList.GlossEntry.ID").Dump();
d.GetInt("glossary.GlossDiv.GlossList.GlossEntry.ItemNumber").Dump();
d.GetObject("glossary.GlossDiv.GlossList.GlossEntry.GlossDef").Dump();
d.GetObject("glossary.GlossDiv.GlossList.GlossEntry.GlossDef.GlossSeeAlso").Dump();
d.GetObject("Some Path That Doesnt Exist.Or.Another").Dump();
}
// Define other methods and classes here
public class JsonDeserializer
{
private IDictionary<string, object> jsonData { get; set; }
public JsonDeserializer(string json)
{
var json_serializer = new JavaScriptSerializer();
jsonData = (IDictionary<string, object>)json_serializer.DeserializeObject(json);
}
public string GetString(string path)
{
return (string) GetObject(path);
}
public int? GetInt(string path)
{
int? result = null;
object o = GetObject(path);
if (o == null)
{
return result;
}
if (o is string)
{
result = Int32.Parse((string)o);
}
else
{
result = (Int32) o;
}
return result;
}
public object GetObject(string path)
{
object result = null;
var curr = jsonData;
var paths = path.Split('.');
var pathCount = paths.Count();
try
{
for (int i = 0; i < pathCount; i++)
{
var key = paths[i];
if (i == (pathCount - 1))
{
result = curr[key];
}
else
{
curr = (IDictionary<string, object>)curr[key];
}
}
}
catch
{
// Probably means an invalid path (ie object doesn't exist)
}
return result;
}
}
As tripletdad99 said
var result = JsonConvert.DeserializeObject<T>(json);
but if you don't want to create an extra object you can make it with Dictionary instead
var result = JsonConvert.DeserializeObject<Dictionary<string, string>>(json_serializer);
add this ddl to reference to your project: System.Web.Extensions.dll
use this namespace: using System.Web.Script.Serialization;
public class IdName
{
public int Id { get; set; }
public string Name { get; set; }
}
string jsonStringSingle = "{'Id': 1, 'Name':'Thulasi Ram.S'}".Replace("'", "\"");
var entity = new JavaScriptSerializer().Deserialize<IdName>(jsonStringSingle);
string jsonStringCollection = "[{'Id': 2, 'Name':'Thulasi Ram.S'},{'Id': 2, 'Name':'Raja Ram.S'},{'Id': 3, 'Name':'Ram.S'}]".Replace("'", "\"");
var collection = new JavaScriptSerializer().Deserialize<IEnumerable<IdName>>(jsonStringCollection);
Copy your Json and paste at textbox on json2csharp and click on Generate button.
A cs class will be generated use that cs file as below
var generatedcsResponce = JsonConvert.DeserializeObject(yourJson);
Where RootObject is the name of the generated cs file;
Another fast and easy way to semi-automate these steps is to:
take the JSON you want to parse and paste it here: https://app.quicktype.io/ . Change language to C# in the drop down.
Update the name in the top left to your class name, it defaults to "Welcome".
In visual studio go to Website -> Manage Packages and use NuGet to add Json.Net from Newtonsoft.
app.quicktype.io generated serialize methods based on Newtonsoft.
Alternatively, you can now use code like:
WebClient client = new WebClient();
string myJSON = client.DownloadString("https://URL_FOR_JSON.com/JSON_STUFF");
var myClass = Newtonsoft.Json.JsonConvert.DeserializeObject(myJSON);
Convert a JSON string into an object in C#. Using below test case.. its worked for me. Here "MenuInfo" is my C# class object.
JsonTextReader reader = null;
try
{
WebClient webClient = new WebClient();
JObject result = JObject.Parse(webClient.DownloadString("YOUR URL"));
reader = new JsonTextReader(new System.IO.StringReader(result.ToString()));
reader.SupportMultipleContent = true;
}
catch(Exception)
{}
JsonSerializer serializer = new JsonSerializer();
MenuInfo menuInfo = serializer.Deserialize<MenuInfo>(reader);
First you have to include library like:
using System.Runtime.Serialization.Json;
DataContractJsonSerializer desc = new DataContractJsonSerializer(typeof(BlogSite));
string json = "{\"Description\":\"Share knowledge\",\"Name\":\"zahid\"}";
using (var ms = new MemoryStream(ASCIIEncoding.ASCII.GetBytes(json)))
{
BlogSite b = (BlogSite)desc.ReadObject(ms);
Console.WriteLine(b.Name);
Console.WriteLine(b.Description);
}
Let's assume you have a class name Student it has following fields and it has a method which will take JSON as a input and return a string Student Object.We can use JavaScriptSerializer here Convert JSON String To C# Object.std is a JSON string here.
public class Student
{
public string FirstName {get;set:}
public string LastName {get;set:}
public int[] Grades {get;set:}
}
public static Student ConvertToStudent(string std)
{
var serializer = new JavaScriptSerializer();
Return serializer.Deserialize<Student>(std);
}
Or, you can use the System.Text.Json library as follows:
using System.Text.Json;
...
var options = new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true
});
var result = JsonSerializer.Deserialize<List<T>>(json, options);
Where T is your object type that matches your JSON string.
System.Text.Json is available in:
.NET Core 2.0 and above
.NET Framework 4.6.1 and above

Categories

Resources