serialization issue with json structure using newtonsoft - c#

This is an old application (.Net 4.5).
I am following the guide here: https://weblog.west-wind.com/posts/2012/aug/30/using-jsonnet-for-dynamic-json-parsing
My goal is to have a way to store a flexible json structure without tying it down to a static structure. For example, in the TriggerJson below, the actual Trigger field is of type string, which is supposed to be json. That json structure could reflect ExpiryTriggerJson, or some other structure which is determined by TriggerType.
I have the following structure:
public class TriggerJson
{
public string TriggerType { get; set; }
public string ConfiguredBy { get; set; }
public string Trigger { get; set; }
}
public class ExpiryTriggerJson
{
public string ActionType { get; set; }
public TriggerRecipient[] Recipients { get; set; }
}
public class TriggerRecipient
{
public string Id { get; set; }
public string Name { get; set; }
public bool IsTag { get; set; }
}
In the following code, I am creating a list of TriggerJson such that each element's Trigger field be a json structure made from ExpiryTriggerJson object:
var tjList = new List<TriggerJson>();
var triggerJson = new TriggerJson();
triggerJson.TriggerType = TriggerJsonHelper.ExpiryTriggerType;
triggerJson.Trigger = JsonConvert.SerializeObject(new ExpiryTriggerJson
{
Recipients = taskRecipients,
ActionType = TriggerJsonHelper.ExpiryTriggerActionType_Task
});
triggerJson.ConfiguredBy = configuredBy;
tjList.Add(triggerJson);
fieldValue.TriggersJson = JsonConvert.SerializeObject(tjList);
This creates the following structure for example where Trigger field reflects a serialized structure which has escaped double quotes due to double serialization:
[{"TriggerType":"ExpiryTrigger","ConfiguredBy":"acd1ac353ac44e078aaef8ce6479a4c6","Trigger":"{\"ActionType\":\"CreateReminderTask\",\"Recipients\":[{\"Id\":\"70050a95-f31b-41b7-9b49-0688fa76dba5\",\"Name\":\"blah blah\",\"IsTag\":false}]}"}]
This creates a problem for me when trying to deserialize this data later on when reading it back:
JArray jsonObj = JArray.Parse(triggersJson);
foreach (dynamic obj in jsonObj)
{
if (obj.TriggerType == ExpiryTriggerType)
{
ExpiryTriggerJson triggerData = obj.Trigger.ToObject<ExpiryTriggerJson>();
The above code tries to parse back ExpiryTriggerJson structure that was first assigned to the field Trigger of TriggerJson element. This throws a runtime deserialization exception when executing the last line trying to convert to ExpiryTriggerJson which I suspect happens due to double deserialization.
My question is how do I accomplish my goal of storing static/structural json data as a string and then parse it back in a nested manner?

This might be what you want.
public dynamic Trigger {get;set;}
Then trigger will be resolved to whatever type it receives at runtime.

Related

Unable to map JSON response to a predefined class in C# / dynamic property name

I have a class which basically contains a property like this:
public class Msg
{
[JsonProperty(PropertyName = "XD1703301059485299")]
public Shipping shipping { get; set; }
}
the problem is in this part:
[JsonProperty(PropertyName = "XD1703301059485299")]
And the dynamic property name that I get from server...
This property name can be any name that server returns. In this particular case it's able to map the JSON to my class since the property names are same... But when server returns something like this:
XS12394124912841
The object is the null....
How can I resolve property name to be dynamic ? Can someone help me out?
P.S. This is the JSON response itself:
{"status":1,"msg":{"dynamic_name":{"order_sn":"12312313123123123","order_status":"0","shipping_info":[{"shipping_name":"","shipping_no":"","shipping_img":"","shipping_code":"","shipping_time":"","track_goods":""}]}},"errcode":0}
So I don't think this problem is as dynamic as it sounds. You can probably just convert to a dyanmic object and explicitly handle conversions.
Sample solution below. I inserted a few values to show conversion works as expected.
Add nuget package Newtonsoft.Json
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
namespace Serialize
{
public class Shipping
{
[JsonProperty(PropertyName = "shipping_name")]
public String Name { get; set; }
[JsonProperty(PropertyName = "shipping_img")]
public String Img { get; set; }
[JsonProperty(PropertyName = "shipping_code")]
public String Code { get; set; }
}
public class Order
{
public Shipping shipping { get; set; }
[JsonProperty(PropertyName = "order_sn")]
public string SerialNumber { get; set; }
[JsonProperty(PropertyName = "order_status")]
public string Status { get; set; }
}
class Program
{
static void Main(string[] args)
{
/*
{
"status":1,
"msg": {
"dynamic_name": {
"order_sn": "12312313123123123",
"order_status":"0",
"shipping_info": [{
"shipping_name":"name",
"shipping_no":"",
"shipping_img":"img",
"shipping_code":"code",
"shipping_time":"",
"track_goods":""
}]
}
},
"errcode":0
}
* */
var raw = "{ \"status\":1, \"msg\":{\"dynamic_name\":{\"order_sn\":\"12312313123123123\",\"order_status\":\"0\",\"shipping_info\":[{\"shipping_name\":\"name\",\"shipping_no\":\"\",\"shipping_img\":\"img\",\"shipping_code\":\"code\",\"shipping_time\":\"\",\"track_goods\":\"\"}]}},\"errcode\":0}";
var incomingOrder = new Order();
// properties on dynamic objects are evaluated at runtime
dynamic msgJson = JObject.Parse(raw);
// you'll want exception handling around all of this
var order = msgJson.msg.dynamic_name;
// accessing properties is easy (if they exist, as these do)
incomingOrder.SerialNumber = order.order_sn;
incomingOrder.Status = order.order_status;
// JObject cast might not be necessary. need to check for array elements, etc.
// but it's simple to serialize into a known type
incomingOrder.shipping = ((JObject)(order.shipping_info[0])).ToObject<Shipping>();
}
}
}
Alternatively, if the property name is given at runtime, you can dereference properties with the indexer getter
dynamic msgJson = JObject.Parse(raw);
JObject order = msgJson.msg["XS12394124912841"];
incomingOrder.SerialNumber = order["order_sn"].ToObject<string>();
incomingOrder.Status = order["order_status"].ToObject<string>();
incomingOrder.shipping = order["shipping_info"][0].ToObject<Shipping>();
You can implement something like this with the help of System.Web.Helpers
using (StreamReader r = new StreamReader("sample.json"))
{
string json = r.ReadToEnd();
dynamic data = Json.Decode(json);
Console.WriteLine(data["your_property"]);
}
Here sample.json contains your sample JSON response.

Unable to use JSON data with Newtonsoft.Json in WPF

I am retrieving data from office365 api. The response is in JSON format. I want to get data like Id, DisplayName etc. into variables but not getting the right way to do it. Following this link. I'm new to API and JSON. Will Appreciate pointers as well towards best learning links.Sample JSON below for listing sub folders of Inbox folder.
Response JSON data.
{"#odata.context":"https://outlook.office365.com/api/v1.0/$metadata#Me/Folders('Inbox')/ChildFolders","value":
[
{"#odata.id":"https://outlook.office365.com/api/v1.0/Users('sample.user#demosite.com')/Folders('AAMkADBjMGZiZGFlLTE4ZmEtNGRlOS1iMjllLTJmsdfsdfdDSFSDFDFDF=')",
"Id":"AAMkADBjMdfgdfgDFGDFGDFGdfGDFGDFGDFGGDzrACAAB4xqMmAAA=",
"DisplayName":"SampleFolder","ParentFolderId":"AAMkADBjMGZiZGFlLTE4ZmEtNGRlOS1sdsDFSDFSDFSDFSDFSDFDFDFrACAAAAAAEMAAA=","ChildFolderCount":0,"UnreadItemCount":8,"TotalItemCount":94},
{"#odata.id":"https://outlook.office365.com/api/v1.0/Users('sample.user#demosite.com')/Folders('AAMkADBjMGZiZGFlLTE4ZmEasdasdasdASDASDASDASDSADDASDASDAB4xqMnAAA=')",
"Id":"AAMkADBjMGZiZGFlLTE4ZmEtNGRlOS1iMjllLTJmOGZkNGRhZmIzNQAuAasdASDASDASDASEDASDASDxSEHjzrACAAB4xqMnAAA=",
"DisplayName":"AnotherSampleFolder","ParentFolderId":"AAMkADBjMGZiZGFlLTE4ZmEtNGRlOS1sdsDFSDFSDFSDFSDFSDFDFDFrACAAAAAAEMAAA=","ChildFolderCount":0,"UnreadItemCount":21,"TotalItemCount":75}
]
}
The C# code using to parse JSON and find the required data.
HttpResponseMessage response = httpClient.SendAsync(request).Result;
if (!response.IsSuccessStatusCode)
throw new WebException(response.StatusCode.ToString() + ": " + response.ReasonPhrase);
string content = response.Content.ReadAsStringAsync().Result;
JObject jResult = JObject.Parse(content);
if (jResult["odata.error"] != null)
throw new Exception((string)jResult["odata.error"]["message"]["value"]);
//Attempt one - using dynamic [NOT WORKING - getting NULL values in the variables]
dynamic results = JsonConvert.DeserializeObject<dynamic>(content);
var folderName = results.Id;
var folderId = results.Name;
//Attempt two - [Not working - Throwing exception -
//Object reference not set to an instance of an object.]
var folderID = (string)jResult["odata.context"]["odata.id"][0]["Id"];
First create a class for your json object
public class RootObject
{
[JsonProperty(PropertyName = "#odata.context")]
public string context { get; set; }
public List<Value> value { get; set; }
}
public class Value
{
[JsonProperty(PropertyName = "#odata.id")]
public string dataId { get; set; }
public string Id { get; set; }
public string DisplayName { get; set; }
public string ParentFolderId { get; set; }
public int ChildFolderCount { get; set; }
public int UnreadItemCount { get; set; }
public int TotalItemCount { get; set; }
}
Then Json Convert the Json string to your RootObject if your are using Newtonsoft Json then Deserilaze by using
RootObject shortiee = JsonConvert.DeserializeObject<RootObject>("Your Json String");
private List<string> GetDisplayNames(JObject content)
{
var obj = Json.Parse(content);
var values = obj["value"].ToList();
var displayNames = new List<string>();
foreach (var value in values)
{
displayNames .Add(system["DisplayName"].ToString());
}
return displayNames;
}
This would return the names, for example, and you could do this for each value you need to retrieve. However, this does not require you to serialize/deserialize the json object before using it. It works, but is most likely not best practice.
if (jResult["odata.error"] != null)
throw new Exception((string)jResult["odata.error"]["message"]["value"]);
//Attempt one - using dynamic [NOT WORKING - getting NULL values in the variables]
dynamic results = JsonConvert.DeserializeObject<dynamic>(content);
Side note: There is no key called "odata.error" in your JSON data. So you're effectively calling something which will return null.
One of the ways to deserialise JSON is to create model classes for the objects you want to process and deserialise into them directly, eg. JsonConvert.DeserializeObject<Folder>(content). As you are talking to an Office365 API, you find documentation and examples here on how they are defined.
Taken your folder response as an example, your model for a single Folder could look like this:
public class Folder
{
[JsonProperty(PropertyName = "#odata.id")]
public string OdataId { get; set; }
public string Id { get; set; }
public string DisplayName { get; set; }
public string ParentFolderId { get; set; }
public int ChildFolderCount { get; set; }
public int UnreadItemCount { get; set; }
public int TotalItemCount { get; set; }
}
Note1: in your example, you get a response with list of folders, so have to adjust this accordingly.
Note2: you can use JsonProperty to define a mapping between a JSON property/key and a C# property as shwon for #odata.id.
However, you can also use the Outlook Client Library which would make it mostly unnecessary to deal with JSON data directly (which seems preferable, unless you have a very specific reason to deal with JSON directly).

Modify a JSON string

I have a string in JSON format as follows
string jsonStr = "{"Type":1, "Id":1000,"Date":null,"Group": "Admin","Country":"India","Type":1}";
I want to modify this string so that Id attribute should always be the first. The order of attributes matters.
Is there any way I can modify this string.
I tried searching google but did not find appropriate solution.
Any help would be appreciated.
EDIT:
I also tried to deserialize object using
object yourOjbect = new JavaScriptSerializer().DeserializeObject(jsonStr);
But here also the "type" attribute comes first. I dont find any way to move the attributes within this deserialized object
It's possible. Use the JsonProperty attribute, property Order.
http://www.newtonsoft.com/json/help/html/JsonPropertyOrder.htm.
Let me know if it works.
Instead of attempting to manipulate the order of the outputted JSON and comparing strings, I would transform both JSON strings that you want to compare, into objects and then perform your comparison. You could then compare individual properties or entire objects with something like the following:
void CompareJSON()
{
string json = #"{""Type"":1, ""Id"":1000,""Date"":null,""Group"": ""Admin"",""Country"":""India"",""Type"":1}";
string jsonToCompare = "JSON TO COMPARE";
MyObject myJsonObject = JsonConvert.DeserializeObject<MyObject>(json);
MyObject myJsonObjectToCompare = JsonConvert.DeserializeObject<MyObject>(jsonToCompare);
if (myJsonObject.Id == myJsonObjectToCompare.Id)
{
// Do something
}
}
class MyObject
{
public int Id { get; set; }
public int Type { get; set; }
public DateTime? Date { get; set; }
public string Group { get; set; }
public string Country { get; set; }
}
Please note that this example is carried out using the Newtonsoft.JSON library. More information on the library can be found here.
Just make your JSON into a c# class with Id first and then serialize it again if that is what you need. You do know that you have "Type" twice in the JSON string? In this solution it will get "fixed" so you only have it once as it should be. But if your string really is with two Type this wont work since the strings will be incorrect. If they really are like that you need to do some ugly string manipulation to fix the order but i hope the first string is incorrect only here and not in your code.
private void Test() {
string json = #"{""Type"":1, ""Id"":1000,""Date"":null,""Group"": ""Admin"",""Country"":""India"",""Type"":1}";
JavaScriptSerializer jsonSerializer = new JavaScriptSerializer();
MyJsonObject myJsonObject = jsonSerializer.Deserialize<MyJsonObject>(json);
string s = jsonSerializer.Serialize(myJsonObject);
//Returns: {"Id":1000,"Type":1,"Date":null,"Group":"Admin","Country":"India"}
}
class MyJsonObject {
public int Id { get; set; }
public int Type { get; set; }
public DateTime? Date { get; set; }
public string Group { get; set; }
public string Country { get; set; }
}

Deserialize from a specific level in C#

I retrieve an information from Steam about a specific game. I provide an application url: http://store.steampowered.com/app/57690/ and get a JSON response like:
{
"57690":{ // this is dynamically got from application id above ^
"success":true
"data": { }
}
}
I have an object, SteamData
public bool success { get; set; }
public InternalData data { get; set; }
and InternalData
public string name { get; set; }
public string about_the_game { get; set; }
public string supported_languages { get; set; }
...
I tried to deserialize above json to SteamData using:
SteamData data = JsonConvert.DeserializeObject<SteamData>( thatJsonAbove );
It's not working because there exists a root which is an application id...
How to deserialize only what is inside root (in this example, what's inside "57690") ?
In case of using Newtonsoft JSON library you may try this:
JObject gameInfo = JObject.Parse(json);
SteamData data= gameInfo.SelectToken("57690", false).ToObject<SteamData>();
Since you know game's id - you can easily use this approach.

Deserialization of Json without name fields

I need to deserialize the following Json, which according to Jsonlint.com is valid, but ive not come across this before or cannot find examples of similar Json and how to deal with it?
[1,"Bellegrove / Sherwood ","76705","486","Bexleyheath Ctr",1354565507000]
My current system with like this:
Data class:
[DataContract]
public class TFLCollection
{ [DataMember(Name = "arrivals")]
public IEnumerable<TFLB> TFLB { get; set; }
}
[DataContract]
public class TFLB
{
[DataMember]
public string routeName { get; set; }
[DataMember]
public string destination { get; set; }
[DataMember]
public string estimatedWait { get; set; }
}
Deserializer:
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(TFLCollection));
using (var stream = new MemoryStream(Encoding.Unicode.GetBytes(result)))
{ var buses = (TFLCollection)serializer.ReadObject(stream);
foreach (var bus in buses.TFLBuses)
{
StopFeed _item = new StopFeed();
_item.Route = bus.routeName;
_item.Direction = bus.destination;
_item.Time = bus.estimatedWait;
listBox1.Items.Add(_item);
My exsiting deserializer works with a full Json stream and iterates through it, but in my new Json I need to deserialize, it only have 1 item, so I wont need to iterate through it.
So is it possible to deserialize my Json example using a similar method than I currently do?
I would say that you are attempting to overcomplicate things. What you have is a perfectly formed json array of strings. If I were you I would deserialize that to an .net array first, and then write a 'mapper' function to copy the values across:
public TFLB BusRouteMapper(string[] input)
{
return new TFLB {
Route = input[x],
Direction = input[y],
};
}
And so on. Of course this assumes that you know what order your json is going to be in, but if you are attempting this in the first place then you must do!

Categories

Resources