How to fill ObservableCollection using JSON? Now there is only the script itself and the model in the desktop application. I can not understand how to tie it up.
I get it after running the script:
{
"records": [
{
"brand_id": "1",
"brand_name": "Gigabyte"
},
{
"brand_id": "2",
"brand_name": "MSI"
},
{
"brand_id": "3",
"brand_name": "Lenovo"
},
{
"brand_id": "4",
"brand_name": "Dell"
},
{
"brand_id": "5",
"brand_name": "Google"
}
]}
And I have a Model in app:
public class Brands
{
int brand_id;
string brand_name;
public int Brand_id { get => brand_id; set => brand_id = value; }
public string Brand_name { get => brand_name; set => brand_name = value; }
}
And Collection:
public class BrandsCollection
{
private ObservableCollection<Brands> brands;
public ObservableCollection<Brands> Brands { get => brands; set => brands = value; }
}
It is fairly straight forward, especially with packages available to simplify a lot of the work. The Nuget Package System.Net.Http will have the packages you need to create an HttpClient to Get() your JSON from the web. I recommend Newtonsoft.Json to parse JSON into a C# object. And then having the object you just need to set the DataGrid.ItemSource to be an array of objects of any type for it to generate columns.
A simple example:
First you would define a simple object representation of your JSON data.
For instance if you had the following data:
[
{
"Name":"Test",
"Data": ["Item1","Item2"]
},
{
"Name":"Test 2",
"Data": ["Item3","Item4"]
}
]
You would have to create an equivalent C# representation.
Basically this is a List of objects so:
public class OuterObject : List<InnerObject> {}
The inner object is as follows:
public class InnerObject {
public string Name { get; set; }
public List<string> Data { get; set; }
}
Having the objects defined you can do something like:
HttpClient client = new HttpClient { BaseAddress = new Uri("ADDRESS") };
var json = await client.GetAsync("/ENDPOINT");
JsonSerializer serializer = JsonSerializer.CreateDefault();
using (StringReader reader = new StringReader(json))
{
using (JsonTextReader jsonReader = new JsonTextReader(reader))
{
var result = serializer.Deserialize<OuterObject>(jsonReader);
}
}
Then to access the data in your program you can access it like so:
string name = result[0].Name;
Or set it to a DataGrid's ItemSource to have the Data show up magically.
grid1.ItemSource = result;
As long as the result is an array of items it will create a row per item.
You may want to specify which items are shown but that is done by modifying DataGrid.Columns definitions and setting DataGrid.AutogenerateColumns = false
EDIT: With your data and models
//Just a small change to the Collection
public class BrandsCollection {
private ObservableCollection<Brands> _records;
public ObservableCollection<Brands> records { get => _records; set => _records= value; }
}
And to parse the data...
JsonSerializer serializer = JsonSerializer.CreateDefault();
using (StringReader reader = new StringReader(json))
{
using (JsonTextReader jsonReader = new JsonTextReader(reader))
{
var result = serializer.Deserialize<BrandsCollection>(jsonReader);
}
}
You have to remember to either use the same names as the json labels or use Json Attributes. For more info on that you can go to the official Newtonsoft.Json documentation
Related
I have the below Json Array and I need to get the value of the key inside my "relation" object. How do I do that? The relation object has different set of key-value pair for each object in the array.
[
{
"data": {
"_hash": null,
"kind": "ENTITY",
"id": "test122",
"payload": {
"attributes": {
"bbl:27": false
},
"relations": {
"bbl:45": "P18",
"bbl:P18": "P562"
},
"type": [
"P185"
]
}
}
}
]
In above inside relations the keys are "bbl:45" and "bbl:P18". I need these values, in specific I need to extract 45 and P18, considering bbl remain constant inside relation object. how do i do it. There are multiple objects in the Jarray, I have shown only one.
I would mention, if you can change data format, consider doing so, having payload as part as a member proper name is highly unusual but you could do like this:
[Fact]
public void DynamicJsonHandlingTest()
{
var serialized = "[{\"data\":{\"_hash\":null,\"kind\":\"ENTITY\",\"id\":\"test122\",\"payload\":{\"attributes\":{\"bbl:27\":false},\"relations\":{\"bbl:45\":\"P18\",\"bbl:P18\":\"P562\"},\"type\":[\"P185\"]}}}]";
using var jDoc = JsonDocument.Parse(serialized);
var enumerator = jDoc.RootElement.EnumerateArray();
foreach(var JsonElement in enumerator)
{
var relationsElement = JsonElement.GetProperty("data").GetProperty("payload").GetProperty("relations");
foreach (var ele in relationsElement.EnumerateObject())
{
var sought = ele.Name.Split(":")[1];
//sought now cointains first 45 then P18
}
}
}
The following Code solves your problem.
using Newtonsoft.Json.Linq;
using System;
using System.Linq;
using Json.Net;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
string json = "{\"data\":{\"_hash\":null,\"kind\":\"ENTITY\",\"id\":\"test122\",\"payload\":{\"attributes\":{\"bbl:27\":false},\"relations\":{\"bbl:45\":\"P18\",\"bbl:P18\":\"P562\"},\"type\":[\"P185\"]}}}";
JObject jsonObject = JObject.Parse(json);
var results = jsonObject["data"]["payload"]["relations"];
foreach (var item in results)
{
string propName = ((JProperty)item).Name;
string requiredValue = propName.Split(':')[1];
Console.WriteLine($"Required: {requiredValue} !");
}
}
}
}
You should add some error handling when the property name does not contains :
For dynamic keys you can use dictionary:
public class Root
{
public Data data { get; set; }
}
public class Data
{
public Payload payload { get; set; }
}
public class Payload
{
public Dictionary<string, string> relations { get; set; }
}
var result = JsonConvert.DeserializeObject<List<Root>>(sourcejson);
And then next will contain all keys:
var keys = result
.Select(r => r.data)
.Select(r => r.payload)
.SelectMany(r => r.relations.Keys)
Or use the LINQ to JSON API:
var result = JsonConvert.DeserializeObject<JArray>(sourcejson);
var list = result
.Children()
.SelectMany(c => c["data"]["payload"]["relations"].Children<JProperty>())
.Select(p => p.Name)
.ToList(); // list is {"bbl:45", "bbl:P18"}
And then you can process the keys as you need - for example using Split(':') to turn "bbl:45" into array of "bbl" and `"45".
The work of transforming JSON data into a typed data model through seems to be made much more complex by the "help" the combination of SharePoint and MS Graph offer. :-)
I have a SharePoint List in Microsoft 365 that I'm accessing through the Graph API in C#, where the query destination is a typed class with properties identical to the SharePoint List Column Properties.
The ListItem class Graph API returns the results in the a Fields.AdditionalData of type Dictionary<string,object{System.Text.Json.JsonElement}> It needs to become an IEnumerable<DataItem>, which I can do by taking the List from the query result through a Serialize/Deserialize round trip, as below:
var backToJSON = ListItems.Select(o => System.Text.Json.JsonSerializer.Serialize(o.Fields.AdditionalData));
var stronglyTypedItems = backToJSON.Select(jsonO => System.Text.Json.JsonSerializer.Deserialize<DataItem>(jsonO));
Is there a way to do this, either with smarter OData or something in Graph API I haven't seen, without taking what used to be JSON and sending it back through JSON Serializers twice?
More details below:
Sample output JSON from Graph Explorer, where value contains an array of :
"value" : [
{ "id": "1001,
"fields": {
"Column" : "true",
"Column2" : "value2",
"Column3" : "65"
}
},
{ "id": "1002,
"fields": {
<and so forth until the array terminates>
]
}
Corresponding C# Class (literally built using "Paste JSON as class"):
Public class DataItem {
public bool Column {get; set;}
public string Column2 {get; set;}
public int Column3 {get; set;}
}
The "Helper" classes in the C# Graph API deliver mostly transformed into the array of fields I actually need:
private static GraphServiceClient graphClient;
public static IListItemsCollectionRequest LicenseExpirationsList => graphClient
.Sites["<guid>"]
.Lists["<nameOfList>"].Items
.Request()
.Header("Accept", "application/json;odata.metadata=none")
.Select("fields,id")
.Expand("fields");
var ListItems = (await GraphHelper.LicenseExpirationsList.GetAsync()).CurrentPage;
// JSON round tripping through JSONSerializer to get the strong type...
// But why? ListItems.Fields.AdditionalData is a Dictionary of JSON elements in the first place!
var backToJSON = ListItems.Select(o => System.Text.Json.JsonSerializer.Serialize(o.Fields.AdditionalData));
var stronglyTypedItems = backToJSON.Select(jsonO => System.Text.Json.JsonSerializer.Deserialize<DataItem>(jsonO));
return stronglyTypedItems;
You could customize the client's JSON serialization to return a derived type of default FieldValueSet.
First, define your own extended FieldValueSet:
public class FieldValueSetWithDataItem : FieldValueSet
{
public bool Column { get; set; }
public string Column2 { get; set; }
public int Column3 { get; set; }
}
Second, implement your own JSON converter:
class CustomFieldValueSetJsonConverter : JsonConverter<FieldValueSet>
{
private static readonly JsonEncodedText ODataTypeProperty
= JsonEncodedText.Encode("#odata.type");
private static readonly JsonEncodedText IdProperty
= JsonEncodedText.Encode("id");
private static readonly JsonEncodedText ColumnProperty
= JsonEncodedText.Encode("Column");
private static readonly JsonEncodedText Column2Property
= JsonEncodedText.Encode("Column2");
private static readonly JsonEncodedText Column3Property
= JsonEncodedText.Encode("Column3");
public override FieldValueSet Read(ref Utf8JsonReader reader,
Type typeToConvert, JsonSerializerOptions options)
{
var result = new FieldValueSetWithDataItem();
using var doc = JsonDocument.ParseValue(ref reader);
var root = doc.RootElement;
foreach (var element in root.EnumerateObject())
{
if (element.NameEquals(ODataTypeProperty.EncodedUtf8Bytes))
{
result.ODataType = element.Value.GetString();
}
else if (element.NameEquals(IdProperty.EncodedUtf8Bytes))
{
result.Id = element.Value.GetString();
}
else if (element.NameEquals(ColumnProperty.EncodedUtf8Bytes))
{
result.Column = element.Value.GetBoolean();
}
else if (element.NameEquals(Column2Property.EncodedUtf8Bytes))
{
result.Column2 = element.Value.GetString();
}
else if (element.NameEquals(Column3Property.EncodedUtf8Bytes))
{
result.Column3 = element.Value.GetInt32();
}
else
{
// Capture unknown property in AdditionalData
if (result.AdditionalData is null)
{
result.AdditionalData = new Dictionary<string, object>();
}
result.AdditionalData.Add(element.Name, element.Value.Clone());
}
}
return result;
}
public override void Write(Utf8JsonWriter writer,
FieldValueSet value, JsonSerializerOptions options)
{
// To support roundtrip serialization:
writer.WriteStartObject();
writer.WriteString(ODataTypeProperty, value.ODataType);
writer.WriteString(IdProperty, value.Id);
if (value is FieldValueSetWithDataItem dataItem)
{
writer.WriteBoolean(ColumnProperty, dataItem.Column);
writer.WriteString(Column2Property, dataItem.Column2);
writer.WriteNumber(Column3Property, dataItem.Column3);
}
if (value.AdditionalData is not null)
{
foreach (var kvp in value.AdditionalData)
{
writer.WritePropertyName(kvp.Key);
((JsonElement)kvp.Value).WriteTo(writer);
}
}
writer.WriteEndObject();
}
}
Last, use the JSON converter when making your request:
// Use custom JSON converter when deserializing response
var serializerOptions = new JsonSerializerOptions();
serializerOptions.Converters.Add(new CustomFieldValueSetJsonConverter());
var responseSerializer = new Serializer(serializerOptions);
var responseHandler = new ResponseHandler(responseSerializer);
var request = (ListItemsCollectionRequest)client.Sites[""].Lists[""].Items.Request();
var listItems = await request
.WithResponseHandler(responseHandler)
.GetAsync();
To access your column values:
var col3 = ((FieldValueSetWithDataItem)listItem.Fields).Column3;
You may find the HttpProvider of the GraphServiceClient helpful in this scenario:
var listItemsCollectionRequest = graphServiceClient
.Sites["<guid>"]
.Lists["<nameOfList>"]
.Items
.Request()
.Header("Accept", "application/json;odata.metadata=none")
.Select("fields,id")
.Expand("fields");
using (var requestMessage = listItemsCollectionRequest.GetHttpRequestMessage())
{
using var responseMessage = await graphServiceClient.HttpProvider.SendAsync(requestMessage);
//deserialize the response body into DataItem
}
By using the HttpProvider you can directly work with the response from the Graph API and deserialize the response body into your custom class.
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.
I am generating a json string using NewtosoftJson using a table to format the the json. This is a simple key value pairs list and looks like:
public class items
{
private string key = String.Empty;
private string value = String.Empty;
public string Key
{
get
{
return key;
}
set
{
if (value != key)
{
key = value;
}
}
}
public string Value
{
get
{
return value;
}
set
{
if (value != this.value)
{
this.value = value;
}
}
}
}
When a list is populated and then serialised I get this JSON:
"Items": [
{
"Key":"FirstValue",
"Value":"One"
},
{
"Key":"SecondValue",
"Value":"Two"
},
{
"Key":"ThirdValue",
"Value":"Three"
}
]
What I need to get is:
"customData": {
"items": [
{
"Key":"FirstValue",
"Value":"One"
},
{
"Key":"SecondValue",
"Value":"Two"
},
{
"Key":"ThirdValue",
"Value":"Three"
}
]
}
I have tried creating a second class CustomData but can't see how to get the original JSON into the second class! Could you advice me on the correct way to construct the second class and method used to populate it please.
You can create an anonymous object and serialize that:
var objContainingItems = ... // your usual code
var customDataObj = new { customData = objContainingItems };
string json = JsonConvert.SerializeObject(customDataObj);
This is the most convenient solution if all you are interested in is serializing.
If you also want to be able to deserialize it, then you will need to use a class as specified in the answer by #William Moore.
Create a class customData and create a reference to the class items inside it. Then serialise your customData class using Newtonsoft.Json rather than your items class. So you will have:
public class CustomData
{
public items[] items; // I would rename the class items to item
}
Then you have an object of type customData, called customData which you pass into Newtonsoft.
You could then use the following to serialise/deserialise your data:
CustomData input = new CustomData();
input.items = []; // Whatever you have at the moment?
string json = JsonConvert.SerializeObject(account) //optionally set Formatting.Indented
CustomData deserialised = JsonConvert.DeserializeObject<CustomData>(json);
This question already has answers here:
Serializing an interface/abstract object using NewtonSoft.JSON
(1 answer)
Why does Json.NET not include $type for the root object when TypeNameHandling is Auto?
(3 answers)
json.net - how to add property $type ONLY on root object
(2 answers)
Closed 4 years ago.
I have encountered an interesting scenario that i don't understand about Json.Net.
I need to serialize objects in order to POST them to API. Objects are created using types with some hierarchy. So in the serialization methods, base type is used to receive the object/objects and serialize it/them using Json.Net with TypeNameHandling.Auto configured.
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace SerializationTest
{
class Program
{
static void Main(string[] args)
{
var dj = new Dj() { Name = "Tim", Label = "UMG", StageName = "Avicii" };
var djJson = JSerializer.Serialize(dj);
var djs = new List<Person>();
djs.Add(dj);
var djsJson = JSerializer.Serialize(djs);
Console.ReadKey();
}
}
public static class JSerializer
{
public static string Serialize(Person person)
{
var result = JsonConvert.SerializeObject(person, Formatting.Indented, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
return result;
}
public static string Serialize(IReadOnlyList<Person> people)
{
var eventModelsSerialized = JsonConvert.SerializeObject(people, Formatting.Indented,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
return eventModelsSerialized;
}
}
public abstract class Person
{
public string Name { get; set; }
}
public abstract class Artist : Person
{
public string Label { get; set; }
}
public class Dj : Artist
{
public string StageName { get; set; }
}
}
What happens then is that:
for single object serialization case, I got very basic Json without any type information:
{
"StageName": "Avicii",
"Label": "UMG",
"Name": "Tim"
}
while for list of objects serialization case, we got Json with type information:
[
{
"$type": "SerializationTest.Dj, SerializationTest",
"StageName": "Avicii",
"Label": "UMG",
"Name": "Tim"
}
]
This creats confusion for server side. So when I want to just serialize single object, the server side has problem to serialize it due to lack of type information. So the workaround is creating a list to include the object and serialize as list instead.
Could anyone explain why Json.Net serialize differently for these two cases? Thanks in advance.
you can wrap your object as given below
var dj = new Dj() { Name = "Tim", Label = "UMG", StageName = "Avicii" };
var obj = new
{
Dj = dj
};
var eventModelsSerialized = JsonConvert.SerializeObject(obj, Formatting.Indented,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
Result with type information:
{
"Dj": {
"StageName": "Avicii",
"Label": "UMG",
"Name": "Tim"
}
}