JSon.Net deserialize custom class - c#

I want to deserialize a json object to a custom class. The class could look like this:
public class CommunicationMessage {
public string Key { get; set; }
public string Value { get; set; }
public List<CommunicationMessage> Childs { get; set; }
}
And the json I want to deserialize looks like this:
{
"Skills": [{
"Skill": [{
"SkillID": "1",
"ParticipantID": "7",
"CanDo": "True"
}, {
"SkillID": "2",
"ParticipantID": "7",
"CanDo": "True"
}, {
"SkillID": "3",
"ParticipantID": "7",
"CanDo": "False"
}]
}]
}
And this is the code I am using to deserialize the json:
private void ReadRecursive(JToken token, ref CommunicationMessage root) {
if (token is JProperty) {
CommunicationMessage msg = new CommunicationMessage();
if (token.First is JValue) {
msg.Key = ((JProperty)token).Name;
msg.Value = (string)((JProperty)token).Value;
} else {
msg.Key = ((JProperty)token).Name;
foreach (JToken child in token.Children()) {
ReadRecursive(child, ref msg);
}
}
root.Childs.Add(msg);
} else {
foreach (JToken child in token.Children()) {
ReadRecursive(child, ref root);
}
}
}
I am expecting to get this hirarchy:
Skills
Skill
SkillID:1
ParticipantID:7
CanDo:true
Skill
SkillID:2
ParticipantID:7
CanDo:true
Skill
SkillID:3
ParticipantID:7
CanDo:false
But I am getting this:
Skills
Skill
SkillID:1
ParticipantID:7
CanDo:
SkillID:2
ParticipantID:7
CanDo:true
SkillID:3
ParticipantID:7
CanDo:false
I can't find the lines where my failure is, so maybe anyone can help me here.
Thanks!!

Your code seems to do its job quite ok (although there are simpler ways to achieve your goal). The problematic part is the JSON it self. It's organized in two arrays.
So your code puts out the Skills-array (which has one element) and the Skill-array which holds the actual the 3 skills you're expecting.
{
"Skills": [{ // array -> note the [
"Skill": [{ // array -> note the [
Hence one way to solve this would be to edit the JSON (if this is possible):
{
"Skills": [{
"SkillID": "1",
"ParticipantID": "7",
"CanDo": "True"
}, {
"SkillID": "2",
"ParticipantID": "7",
"CanDo": "True"
}, {
"SkillID": "3",
"ParticipantID": "7",
"CanDo": "False"
}]
}

Use Newtonsoft Json.NET.
output message = JsonConvert.DeserializeObject<CommunicationMessage>(json);
(where json is the JSON string.)
I used this page - json2csharp - to create classes that match the JSON you posted:
public class Skill2
{
public string SkillID { get; set; }
public string ParticipantID { get; set; }
public string CanDo { get; set; }
}
public class Skill
{
public List<Skill2> Skill { get; set; }
}
public class CommunicationMessage
{
public List<Skill> Skills { get; set; }
}
The class names are autogenerated. It always names the root object RootObject. But you can change it to CommunicationMessage (I did.)
If you want the class to have different property names that don't match the JSON you can do that with attributes.
public class Skill2
{
[JsonProperty["Key"]
public string SkillID { get; set; }
[JsonProperty["Value"]
public string ParticipantID { get; set; }
public string CanDo { get; set; }
}

Using a DataContractJsonSerializerfrom System.Runtime.Serializationwould make the deserialization easier:
Stream data = File.OpenRead(#"data.json");
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(CommunicationMessage));
CommunicationMessage message = (CommunicationMessage)serializer.ReadObject(data);
But you also need a class like this:
[DataContract]
class CommunicationMessage
{
[DataContract]
class SkillsData
{
[DataContract]
internal class SkillData
{
[DataMember(Name = "SkillID")]
internal object SkillID;
[DataMember(Name = "ParticipantID")]
internal object ParticipantID;
[DataMember(Name = "CanDo")]
internal object CanDo;
}
[DataMember(Name = "Skill")]
internal SkillData[] Skill;
}
[DataMember(Name = "Skills")]
SkillsData[] Skills;
}
Above you have the class SkillData, which holds the data of each skill. So if you take the array Skill, you have the wanted hirarchy.

You could just check for when you are at the right level/object type using logic inside your recursive method.
void ReadRecursive(JToken token, ref CommunicationMessage root)
{
var p = token as JProperty;
if (p != null && p.Name == "Skill")
{
foreach (JArray child in p.Children())
{
foreach (JObject skill in child.Children())
{
// Create/add a Skill message instance for current Skill (JObject)
var skillMsg = new CommunicationMessage { Key = p.Name };
// Populate Childs for current skill instance
skillMsg.Childs = new List<CommunicationMessage>();
foreach (JProperty skillProp in skill.Children())
{
skillMsg.Childs.Add(new CommunicationMessage
{
Key = skillProp.Name,
Value = (string)skillProp.Value
});
}
root.Childs.Add(skillMsg);
}
}
}
// Recurse
foreach (JToken child in token.Children())
ReadRecursive(child, ref root);
}

Related

Flatten nested JSON with JSON.NET in C#

I receive a bill of materials in JSON format via a WebApi, which has a corresponding hierarchy.
The hierarchy or the nesting can be any depth.
An example bill of materials is shown below:
{
"Quantity":0,
"QuantityUnit":"pcs",
"PartNumber":"12345",
"Parent":"",
"Children":[
{
"Quantity":1,
"QuantityUnit":"pcs",
"PartNumber":"88774",
"Parent":"12345",
"Children":[
{
"Quantity":1,
"QuantityUnit":"pcs",
"PartNumber":"42447",
"Parent":"88774"
},
{
"Quantity":0.420,
"QuantityUnit":"kg",
"PartNumber":"12387",
"Parent":"88774"
}
]
}
]
}
How can I resolve this nested structure into a simple structure using JSON.NET in C#?
I want to transform it to:
[
{
"Quantity":0,
"QuantityUnit":"pcs",
"PartNumber":"12345",
"Parent":""
},
{
"Quantity":1,
"QuantityUnit":"pcs",
"PartNumber":"88774",
"Parent":"12345"
},
{
"Quantity":1,
"QuantityUnit":"pcs",
"PartNumber":"42447",
"Parent":"88774"
},
{
"Quantity":0.420,
"QuantityUnit":"kg",
"PartNumber":"12387",
"Parent":"88774"
}
]
For the deserialization I use the following class:
public class Bom
{
public class TopLevel
{
public double Quantity { get; set; }
public string QuantityUnit { get; set; }
public string PartNumber { get; set; }
public string Parent { get; set; }
public List<Item> Children { get; set; }
}
public class Item
{
public double Quantity { get; set; }
public string QuantityUnit { get; set; }
public string PartNumber { get; set; }
public string Parent { get; set; }
}
public double Quantity { get; set; }
public string QuantityUnit { get; set; }
public string PartNumber { get; set; }
public string Parent { get; set; }
public IList<TopLevel> Children { get; set; }
}
Furthermore, I use this code to deserialize the JSON to an object:
Bom bom = JsonConvert.DeserializeObject<Bom>(File.ReadAllText(jsonPath));
First let's define a mapper
JObject Map(JObject source)
{
var result = (JObject)source.DeepClone();
result.Remove("Children");
return result;
}
It simply clones the object and removes the Children property
Next let's define a recursive function to accumulate the JObjects
void Flatten(JArray children, JArray accumulator)
{
if (children == null) return;
foreach (JObject child in children)
{
accumulator.Add(Map(child));
Flatten((JArray)child["Children"], accumulator);
}
}
And finally let's make use of them
var semiParsed = JObject.Parse(json);
var accumulator = new JArray();
accumulator.Add(Map(semiParsed));
Flatten((JArray)semiParsed["Children"], accumulator);
The ToString call on the accumulator will return this
[
{
"Quantity": 0,
"QuantityUnit": "pcs",
"PartNumber": "12345",
"Parent": ""
},
{
"Quantity": 1,
"QuantityUnit": "pcs",
"PartNumber": "88774",
"Parent": "12345"
},
{
"Quantity": 1,
"QuantityUnit": "pcs",
"PartNumber": "42447",
"Parent": "88774"
},
{
"Quantity": 0.42,
"QuantityUnit": "kg",
"PartNumber": "12387",
"Parent": "88774"
}
]
UPDATE #1
If your source json contains a deep hierarchy (lets say more than 5 levels) then the DeepClone is not really efficient, since you are copying the whole subtree.
To fix this problem you just need to rewrite the Map function
JObject Map(JObject source)
=> JObject.FromObject(new
{
Quantity = (double)source["Quantity"],
QuantityUnit = (string)source["QuantityUnit"],
PartNumber = (string)source["PartNumber"],
Parent = (string)source["Parent"]
});
Deserialize the original list, flatten it with Enumerable.SelectMany, and serialize the resulting sequence.

Getting data in Json.Net c#

I want to get data from json file correctly. The json data file I modeled for this is as follows:
{
"Free title 1":[
{
"Subject": "a1",
"Relation": "a2"
},
{
"Subject": "b1",
"Relation": "b2"
}
],
"Another free title":[
{
"Subject": "z1",
"Relation": "z2"
},
{
"Subject": "y1",
"Relation": "y2"
}
],
"Unordered title":[
{
"Subject": "x1",
"Relation": "x2"
},
{
"Subject": "w1",
"Relation": "w2"
}
]
}
This is how I create an object class:
public class _Infos_
{
public List<_Info_> Infos { get; set; }
}
public class _Info_
{
public string Subject { get; set; }
public string Relation { get; set; }
}
And finally I'm trying to get the data in a method like this:
var js = JsonConvert.DeserializeObject<_Infos_>(File.ReadAllText("__FILE_PATH__"));
foreach (var j in js.Infos)
{
MessageBox.Show(j.Subject);
}
I get the error that js is empty. Here I want to get Free title 1, Another free title and Unordered title in a list. Of course, these titles will be constantly changing. Afterwards, I want to get the Subject and Relation data under these titles. But I have no idea how to get it.
This data structure is a dictionary of collections of _Info_s. You need to deserialize it to Dictionary<string, List<_Info_>>.
Here are System.Text.Json and Json.net examples:
var d = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, List<_Info_>>>(json);
var d2 = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, List<_Info_>>>(json);
Your class definition is a little wrong.
You can use online tools "json to c#" to generate the correct classes.
like this one: https://json2csharp.com
Your "root" of your json for example does not contain an array in your json. The property "Free title 1":[..] is an array, so your root needs a property with the name FreeTitle1 and it has to be an array/list.
public class Root
{
[JsonProperty("Free title 1")]
public List<TitleInfo> FreeTitle1 { get; set; }
[JsonProperty("Another free title")]
public List<TitleInfo> AnotherFreeTitle { get; set; }
[JsonProperty("Unordered title")]
public List<TitleInfo> UnorderedTitle { get; set; }
}
public class TitleInfo
{
public string Subject { get; set; }
public string Relation { get; set; }
}
If your object members have dynamic names, you can also manually deserialize the object, e.g. using the general type JObject. E.g.
JObject obj = JObject.Parse(File.ReadAllText("__FILE_PATH__"));
JObject implements IEnumerable<KeyValuePair<string, JToken>> over which you can iterate.
Each member will then have JToken Value, which is a JArray in this case, which you can cast to a List of your type.
foreach (var groups in obj)
{
var infos = groups.Value.ToObject<List<_Info_>>();
// .. loop over infos
}

How to deserialise or format WebAPI result into specific json structure

I was working with a .net core 3.1 Web API. Which is getting data from an external API. Following is my code Controller part
[HttpGet("transinfo/{id}")]
public Object GettransactionData(int id)
{
var result=_transaction.GettransactionDetails(id).Result;
List<PipeLineResponse> P = JsonConvert.DeserializeObject<List<PipeLineResponse>>(result.ToString());
PipeLineResponseObject P1 = new PipeLineResponseObject();
P1.data = P;
return P1;
}
And my service code as follows
public async Task<Object> GettransactionDetails(int id)
{
string request=//fetched from db
var stringContent = new StringContent(request);
Client = utilities.GetHttpClient();
string apiEndpoint=//External API URL
HttpResponseMessage httpResponseMessage = await Client.PostAsync(apiEndpoint, stringContent);
if (httpResponseMessage.IsSuccessStatusCode)
{
return await httpResponseMessage.Content.ReadAsAsync<Object>();
}
}
But i am getting the result in following format (response from postman)
{
"data": [
{
"Tranid": "34540d40-7db8-44c1-9a2a-5072c2d01756",
"fields": {
"Fields.10": "1001",
"Fields.11": "Test1",
"Fields.12": "Fixed1"
}
},
{
"Tranid": "145800f9-c4a5-4625-84d7-29af5e674a14",
"fields": {
"Fields.10": "1002",
"Fields.11": "Test2",
"Fields.12": "Fixed2"
}
}
]
}
But i need the data in following format
{
"data": [
{
"TransactionID": "34540d40-7db8-44c1-9a2a-5072c2d01756",
"fieldsList": [
{
"fieldId": "10",
"fieldValue": "1001"
},
{
"fieldId": "11",
"fieldValue": "Test1"
},
{
"fieldId": "12",
"fieldValue": "Fixed1"
}
]
},
{
"TransactionID": "145800f9-c4a5-4625-84d7-29af5e674a14",
"fieldsList": [
{
"fieldId": "10",
"fieldValue": "1002"
},
{
"fieldId": "11",
"fieldValue": "Test2"
},
{
"fieldId": "12",
"fieldValue": "Fixed2"
}
]
}
]
}
How can i achieve this ? is possible to deserialise using JObject or JArray? Please help.
i have tried to create following model class and tried to deserialise but not getting result as expected.
public class PipeLineResponse
{
public string TransactionID { get; set; }
public List<Dictionary<string, string>> fields { get; set; }
}
public class PipeLineResponseObject
{
public List<PipeLineResponse> data { get; set; }
}
How to create that json in that format any DTO or Automapper will work ? Please help me with samples.
The solution that I am laying down here takes the DTO approach. The response from the service is being deserialized to the DTO, which further is being manually mapped to the final ViewModel that we are sending to the client. By no means, this implementation is production-ready and there is scope for improvement, for which I am adding in comments. But this gives a detailed understanding of how we can handle these kind of scenarios. We are making use of Newtonsoft.Json, which can be pulled into your project via the NuGet package manager.
Structure of the DTO
// RootDTO.cs
// This structure is directly based on the response obtained from remote service.
public class Fields
{
[JsonProperty(PropertyName ="Fields.10")]
public string Fields10 { get; set; }
[JsonProperty(PropertyName = "Fields.11")]
public string Fields11 { get; set; }
[JsonProperty(PropertyName = "Fields.12")]
public string Fields12 { get; set; }
}
public class Datum
{
public string Tranid { get; set; }
public Fields fields { get; set; }
}
public class RootDTO
{
[JsonProperty(PropertyName ="data")]
public List<Datum> data { get; set; }
}
Structure of ViewModel
// PipelineResponse.cs
public class FieldsList
{
public string fieldId { get; set; }
public string fieldValue { get; set; }
}
public class ResponseDatum
{
[JsonProperty(PropertyName = "TransactionID")]
public string TransactionID { get; set; }
public List<FieldsList> fieldsList { get; set; }
}
public class PipelineResponse
{
public List<ResponseDatum> data { get; set; }
}
Deserializing the response to the DTO
// ...other code
var responseString = await httpResponseMessage.Content.ReadAsAsync<Object>();
// This is where the DTO object is created. This should be mapped to view model type.
var responseDTO = JsonConvert.DeserializeObject<RootDTO>(responseString);
Mapping the DTO to ViewModel
The mapping from DTO type to ViewModel type needs to be done before sending the response to the client. It is the view model type that is sent to the client. This logic can be placed within a separate helper (ideally, to separate concerns) or any other location as per the practices you are following.
public PipelineResponse ConvertResponseDTOToResponse(RootDTO responseDTO)
{
// FieldId is being hardcoded here. Instead, you can use Reflection to
// fetch the property name, split on '.' and take the item at index 1.
// Notice that DTO properties have "JsonProperty" attributes for this.
try
{
List<ResponseDatum> responseList = new List<ResponseDatum>();
if (responseDTO != null)
{
// Reflection can be used to avoid hardcoding on 'fieldId'
foreach (var item in responseDTO.data)
{
var responseDataObj = new ResponseDatum
{
TransactionID = item.Tranid,
fieldsList = new List<FieldsList>
{
new FieldsList
{
fieldValue = item.fields.Fields10,
fieldId = "10"
},
new FieldsList
{
fieldValue = item.fields.Fields11,
fieldId = "11"
},
new FieldsList
{
fieldValue = item.fields.Fields12,
fieldId = "12"
}
}
};
responseList.Add(responseDataObj);
}
}
// This object is what you return from your controller endpoint finally.
// The serialized response of this object is of the json structure you need
return new PipelineResponse { data = responseList };
}
catch (Exception ex)
{
throw ex;
}
}

c# jtoken select token

I am trying to grab a url value from a json using JSON.net and jtoken SelectTokens method, I'm able to get the label for the url from the json but not able to get the url value.
This is a part of the json from which I'm trying to grab the url. I'm trying to get the label "Law Library " and its corresponding "href"
"anchorEndControls": [
{
"State": {
"clickAction": 19,
"image": "$$USERDATA$$Toolbars\\images\\Comm.png",
"showLabel": true,
"label": "Commissary ",
"href": "https://deposits.com",
"favicon": false
},
"type": 2,
"mergeStyles": []
},
{
"State": {
"clickAction": 19,
"image": "$$USERDATA$$Toolbars\\images\\Law.png",
"showLabel": true,
"label": "Law Library",
"href": "https://apps.com",
"favicon": false
},
"type": 2,
"mergeStyles": []
},
public class Program
{
public static void Main(string[] args)
{
var list1 = jObj.SelectTokens("$.....State.*")
.Where(t => t.Value<string>() == labeltofind).ToList()
foreach (var item in list1)
{
Console.WriteLine(item);
}
As you can see I am able to get the label "Law Library " but not able to get the corresponding href
This method gets you the information of each of the lists based on your input.
string labelToFind = "Commissary ";
var list1 = jObj.SelectTokens($"anchorEndControls[?(#.State.label == '{labelToFind}')]").ToList();
Alternate Suggestion
Linq is another solution i can suggest since you can use queries to get specific items from your json and get corresponding values. You can get the classes for the json you are using from any of the websites such as www.json2csharp.com.
public class State
{
public int clickAction { get; set; }
public string image { get; set; }
public bool showLabel { get; set; }
public string label { get; set; }
public string href { get; set; }
public bool favicon { get; set; }
}
public class AnchorEndControl
{
public State State { get; set; }
public int type { get; set; }
public List<object> mergeStyles { get; set; }
}
public class RootObject
{
public List<AnchorEndControl> anchorEndControls { get; set; }
}
Usage
Deserialize the object to the root class (based on the root element of your json)
Find the element that has the Label you are looking for
Use that element to look up corresponding href value.
var jsonObj = JsonConvert.DeserializeObject<RootObject>(json);
var element = jsonObj.anchorEndControls.Where(x => x.State.label.Equals("Law Library")).First();
Console.WriteLine(element.State.href);
Output
https://apps.com
You are diving in too deep with that .Where() clause, it will filter out all the properties except Label. Here's a way to do it:
foreach (JToken state in jObj.SelectTokens("$.....State"))
{
if (state["label"].ToString() == labeltofind)
{
Console.WriteLine(state["href"]);
}
}

Deserialize Json to Class that implements Ienumerable in Asp.net

I've below function which fetches facebook data and return it as string.
public static string GetUserNewsFeed(string strAccessToken)
{
Dictionary<string, object> PostDetail = new Dictionary<string, object>();
DateTime CreatedDateTime = DateTime.Today.AddDays(-90);
var epoch = (CreatedDateTime.ToUniversalTime().Ticks - 621355968000000000) / 10000000;
PostDetail.Add("Posts", "SELECT post_id,source_id FROM stream WHERE filter_key in (SELECT filter_key FROM stream_filter WHERE uid=me() AND type='newsfeed') and (likes.count > 0 or comments.count > 0) and created_time > " + epoch);
PostDetail.Add("PostComments", "select post_id,fromid from comment where post_id in (select post_id from #Posts)");
PostDetail.Add("PostLikes", "SELECT object_id,post_id,user_id FROM like WHERE post_id in (select post_id from #Posts)");
string Json = HttpUtility.UrlEncode(Newtonsoft.Json.JsonConvert.SerializeObject(PostDetail));
return RunFQLQuery(Json, strAccessToken);
}
Here's the code that calls this function and convert it to Jobject using Json.NET :
strFeed = FacebookAPI.GetUserNewsFeed(Convert.ToString(Session["access_token"]));
JObject objStreamData = JObject.Parse(strFeed);
var PostResponse = objStreamData.SelectToken("data[0]");
Below line deserialize it to NewsFeedPost Class :
var Posts = JsonConvert.DeserializeObject<NewsFeedPost>(PostResponse.ToString());
And below is the class :
public class NewsFeedPost
{
public string name { get; set; }
public List<Post> fql_result_set { get; set; }
}
public class Post
{
public string post_id { get; set; }
public string source_id { get; set; }
}
Now problem is when I change my NewsFeedPost class to below it throws error "Cannot deserialize JSON object into type 'BAL.NewsFeedPost'" :
public class NewsFeedPost : IEnumerable<Post>
{
public string name { get; set; }
public List<Post> fql_result_set { get; set; }
public IEnumerator<Post> GetEnumerator()
{
foreach (Post item in fql_result_set)
{
yield return item;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Below is the sample of Json Response :
{
"data": [
{
"name": "Posts",
"fql_result_set": [
{
"post_id": "1"
},
{
"post_id": "2"
}
]
},
{
"name": "PostComments",
"fql_result_set": [
{
"post_id": "3",
"fromid": 4
},
{
"post_id": "5",
"fromid": 6
}
]
},
{
"name": "PostLikes",
"fql_result_set": [
{
"object_id": 7,
"post_id": "8",
"user_id": 9
},
{
"object_id": 10,
"post_id": "11",
"user_id": 12
}
]
}
]
}
I just want to enumerate through list and create comma delimited list and save it to db. Can anybody throw some light what needs to be done? How can I get the Count and Length Property for List?
Two things:
1) Add JsonObjectAttribute to NewsFeedPost:
[JsonObject]
public class NewsFeedPost : IEnumerable<Post>
2) To deserialize that JSON which you show, you need another class:
public class NewsFeedPosts
{
public List<NewsFeedPost> Data { get; set; }
}
and then you'd call your deseralize with that class as what you want deserialized:
var Posts = JsonConvert.DeserializeObject<NewsFeedPosts>(PostResponse.ToString());
You may want to try a cast on the DeserializeObject
var Posts = JsonConvert.DeserializeObject<NewsFeedPost>((NewsFeedPost)PostResponse.ToString());

Categories