I have a part of some JSon that I am trying to map to a C# object. The rest of the JSon maps correctly to the object but only this part does not map anything and the objects values are null.
Here is the code that I am calling
var data = JObject.Parse(model.Data);
var dataModel = data.ToObject<MyModel>();
In MyModel I have this code
public class P
{
public string R { get; set; }
public IList<SavedSearch> SavedSearches { get; set; }
public class SavedSearch
{
public string A { get; set; }
public string B { get; set; }
public string CD { get; set; }
}
}
The corresponding JSon snippet is
"p": [
{
"r": "something",
"savedSearches": [
{
"saved-search": {
"#a": "blah",
"#b": "blahblah",
"#c-d": "blahblahblah"
}
}
]
}
]
R is correctly populated with the correct value, but A, B and CD are not. How must I change my C# model to fix this?
Your need to add an extra level of indirection for "saved-search", and also inform the serializer how to map the JSON property names to c# property names, since the JSON property names contain invalid characters (e.g. #) for c# properties. Thus:
[DataContract]
public class P
{
[DataMember(Name="r")]
public string R { get; set; }
[DataMember(Name="savedSearches")]
public IList<SavedSearch> SavedSearches { get; set; }
[DataContract]
public class SavedSearch
{
[DataMember(Name="saved-search")]
public SavedSearchItem SavedSearchItem { get; set; }
}
[DataContract]
public class SavedSearchItem
{
[DataMember(Name="#a")]
public string A { get; set; }
[DataMember(Name = "#b")]
public string B { get; set; }
[DataMember(Name = "#c-d")]
public string CD { get; set; }
}
}
And
public class MyModel
{
public List<P> p { get; set; }
}
When used as follows:
string json = #"
{""p"": [
{
""r"": ""something"",
""savedSearches"": [
{
""saved-search"": {
""#a"": ""blah"",
""#b"": ""blahblah"",
""#c-d"": ""blahblahblah""
}
}
]
}
]
}
";
var data = JObject.Parse(json);
var dataModel = data.ToObject<MyModel>();
Debug.WriteLine(JsonConvert.SerializeObject(dataModel, Formatting.Indented));
Produce
{
"p": [
{
"r": "something",
"savedSearches": [
{
"saved-search": {
"#a": "blah",
"#b": "blahblah",
"#c-d": "blahblahblah"
}
}
]
}
]
}
Incidentally, since you will be remapping the property names anyway, I'd suggest using more descriptive names in c# than R, A, B and CD.
Related
I have A Json file Which can be used for deserialize to Entity framework. For simplify we can assume the Json like this
{
"stat": "val0",
"results": [
{
"datasets": [
"val1",
"val2"
],
"head": "val3"
},
{
"datasets": [
"val4",
"val5"
],
"head": "val6"
}
]
}
And my Entity Classes like
[Serializable]
public class Root
{
[Key]
public int Id { get; set; }
public int stat { get; set; }
public List<Result> results { get; set; }
}
[Serializable]
public class Result
{
[Key]
public int Id { get; set; }
public List<String> _strings { get; set; }
public List<string> Strings
{
get { return _strings; }
set { _strings = value; }
}
[Required]
public string datasets
{
get { return String.Join(",", _strings); }
set { _strings = value.Split(',').ToList(); }
}
public string head{ get; set; }
public virtual root { get; set; }
}
I know Entity Framework does not support primitive types and I know problem causes from my datasets fields. that I found this way to solve String array deserialize issue here. I have tried
URL = "http://...";//Restful webservice address
WebClient client = new WebClient();
String JSON= client.DownloadString(URL);
var dsobj = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize<RootObject>(json);
But I got
System.InvalidOperationException
Then I have decided to use Newtonsoft
URL = "http://...";//Restful webservice address
WebClient client = new WebClient();
String JSON= client.DownloadString(URL);
var dsobj = JsonConvert.DeserializeObject<Root>(json);
Then I got this error
Newtonsoft.Json.JsonReaderException: 'Unexpected character encountered while parsing value: [. Path 'results[0].senses[0].definition', line 1, position...
I found this but I cant figure it out.
How can Fix these isseus. Any help appreciated.
Your json consist of two unwanted commas, try removing those
try
[Serializable]
public class Root
{
[Key]
public int Id { get; set; }
public string stat { get; set; } // changed to a string
public List<Result> results { get; set; }
}
[Serializable]
public class Result
{
[Key]
public int Id { get; set; }
public List<String> _dataSets { get; set; }
public List<string> dataSets // the JSON array will deserialize into this property
{
get { return _dataSets; }
set { _dataSets = value; }
}
[Required]
public string DatasetsAsString
{
get { return String.Join(",", _dataSets); }
set { _dataSets = value.Split(',').ToList(); }
}
public string head{ get; set; }
public virtual root { get; set; }
}
Edit: stat property has to be a string too.
I Get json data from third party in json format.
I am trying to fetch RollId from "Id" and MType from "Data" in some cases
"Data" doesn't have fields it is kind of blank.
It's working when we have "Data". In case of blank it's not working.
any idea on this? Is there a better approach of doing this?
This is the Json
string json = #"{
'root': {
'_type': '_container',
'Class': '.key.PModel',
'Elements': {
'_type': 'array<element>',
'_data': [
{
'_type': 'element',
'Class': '.key.PElement',
'Id': {
'_type': 'testId',
'Class': '.key.PModel',
'RollId': '.key.7157'
},
'Data': {
'_type': 'p_model',
'Class': '.key.Unsupported',
'MType': '.TestMType',
'Version': {
'_type': 'test__version',
'Class': '.key.TestVersion',
}
}
},
{
'_type': 'element',
'Class': '.key.PElement',
'Id': {
'_type': 'TestId',
'Class': '.key.PModel',
'RollId': '.key.11261'
},
'Data': '.ref.root.Elements.0.Data'
},
{
'_type': 'element',
'Class': '.key.PElement',
'Id': {
'_type': 'TestId',
'Class': '.key.PModel',
'RollId': '.key.7914'
},
'Data': '.ref.root.Elements.0.Data'
}
]
}
}
}";
This is the Code
public class Program
{
static void Main(string[] args)
{
//it provide json
var testCont = thirdpartyapi();
var dataList = new List<TestResponse>();
foreach (var testData in testCont.Elements())
{
var data = new TestResponse();
data.col1 = Convert.ToInt32(testData.Id().RollId());
data.col2 = testData.Data().MType().ToString();
dataList.Add(data);
}
}
public class TestResponse
{
public int col1 { get; set; }
public string col2 { get; set; }
}
}
First, try to get a valid well-formed json. You can use this code beautify tool. Then you can automatically generate C# class using json2csharp. Finally as you have C# class you can apply if statements and check if property is null.
Generated C# wrapper:
public class Id
{
public string _type { get; set; }
public string Class { get; set; }
public string RollId { get; set; }
}
public class Datum
{
public string _type { get; set; }
public string Class { get; set; }
public Id Id { get; set; }
public object Data { get; set; }
}
public class Elements
{
public string _type { get; set; }
public List<Datum> _data { get; set; }
}
public class Root
{
public string _type { get; set; }
public string Class { get; set; }
public Elements Elements { get; set; }
}
public class RootObject
{
public int encoding_version { get; set; }
public Root root { get; set; }
}
If you want to pick only few fields from complex JSON, you can consider using Cinchoo ETL - an open source library
Sample below shows how to pick RollId and MType values from your json
using (var r = new ChoJSONReader("## YOUR JSON FILE PATH ##")
.WithJSONPath("$.._data")
.WithField("RollId", jsonPath: "$..Id.RollId", fieldType: typeof(string))
.WithField("MType", jsonPath: "$..Data.MType", fieldType: typeof(string))
)
{
foreach (var rec in r)
{
Console.WriteLine((string)rec.RollId);
Console.WriteLine((string)rec.MType);
}
}
Hope it helps.
The easiest way is use the DataContractJsonSerializer
Here you can read more about it.
All in all you need to create a model which has the same possible outcome as your json.
Then you can just use the DataContractJsonSerializer to create an object of you model by using a MemoryStream.
Here you can find a nice tool to create the model from the JSON.
For example this one:
public class Id
{
public string _type { get; set; }
public string Class { get; set; }
public string RollId { get; set; }
}
public class Datum
{
public string _type { get; set; }
public string Class { get; set; }
public Id Id { get; set; }
public object Data { get; set; }
}
public class Elements
{
public string _type { get; set; }
public List<Datum> _data { get; set; }
}
public class Root
{
public string _type { get; set; }
public string Class { get; set; }
public Elements Elements { get; set; }
}
public class RootObject
{
public int encoding_version { get; set; }
public Root root { get; set; }
}
Then you use the MemoryStream and the DataContractJsonSerializer to Create an object of RootObject from that JSON.
MemoryStream stream1 = new MemoryStream();
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(RootObject));
stream1.Position = 0;
RootObject rootObject = (RootObject)ser.ReadObject(stream1);
And yes, as Adriani6 mentiones - your JSON is invalid at this point:
"Data": {
"_type": "p_model",
"Class": ".key.Unsupported",
"MType": ".TestMType",
"Version": {
"_type": "test__version",
"Class": ".key.TestVersion",
}
There is a , at the end wich is not allowed.
I have below json received from mailgun API.
{
"items": [{
"delivery-status": {
"message": null,
"code": 605,
"description": "Not delivering to previously bounced address",
"session-seconds": 0
},
"event": "failed",
"log-level": "error",
"recipient": "test#test.com"
},
{
//some other properties of above types
}]
}
Now I was trying to create a class structure for above json to auto-map the properties after deserializing.
public class test
{
public List<Item> items { get; set; }
}
public class Item
{
public string recipient { get; set; }
public string #event { get; set; }
public DeliveryStatus delivery_status { get; set; }
}
public class DeliveryStatus
{
public string description { get; set; }
}
This is how I deserialize and try to map the properties.
var resp = client.Execute(request);
var json = new JavaScriptSerializer();
var content = json.Deserialize<Dictionary<string, object>>(resp.Content);
test testContent = (test)json.Deserialize(resp.Content, typeof(test));
var eventType = testContent.items[0].#event;
var desc = testContent.items[0].delivery_status.description; //stays null
Now in the above class Item, recipient and #event gets mapped properly and since it was a keyword I was suppose to use preceding # character and it works well. But the delivery-status property from json, does not get mapped with delevery_status property in class DeliveryStatus. I have tried creating it as deliveryStatus or #deliver-status. The earlier on doesn't map again and the later one throws compile time exception. Is there anyway these things can be handled, like declaring a property with - in between? I cannot change response json as it is not getting generated from my end. Hoping for some help.
Update
Changed the class as below referring this answer, but did not help. Its null again.
public class Item
{
public string #event { get; set; }
[JsonProperty(PropertyName = "delivery-status")]
public DeliveryStatus deliveryStatus { get; set; }
}
I am not sure what the issue is at your end, but at least it works if you use this code. Make sure to include a recent version of Newtonsoft.Json in your project and you should be fine.
public class DeliveryStatus
{
public object message { get; set; }
public int code { get; set; }
public string description { get; set; }
[JsonProperty("session-seconds")]
public int session_seconds { get; set; }
}
public class Item
{
[JsonProperty("delivery-status")]
public DeliveryStatus delivery_status { get; set; }
public string #event { get; set; }
[JsonProperty("log-level")]
public string log_level { get; set; }
public string recipient { get; set; }
}
public class RootObject
{
public List<Item> items { get; set; }
}
public static void Main(string[] args)
{
string json = #"{
""items"": [{
""delivery-status"": {
""message"": null,
""code"": 605,
""description"": ""Not delivering to previously bounced address"",
""session-seconds"": 0
},
""event"": ""failed"",
""log-level"": ""error"",
""recipient"": ""test#test.com""
}]
}";
RootObject r = JsonConvert.DeserializeObject<RootObject>(json);
}
how can I deserialize json arrays with newtonsoft?
Here my json file:
{
"one": [
{
"one":"1",
"two":"2",
"three":"3"
},
{
"one":"1",
"two":"2",
"three":"3"
}
],
"two": [
{
"one":"1",
"two":"2",
"three":"3"
}
]
}
Here is my code:
myList= JsonConvert.DeserializeObject <List<MyClass>>(jsonFile);
public class MyClass
{
public string one{ get; set; }
public string two { get; set; }
public string three { get; set; }
}
Maybe I need change in some way my json file?
Your class needs to match the structure your JSON. It should look like this:
public class Foo
{
[JsonProperty("one")]
public string One { get; set; }
[JsonProperty("two")]
public string Two { get; set; }
[JsonProperty("three")]
public string Three { get; set; }
}
public class RootObject
{
[JsonProperty("one")]
public List<Foo> One { get; set; }
[JsonProperty("two")]
public List<Foo> Two { get; set; }
}
Now it will properly deserialize:
Console.WriteLine(JsonConvert.DeserializeObject<RootObject>(json));
Try This :
`public class Foo
{
[JsonProperty("one")]
public string One { get; set; }
[JsonProperty("two")]
public string Two { get; set; }
[JsonProperty("three")]
public string Three { get; set; }
}
public class RootObject
{
[JsonProperty("one")]
public List<Foo> One { get; set; }
[JsonProperty("two")]
public List<Foo> Two { get; set; }
}`
and to deserialize object use
` RootObject _rootObject = JsonConvert.DeserializeObject<RootObject>
(jObject.ToString());`
If you want to keep your class structure you have to change your json file into this:
[
{
"one":"1",
"two":"2",
"three":"3"
},
{
"one":"1",
"two":"2",
"three":"3"
}
]
With this json file style the deserialization
myList= JsonConvert.DeserializeObject <List<MyClass>>(jsonFile);
will complete properly.
Your above json file has two arrays in it. Because of that the deserialization will fail.
That is no JSON array, it is an object with two array properties.
Use http://json2csharp.com:
public class YourClass
{
public string one { get; set; }
public string two { get; set; }
public string three { get; set; }
}
public class RootObject
{
public List<YourClass> one { get; set; }
public List<YourClass> two { get; set; }
}
(Don't we still have a canonical question for this? Every day the same...)
You should deserialize it as a Dictionary<string, MyClass[]>
var test = "{ \"one\": [ { \"one\":\"1\", \"two\":\"2\", \"three\":\"3\" }, { \"one\":\"1\", \"two\":\"2\", \"three\":\"3\" } ], \"two\": [ { \"one\":\"1\", \"two\":\"2\", \"three\":\"3\" } ] }";
var testar = JsonConvert.DeserializeObject <Dictionary<string, MyClass[]>>(test);
This would work out of the box if there is more keys added like Forth, Fifth and so on.
I am working with box api, and trying to parse json object to a class.
This is the json:
{
"type":"folder",
"id":"0",
"sequence_id":null,
"etag":null,
"name":"All Files",
"created_at":null,
"modified_at":null,
"description":"",
"size":9049537,
"path_collection":
{
"total_count":0,"entries":[]
},
"created_by":
{
"type":"user","id":"","name":"","login":""
},
"modified_by":
{
"type":"user",
"id":"111",
"name":"a a",
"login":"a#gmail.com"
},
"trashed_at":null,
"purged_at":null,
"content_created_at":null,
"content_modified_at":null,
"owned_by":
{
"type":"user",
"id":"111",
"name":"a a",
"login":"a#gmail.com"
},
"shared_link":null,
"folder_upload_email":null,
"parent":null,
"item_status":"active",
"item_collection":
{
"total_count":4,
"entries":
[
{
"type":"file",
"id":"22887167395",
"sequence_id":"0",
"etag":"0",
"sha1":"883c99863eefc0f46b3d34915cc4d97a6008fabf",
"name":"13.ppt"
},
{
"type":"file",
"id":"22887169687",
"sequence_id":"0",
"etag":"0",
"sha1":"a345fd68b1c90a3678a3e746e0e5343693d8a022",
"name":"6.docx"
}
],
"offset":0,
"limit":100,
"order":
[
{
"by":"type",
"direction":"ASC"
},
{
"by":"name",
"direction":"ASC"
}
]
}
}
Basically, this is the root folder (in that case) that contains two files:
13.ppt
6.docx
I created a class:
[JsonObject(MemberSerialization.OptIn)]
public class BoxFile
{
[JsonProperty(PropertyName = "type")]
public string Type { get; internal set; }
[JsonProperty(PropertyName = "id")]
public string Id { get; internal set; }
[JsonProperty(PropertyName = "sequence_id")]
public string SequenceId { get; internal set; }
[JsonProperty(PropertyName = "etag")]
public string Etag { get; internal set; }
[JsonProperty(PropertyName = "name")]
public string Name { get; internal set; }
[JsonProperty(PropertyName = "created_at")]
public string CreatedAt { get; internal set; }
[JsonProperty(PropertyName = "modified_at")]
public string ModifiedAt { get; internal set; }
[JsonProperty(PropertyName = "description")]
public string Description { get; internal set; }
[JsonProperty(PropertyName = "size")]
public long Size { get; internal set; }
[JsonProperty(PropertyName = "item_collection")]
public IEnumerable<BoxFile> ItemCollection { get; internal set; }
}
But the "item_collection" part is not working.. it gives me an error..
How do I get a list of subfiles inside "item_collection"?
I use it by:
private T ParseJson<T>(string json) where T : class, new()
{
JObject jobject = JObject.Parse(json);
return JsonConvert.DeserializeObject<T>(jobject.ToString());
}
And:
BoxFile parsed = ParseJson<BoxFile>(json);
You are getting an error because your class structure does not match your JSON. Specifically, in the JSON, the item_collection property is an object, not a list. That JSON object has two properties, total_count and entries, the latter of which contains the actual list of files. To handle this, you need to define another class:
public class ItemCollection
{
[JsonProperty(PropertyName = "entries")]
public IEnumerable<BoxFile> Entries { get; internal set; }
}
and then change the ItemCollection property in your BoxFile class to use this new class:
[JsonProperty(PropertyName = "item_collection")]
public ItemCollection ItemCollection { get; internal set; }
You can then access the list of files like this:
BoxFile parsed = ParseJson<BoxFile>(json);
foreach (BoxFile file in parsed.ItemCollection.Entries)
{
Console.WriteLine(file.Name);
}
Here is a working demo: https://dotnetfiddle.net/DB9Coc
As an aside, you can simplify your ParseJson method to one line. There is no need to parse the JSON to an JObject, turn it back into JSON and then parse it again.
private T ParseJson<T>(string json) where T : class, new()
{
return JsonConvert.DeserializeObject<T>(json);
}