I'm currently using Newtonsoft's json library to deserialize json from a file.
JSON:
{
"name":"test",
"description":"test",
"tags":"Maps"
}
C#:
public class RootObject
{
public string name { get; set; }
public string tag { get; set; }
public string description { get; set; }
public string[] tags { get; set; }
}
The "tags" has to be an array as it is required for steam.
How can I retrieve the "tags" as an string array?
The console prints out this expection:
Newtonsoft.Json.JsonSerializationException: Error converting value "Maps" to type 'System.String[]'. Path 'tags', line 4, position 17
Your json property "tags" doesn't contain an array, but just a string. Change "Maps" to ["Maps"].
What you need is a Custom JsonConverter.
public class StringOrArrayToStringConveter<T> : JsonConverter
The meat of the solution is the override of the ReadJson which you need to return an array of T. Then you can use it like
[JsonConverter(typeof(StringOrArrayToStringConveter<string>))]
public string[] tags { get; set; }
Hope that put you in a good starting point.
EDIT:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace ConsoleApp1
{
public class RootObject
{
public string name { get; set; }
public string tag { get; set; }
public string description { get; set; }
[JsonConverter(typeof(StringOrArrayToStringConveter<string>))]
public string[] tags { get; set; }
}
class Program
{
static void Main(string[] args)
{
var data1 = "{\"name\":\"test\", \"description\":\"test\",\"tags\":\"Maps\"}";
var deserialized1 = JsonConvert.DeserializeObject<RootObject>(data1);
var data2 = "{\"name\":\"test\", \"description\":\"test\",\"tags\":[\"Maps\", \"Maps2\"]}";
var deserialized2 = JsonConvert.DeserializeObject<RootObject>(data2);
}
}
public class StringOrArrayToStringConveter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
object returnValue = new Object();
if (reader.TokenType == JsonToken.StartArray)
{
returnValue = JToken.Load(reader).ToObject<T[]>();
}
else if (reader.TokenType == JsonToken.String)
{
T instance = (T)serializer.Deserialize(reader, typeof(T));
returnValue = new List<T>() { instance }.ToArray();
}
return returnValue;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
}
I would suggest you change Tag into Dictionary e.g. public Dictionary tags { get; set; }
public class RootObject
{
public string name { get; set; }
public string tag { get; set; }
public string description { get; set; }
public string[] tags { get; set; }
public Dictionary<string, Tag> tags { get; set; }
}
public Tag
{
public string name{ get; set; }
}
Related
I have this class hierarchy :
public class ProxyBotsSnapshotLogEntryDetails : IBotsSnapshotLogEntryDetails
{
public ICollection<IBotSnapshot> Snapshots { get; set; }
}
public class ProxyBotSnapshot : IBotSnapshot
{
public string Name { get; set; }
public ICollection<IBotSnapshotItem> States { get; set; }
}
public class ProxyBotSnapshotItem : IBotSnapshotItem
{
public int Count { get; set; }
public IrcBotChannelStateEnum State { get; set; }
}
and their corresponding interfaces
public interface IBotsSnapshotLogEntryDetails
{
ICollection<IBotSnapshot> Snapshots { get; set; }
}
public interface IBotSnapshot
{
string Name { get; set; }
ICollection<IBotSnapshotItem> States { get; set; }
}
public interface IBotSnapshotItem
{
int Count { get; set; }
IrcBotChannelStateEnum State { get; set; }
}
that I would like to deserialize from JSON:
var test = JsonConvert.DeserializeObject<ProxyBotsSnapshotLogEntryDetails>(entry.DetailsSerialized);
but I get an error saying that Newtonsoft cannot convert interfaces.
I found this promising article:
https://www.c-sharpcorner.com/UploadFile/20c06b/deserializing-interface-properties-with-json-net/
but am not sure how to use the attribute, since in my case, the property is a list of interface.
The converter provided in the article works super nicely, I was just missing the syntax to use it on a collection property. Here is the code with the converter and the working attributes:
// From the article
public class ConcreteConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType) => true;
public override object ReadJson(JsonReader reader,
Type objectType, object existingValue, JsonSerializer serializer)
{
return serializer.Deserialize<T>(reader);
}
public override void WriteJson(JsonWriter writer,
object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
public class ProxyBotsSnapshotLogEntryDetails : IBotsSnapshotLogEntryDetails
{
[JsonProperty(ItemConverterType = typeof(ConcreteConverter<ProxyBotSnapshot>))]
public ICollection<IBotSnapshot> Snapshots { get; set; }
}
public class ProxyBotSnapshot : IBotSnapshot
{
public string Name { get; set; }
[JsonProperty(ItemConverterType = typeof(ConcreteConverter<ProxyBotSnapshotItem>))]
public ICollection<IBotSnapshotItem> States { get; set; }
}
public class ProxyBotSnapshotItem : IBotSnapshotItem
{
public int Count { get; set; }
public IrcBotChannelStateEnum State { get; set; }
}
Maybe it works if you add the following settings to the Serializer and Deserializer methods:
new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Auto}
For example:
var test = JsonConvert.DeserializeObject<ProxyBotsSnapshotLogEntryDetails>(entry.DetailsSerialized,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
I'm using Newtonsoft.Json to work with some JSON data that is being returned to me. Depending on what I request I can either get back something that looks like:
{
"TotalRecords":2,
"Result":
[
{
"Id":24379,
"AccountName":"foo"
},
{
"Id":37209,
"AccountName":"bar"
}
],
"ResponseCode":0,
"Status":"OK",
"Error":"None"
}
or
{
"Result":
{
"Id":24379,
"AccountName":"foo"
},
"ResponseCode":0,
"Status":"OK",
"Error":"None"
}
So sometimes "Result" is an array of Results or "Result" could be a single response.
I've tried using the answer from How to handle both a single item and an array for the same property using JSON.net but I still get errors.
In particular I'm getting a
Newtonsoft.json.jsonSerializationException: 'Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List'...
Custom converter looks like:
public class SingleOrArrayConverter<T> : JsonConverter
{
public override bool CanConvert(Type objecType)
{
return (objecType == typeof(List<T>));
}
public override object ReadJson(JsonReader reader, Type objecType, object existingValue,
JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token.Type == JTokenType.Array)
{
return token.ToObject<List<T>>();
}
return new List<T> { token.ToObject<T>() };
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
My response class(es) look like
public class TestResponse
{
[JsonProperty("Result")]
[JsonConverter(typeof(SingleOrArrayConverter<string>))]
public List<DeserializedResult> Result { get; set; }
}
public class DeserializedResult
{
public string Id { get; set; }
public string AccountName { get; set; }
}
And finally my request looks like
List<TestResponse> list = JsonConvert.DeserializeObject<List<TestResponse>>(response.Content);
Your code is fine, it just needs a few type tweaks.
This line
List<TestResponse> list = JsonConvert.DeserializeObject<List<TestResponse>>(response.Content);
needs to be like this, because your response is an object, not a List.
TestResponse list = JsonConvert.DeserializeObject<TestResponse>(response);
Then your custom deserializer attribute:
[JsonConverter(typeof(SingleOrArrayConverter<string>))]
needs to become:
[JsonConverter(typeof(SingleOrArrayConverter<DeserializedResult>))]
because your Result object is not a string or an array of strings, it's either an array of DeserializedResults or a DeserializedResult.
I think, that there is no way to understend which type of response do you have due desirialization. Thats why i propose to check manualy type of response:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace TestConsoleApp
{
public class Class1
{
public class Result
{
public int Id { get; set; }
public string AccountName { get; set; }
}
public class ModelWithArray
{
public int TotalRecords { get; set; }
public List<Result> Result { get; set; }
public int ResponseCode { get; set; }
public string Status { get; set; }
public string Error { get; set; }
}
public class Result2
{
public int Id { get; set; }
public string AccountName { get; set; }
}
public class ModelWithoutArray
{
public Result2 Result { get; set; }
public int ResponseCode { get; set; }
public string Status { get; set; }
public string Error { get; set; }
}
public static void Main(params string[] args)
{
//string json = "{\"TotalRecords\":2,\"Result\":[{\"Id\":24379,\"AccountName\":\"foo\"},{\"Id\":37209,\"AccountName\":\"bar\"}], \"ResponseCode\":0,\"Status\":\"OK\",\"Error\":\"None\"}";
string json = "{\"Result\":{\"Id\":24379,\"AccountName\":\"foo\"},\"ResponseCode\":0,\"Status\":\"OK\",\"Error\":\"None\"}";
if (checkIsArray(json))
{
ModelWithArray data = JsonConver.DeserializeObject<ModelWithArray >(json);
}else
{
ModelWithoutArray data = JsonConver.DeserializeObject<ModelWithoutArray>(json);
}
}
static bool checkIsArray(string json)
{
Dictionary<string, object> desData = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
if (desData["Result"].GetType().Name.Contains("Array"))
{
return true;
}
else
{
return false;
}
}
}
}
I am getting JSon from a third party API which does not match my classes.
Some JSon properties are not be be converted others has different names, etc.
How can I define a custom conversion from the JSON to my C# object.
These are the objects I have in C#:
public class PropertyList {
public Int32 Page { get; set; }
public String[] ErrorMessages { get; set; }
public List<Property> Properties { get; set; }
}
public class Property {
public String Name { get; set; }
public String Reference { get; set; }
public String Region { get; set; }
public IList<String> Features { get; set; }
public String Id { get; set; }
public Double Price { get; set; }
public IList<String> ImagesUrls { get; set; }
}
And this is the JSon data which I want to convert from:
{
"page" : 0,
"errorMessages" : [ ],
"listings" : [
{
"data" : {
"name" : "The name",
"reference__c" : "ref1234",
"region__c" : "London",
"features__c" : "Garage;Garden;",
"id" : "id1234",
"price_pb__c" : 700000,
},
"media" : {
"images" : [
{
"title" : "image1",
"url" : "http://www.domain.com/image1"
},
{
"title" : "image2",
"url" : "http://www.domain.com/image2"
}
]
}
}
{
NOTE: Other items
}
]
}
How should I do this?
UPDATE 1
Using Thiago suggestion I was able to parse Page and ErrorMessages but not Properties. So I create the following converter:
public class PropertyResultConverter : JsonConverter {
public override bool CanConvert(Type objectType) {
return (objectType == typeof(PropertyResult));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
JObject jo = JObject.Load(reader);
PropertyResult propertyResult = jo.ToObject<PropertyResult>();
propertyResult.Properties = jo.SelectToken("listings.data").ToObject<List<Property>>();
return propertyResult;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
throw new NotImplementedException();
}
}
But when I try this I get the following error:
An exception of type 'System.NullReferenceException' occurred
On:
propertyResult.Properties = jo.SelectToken("listings.data").ToObject<List<Property>>();
Any idea why this happens?
Miguel, have you tried http://www.newtonsoft.com/json ?
You can do the mappings of your code using C# attributes, like the snippets below.
public class PropertyList {
[JsonProperty("page")]
public Int32 Page { get; set; }
[JsonProperty("errorMessages")]
public String[] ErrorMessages { get; set; }
[JsonProperty("listings")]
public List<Property> Properties { get; set; }
}
public class Property {
[JsonProperty("name")]
public String Name { get; set; }
[JsonProperty("reference__c")]
public String Reference { get; set; }
[JsonProperty("region__c")]
public String Region { get; set; }
[JsonProperty("features__c")]
public IList<String> Features { get; set; }
[JsonProperty("id")]
public String Id { get; set; }
[JsonProperty("price_pb__c")]
public Double Price { get; set; }
[JsonProperty("media")]
public IList<String> ImagesUrls { get; set; }
}
I'm not sure if IList will work as you are expecting. I believe you will need to adapt it to Dictionary.
You can take a look on the API:
http://www.newtonsoft.com/json/help/html/SerializationAttributes.htm
UPDATE
Try to parse 'data' using the snippet below:
JObject jsonObject = JObject.Parse(json);
IList<JToken> results = jsonObject["listings"]["data"].Children().ToList();
IList<Property> processedList = new List<Property>();
foreach (JToken item in results)
{
Property propertyItem = JsonConvert.DeserializeObject<Property>(item.ToString());
processedList.Add(propertyItem);
}
I ran that snippet and it worked. You can explore the code that I generated on my github:
https://github.com/thiagoavelino/VisualStudio_C/blob/master/VisualStudio_C/StackOverFlow/ParsingJason/ParsingJson.cs
I'm trying to deserialize an object with Json.net. I was able to do it successfully but its more of a hack so I'm looking for a better/proper way to do so.
{
"page":"admin",
"context":"id",
"records":{
"rCount":6,
"1":[
{
"name":"Romeo",
"dob":"01\/01\/1970"
},
{
"name":"Harry",
"dob":"10\/10\/2012"
},
{
"name":"Lee",
"dob":"17\/10\/2012"
}],
"2":[
{
"name":"Mark",
"dob":"01\/01\/1970"
},
{
"name":"Jack",
"dob":"10\/10\/2012"
},
{
"name":"Json",
"dob":"17\/10\/2012"
}],
}}
this is the json string the issue is with the records object. if it doesn't have that rCount variable then it can be deserialized as a Dictionary but because of the rCount variable it can't be deserialized properly as a dictionary. What should be the proper way of deserialzing this object.
Here is my Solution:
class Program
{
static void Main(string[] args)
{
var recordFile = JsonConvert.DeserializeObject<RecordFile>(Properties.Resources.data);
}
public class RecordFile
{
public string Page { get; set; }
public string Context { get; set; }
public Records Records { get; set; }
}
public class Records
{
public int RCount { get; set; }
[JsonExtensionData]
private Dictionary<string, object> _reocordList;
public List<Record[]> RecordList
{
get
{
if (_reocordList != null && _reocordList.Count > 0)
{
return _reocordList.Values.Select(record => JsonConvert.DeserializeObject<Record[]>(record.ToString())).ToList();
}
return new List<Record[]>();
}
}
}
public class Record
{
public string Name { get; set; }
public string Dob { get; set; }
}
}
You can use jObject to manually parse JSON:
public class RecordFile
{
public string Page { get; set; }
public string Context { get; set; }
public Records Records { get; set; }
}
public class Records
{
public int RCount { get; set; }
public IDictionary<string, List<Record>> RecordsDictionary { get; set; }
}
public class Record
{
public string Name { get; set; }
public string Dob { get; set; }
}
Then:
var jObject = JObject.Parse(\* your json *\);
var recordFile = new RecordFile
{
Page = jObject.Value<string>("page"),
Context = jObject.Value<string>("context"),
Records = new Records
{
RCount = jObject["records"].Value<int>("rCount"),
RecordsDictionary =
jObject["records"].Children<JProperty>()
.Where(prop => prop.Name != "rCount")
.ToDictionary(prop => prop.Name),
prop =>
prop.Value.ToObject<List<Record>>())
}
};
Of course it can be easily to handle cases, when property is not present.
I'm assuming you want to get rid of the Records class completely, ending up with something like this:
public class RecordFile
{
public string Page { get; set; }
public string Context { get; set; }
public Dictionary<string, Record[]> Records { get; set; }
}
public class Record
{
public string Name { get; set; }
public string Dob { get; set; }
}
Since you don't care at all about the records.rCount property from the JSON, you could specify a new error handler to simply ignore the property:
var recordFile = JsonConvert.DeserializeObject<RecordFile>(
jsonString,
new JsonSerializerSettings
{
Error = (sender, args) =>
{
if (args.ErrorContext.Path == "records.rCount")
{
// Ignore the error
args.ErrorContext.Handled = true;
}
}
});
Now when an error is encountered with the property in question, the deserializer will just skip over it. Another option would be to write a custom converter, but that feels like overkill just to ignore one property.
Example: https://dotnetfiddle.net/3svPqk
The other answers posted so far should both work. For completeness, I'll show how you can use a custom JsonConverter to solve this and also simplify your model a bit.
Here is the code for the converter:
class RecordFileConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(RecordFile));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
RecordFile rf = new RecordFile();
rf.Page = (string)jo["page"];
rf.Context = (string)jo["context"];
JObject records = (JObject)jo["records"];
rf.RecordCount = (int)records["rCount"];
rf.Records = records.Properties()
.Where(p => p.Name != "rCount")
.Select(p => p.Value.ToObject<Record[]>())
.ToList();
return rf;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To work with the converter, change your model classes as shown below. (Notice that the RecordFile class has a JsonConverter attribute to link the custom RecordFileConverter to it. Also notice the Records class is deleted, while the RCount and RecordList properties have been moved up to the RecordFile class and renamed.)
[JsonConverter(typeof(RecordFileConverter))]
public class RecordFile
{
public string Page { get; set; }
public string Context { get; set; }
public int RecordCount { get; set; }
public List<Record[]> Records { get; set; }
}
public class Record
{
public string Name { get; set; }
public string Dob { get; set; }
}
Then, deserialize as normal:
var recordFile = JsonConvert.DeserializeObject<RecordFile>(json);
Demo: https://dotnetfiddle.net/jz3zUT
I have problem when trying to deserialize a json from internet.
But I don't know how to deserialize difference types in one array correctly.
How to do it correctly?
Sorry for my poor english.
Here is json:
{
"timestamp":"2012-06-19T08:00:49Z",
"items":[
{
"type":"text",
"content":"etc"
},
{
"type":"video",
"url":"etc"
}
...
]
}
My code:
public interface IPost
{
string PostType { get; set; }
}
public class TextPost : IPost
{
[JsonProperty("type")]
public string PostType { get; set; }
[JsonProperty("content")]
public string Content { get; set; }
}
public class VideoPost : IPost
{
[JsonProperty("type")]
public string PostType { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
}
public class ResponseData
{
[JsonProperty("timestamp")]
public string Timestamp { get; set; }
[JsonProperty("items")]
public List<IPost> Items { get; set; }
}
I figured out a way to parse it. By using JsonConverter.
Here is my Converter class:
public class PostsConverter : JsonConverter
{
// Create an instance
protected IPost Create(JObject jObject)
{
if (PostType("Text", jObject)) return new Text();
if (PostType("Video", jObject)) return new Video();
throw new Exception("Error");
}
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var list = new List<IPost>();
var arr = JArray.Load(reader);
foreach (var item in arr)
{
var obj = JObject.Load(item.CreateReader());
// Create target object based on JObject
var post = Create(obj);
// Populate the object properties
serializer.Populate(obj.CreateReader(), post);
list.Add(post);
}
return list;
}
private bool PostType(string type, JObject jObject)
{
return jObject["PostType"] != null && jObject["PostType"].Value<string>() == type;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// I don't need serialize object
throw new NotImplementedException();
}
}
Response class:
public class ResponseData
{
[JsonProperty("timestamp")]
public string Timestamp { get; set; }
[JsonConverter(typeof(PostsConverter))]
[JsonProperty("items")]
public List<IPost> Items { get; set; }
}
Now I can parse it:
var myData = JsonConvert.DeserializeObject<ResponseData>(json);