I use NSwagStudio to create a C# client from an OpenAPI document.
So far everything works, but I noticed a problem to which I haven't found a solution yet.
The problem is that NSwagStudio creates duplicate classes for a recurring structure.
The following JSON as an example:
{
"components": {
"schemas": {
"Person": {
"type": "object",
"properties": {
"Id": {
"type": "string"
},
"Name": {
"type": "string"
},
"First Name": {
"type": "string"
},
"Lastname": {
"type": "string"
},
"attributes": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
}
}
}
},
"description": "Account Object"
},
"Address": {
"type": "object",
"properties": {
"Id": {
"type": "string"
},
"Street": {
"type": "string"
},
"Post Code": {
"type": "string"
},
"City": {
"type": "string"
},
"Country": {
"type": "string"
},
"attributes": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
}
}
}
},
"description": "Address Object"
}
}
}
}
Here are 2 simple objects described, Person and Address.
Both objects have this structure in common:
"attributes" : {
"type" : "object",
"properties" : {
"type" : {
"type" : "string"
},
"url" : {
"type" : "string"
}
}
}
In C#, the result looks like this:
public partial class Attributes
{
[Newtonsoft.Json.JsonProperty("type", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public string Type { get; set; }
[Newtonsoft.Json.JsonProperty("url", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public string Url { get; set; }
private System.Collections.Generic.IDictionary<string, object> _additionalProperties = new System.Collections.Generic.Dictionary<string, object>();
[Newtonsoft.Json.JsonExtensionData]
public System.Collections.Generic.IDictionary<string, object> AdditionalProperties
{
get { return _additionalProperties; }
set { _additionalProperties = value; }
}
public string ToJson()
{
return Newtonsoft.Json.JsonConvert.SerializeObject(this, new Newtonsoft.Json.JsonSerializerSettings());
}
public static Attributes FromJson(string data)
{
return Newtonsoft.Json.JsonConvert.DeserializeObject<Attributes2>(data, new Newtonsoft.Json.JsonSerializerSettings());
}
}
public partial class Attributes2
{
[Newtonsoft.Json.JsonProperty("type", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public string Type { get; set; }
[Newtonsoft.Json.JsonProperty("url", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public string Url { get; set; }
private System.Collections.Generic.IDictionary<string, object> _additionalProperties = new System.Collections.Generic.Dictionary<string, object>();
[Newtonsoft.Json.JsonExtensionData]
public System.Collections.Generic.IDictionary<string, object> AdditionalProperties
{
get { return _additionalProperties; }
set { _additionalProperties = value; }
}
public string ToJson()
{
return Newtonsoft.Json.JsonConvert.SerializeObject(this, new Newtonsoft.Json.JsonSerializerSettings());
}
public static Attributes2 FromJson(string data)
{
return Newtonsoft.Json.JsonConvert.DeserializeObject<Attributes2>(data, new Newtonsoft.Json.JsonSerializerSettings());
}
}
These attribute classes themselves are just helper classes for other classes.
In total over 700 of them were created.
Is it not possible to create only one class for this?
The path part of the OpenAPI document contains $ref references to the "main" object
{
"/Person": {
"description": "",
"get": {
"responses": {
"200": {
"description": "Status Code 200",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Person"
}
}
}
},
}
}
}
}
The C# class Person, which is specified by $ref (see above)
Here the getter/setter with Attributes372 class.
public partial class Person
{
[Newtonsoft.Json.JsonProperty("Id", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public string Id { get; set; }
[Newtonsoft.Json.JsonProperty("Name", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public string Name{ get; set; }
[Newtonsoft.Json.JsonProperty("LastName", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public string LastName { get; set; }
[Newtonsoft.Json.JsonProperty("FirstName", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public string FirstName { get; set; }
[Newtonsoft.Json.JsonProperty("attributes", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public Attributes372 Attributes { get; set; }
private System.Collections.Generic.IDictionary<string, object> _additionalProperties = new System.Collections.Generic.Dictionary<string, object>();
[Newtonsoft.Json.JsonExtensionData]
public System.Collections.Generic.IDictionary<string, object> AdditionalProperties
{
get { return _additionalProperties; }
set { _additionalProperties = value; }
}
}
``
Related
{
"name": "Not Okay Bears Solana #1",
"image": "ipfs://QmV7QPwmfc6iFXw2anb9oPZbkFR75zrtw6exd8LherHgvU/1.png",
"attributes": [
{
"trait_type": "Background",
"value": "Amethyst"
},
{
"trait_type": "Fur",
"value": "Warm Ivory"
},
{
"trait_type": "Mouth",
"value": "Clean Smile"
},
{
"trait_type": "Eyes",
"value": "Not Okay"
},
{
"trait_type": "Hat",
"value": "Bucket Hat"
},
{
"trait_type": "Clothes",
"value": "Plaid Jacket"
},
{
"trait_type": "Eyewear",
"value": "Plastic Glasses"
}
],
"description": "Not Okay Bears Solana is an NFT project for mental health awareness. 10k collection on the Polygon blockchain. We are not okay."
}
I need to add an object to attributes.
How to do this?
My JSON classes:
public class Attribute
{
public string trait_type { get; set; }
public string value { get; set; }
}
public class Root
{
public string name { get; set; }
public string image { get; set; }
public List<Attribute> attributes { get; set; }
public string description { get; set; }
}
try this, in this case you don't need any classes
var jsonObject = JObject.Parse(json);
JObject obj = new JObject();
obj.Add("trait_type", "type");
obj.Add("value", "value");
((JArray)jsonObject["attributes"]).Add(obj);
var newJson=jsonObject.ToString();
but if you need the data not a json , you can use this code
Root data = JsonConvert.DeserializeObject<Root>(json);
data.attributes.Add(new Attribute { trait_type="type", value="value"});
I would like my output JSON to contain a simple array shown below
{
"attributes":[
{
"trait_type": "Background",
"value": "Green"
},
{
"trait_type": "Body",
"value": "Body_1"
},
{
"trait_type": "Outfit",
"value": "Beach_Singlet"
},
{
"display_type":"date",
"trait_type":"birthday",
"value":869270400
}
]
}
Notice how the last item is different from the previous items in the array. The variable named "value" is also an integer as compared to the previous entries as strings.
How do I go about in order to be able to output my JSON as shown above? I have tried creating a class that can store all the information, but I cannot reuse the name "value" for both an int and string declaration, and also do not wish to show the variables if their value is null
(Example shown below)
{
"attributes": [
{
"display_type": "",
"trait_type": "Background",
"value": "Green"
},
{
"display_type": "",
"trait_type": "Body",
"value": "Body_1"
},
{
"display_type": "",
"trait_type": "Outfit",
"value": "Beach_Singlet"
},
{
"display_type": "date",
"trait_type": "birthday",
"value": 869270400
}
]
}
You can use object type.
using Newtonsoft.Json;
var list = new AttributeList
{
attributes = new []{
new Attribute
{
trait_type = "Background",
value = "green"
},
new Attribute
{
display_type = "date",
trait_type = "birthday",
value = 869270400
}
}
};
var json = JsonConvert.SerializeObject(list, Formatting.Indented);
Console.WriteLine(json);
public class Attribute
{
public object value { get; set; }
public string trait_type { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string display_type { get; set; }
}
public class AttributeList
{
public Attribute[] attributes { get; set; }
}
Output:
{
"attributes": [
{
"value": "green",
"trait_type": "Background"
},
{
"value": 869270400,
"trait_type": "birthday",
"display_type": "date"
}
]
}
try this
var attributes=new List<Attribute>{
new AttributeString{
trait_type="Background",
value="green"
},
new AttributeInt{
display_type ="date",
trait_type="birthday",
value=869270400
}
};
var jsonSerializerSettings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Objects,
NullValueHandling=NullValueHandling.Ignore,
Formatting=Newtonsoft.Json.Formatting.Indented
};
var json = JsonConvert.SerializeObject(attributes,jsonSerializerSettings);
classes
public class Attribute
{
public string trait_type { get; set; }
public string display_type { get; set; }
}
public class AttributeString:Attribute
{
public string value { get; set; }
}
public class AttributeInt:Attribute
{
public int value { get; set; }
}
public class AttributeList
{
public List<Attribute> attributes { get; set; }
}
I write an app that gets IMDb movie information by scraping movie page source. Some of the movie data in page source are in JSON format with movie schema from "Schema.org".
{
"#context": "http://schema.org",
"#type": "Movie",
"url": "/title/tt7131622/",
"name": "Once Upon a Time... in Hollywood",
"genre": [
"Comedy",
"Drama"
],
"actor": [
{
"#type": "Person",
"url": "/name/nm0000138/",
"name": "Leonardo DiCaprio"
},
{
"#type": "Person",
"url": "/name/nm0000093/",
"name": "Brad Pitt"
},
{
"#type": "Person",
"url": "/name/nm3053338/",
"name": "Margot Robbie"
},
{
"#type": "Person",
"url": "/name/nm0386472/",
"name": "Emile Hirsch"
}
],
"director": {
"#type": "Person",
"url": "/name/nm0000233/",
"name": "Quentin Tarantino"
},
"creator": [
{
"#type": "Person",
"url": "/name/nm0000233/",
"name": "Quentin Tarantino"
},
{
"#type": "Organization",
"url": "/company/co0050868/"
},
{
"#type": "Organization",
"url": "/company/co0452101/"
},
{
"#type": "Organization",
"url": "/company/co0159772/"
}
}
I made a "Movie" class to deserialize the JSON object. There is a property Person class with the name "Director".
internal class ImdbJsonMovie
{
public string Url { get; set; }
public string Name { get; set; }
public string Image { get; set; }
public List<string> Genre { get; set; }
public List<ImdbJsonPerson> Actor { get; set; }
public ImdbJsonPerson Director { get; set; }
//public string[] Creator { get; set; }
}
It's OK. But the problem is some movies such as "The Matrix" have more than one director.
{
"#context": "http://schema.org",
"#type": "Movie",
"url": "/title/tt0133093/",
"name": "The Matrix",
"genre": [
"Action",
"Sci-Fi"
],
"actor": [
{
"#type": "Person",
"url": "/name/nm0000206/",
"name": "Keanu Reeves"
},
{
"#type": "Person",
"url": "/name/nm0000401/",
"name": "Laurence Fishburne"
},
{
"#type": "Person",
"url": "/name/nm0005251/",
"name": "Carrie-Anne Moss"
},
{
"#type": "Person",
"url": "/name/nm0915989/",
"name": "Hugo Weaving"
}
],
"director": [
{
"#type": "Person",
"url": "/name/nm0905154/",
"name": "Lana Wachowski"
},
{
"#type": "Person",
"url": "/name/nm0905152/",
"name": "Lilly Wachowski"
}
],
"creator": [
{
"#type": "Person",
"url": "/name/nm0905152/",
"name": "Lilly Wachowski"
},
{
"#type": "Person",
"url": "/name/nm0905154/",
"name": "Lana Wachowski"
},
{
"#type": "Organization",
"url": "/company/co0002663/"
},
{
"#type": "Organization",
"url": "/company/co0108864/"
},
{
"#type": "Organization",
"url": "/company/co0060075/"
},
{
"#type": "Organization",
"url": "/company/co0019968/"
},
{
"#type": "Organization",
"url": "/company/co0070636/"
}
}
So it must be List<Person>.
internal class ImdbJsonMovie
{
public string Url { get; set; }
public string Name { get; set; }
public string Image { get; set; }
public List<string> Genre { get; set; }
public List<ImdbJsonPerson> Actor { get; set; }
public List<ImdbJsonPerson> Director { get; set; }
//public string[] Creator { get; set; }
}
Another problem is how to deserialize creator property that is made by the Person class and Organization class.
So the question is "How to deserialize this complex JSON object?"
Thank you
Did you try: https://app.quicktype.io/?l=csharp? It can generate model in C# for you, which is very good begining for further changes (if the Schema has to be different according to different json responses)
I did enter your JSON and the model created is following:
namespace QuickType
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class Movies
{
[JsonProperty("#context")]
public Uri Context { get; set; }
[JsonProperty("#type")]
public string Type { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("genre")]
public List<string> Genre { get; set; }
[JsonProperty("actor")]
public List<Tor> Actor { get; set; }
[JsonProperty("director")]
public List<Tor> Director { get; set; }
[JsonProperty("creator")]
public List<Tor> Creator { get; set; }
}
public partial class Tor
{
[JsonProperty("#type")]
public TypeEnum Type { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
public string Name { get; set; }
}
public enum TypeEnum { Organization, Person };
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
TypeEnumConverter.Singleton,
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
internal class TypeEnumConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(TypeEnum) || t == typeof(TypeEnum?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
switch (value)
{
case "Organization":
return TypeEnum.Organization;
case "Person":
return TypeEnum.Person;
}
throw new Exception("Cannot unmarshal type TypeEnum");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (TypeEnum)untypedValue;
switch (value)
{
case TypeEnum.Organization:
serializer.Serialize(writer, "Organization");
return;
case TypeEnum.Person:
serializer.Serialize(writer, "Person");
return;
}
throw new Exception("Cannot marshal type TypeEnum");
}
public static readonly TypeEnumConverter Singleton = new TypeEnumConverter();
}
}
[Update]
As for problems with sometime single, sometime array thing --> look here: How to handle both a single item and an array for the same property using JSON.net
Thank you #Piotr. It completely worked. because your first part of the answer was not correct for me, I rewrite your response as an answer.
as you said the correct answer was in this link.
https://stackoverflow.com/questions/18994685/how-to-handle-both-a-single-item-and-an-array-for-the-same-property-using-json-n
So I made this class.
class JsonConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(List<T>);
}
public override object ReadJson(JsonReader reader, Type objectType, 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();
}
}
and changed my Movie Class to this.
internal class ImdbJsonMovie
{
public string Url { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("image")]
public string Image { get; set; }
[JsonProperty("genre")]
[JsonConverter(typeof(JsonConverter<string>))]
public List<string> Genre { get; set; }
[JsonProperty("contentRating")]
public string ContentRating { get; set; }
[JsonProperty("actor")]
[JsonConverter(typeof(JsonConverter<ImdbJsonTypeEnum>))]
public List<ImdbJsonTypeEnum> Actor { get; set; }
[JsonProperty("director")]
[JsonConverter(typeof(JsonConverter<ImdbJsonTypeEnum>))]
public List<ImdbJsonTypeEnum> Director { get; set; }
[JsonProperty("creator")]
[JsonConverter(typeof(JsonConverter<ImdbJsonTypeEnum>))]
public List<ImdbJsonTypeEnum> Creator { get; set; }
}
and this Enum
public class ImdbJsonTypeEnum
{
[JsonProperty("#type")]
public TypeEnum Type { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
public enum TypeEnum
{
Organization,
Person
};
}
It worked for one director and multi director movies.
Thank you
I have following JSON that I'm writing object model to deserialize into:
{
"company_webhooks": [
{
"company_webhook": {
"id": 42,
"url": "https://keeptruckin.com/callbacktest/842b02",
"secret": "fe8b75de0a4e5898f0011faeb8c93654",
"format": "json",
"actions": [
"vehicle_location_received",
"vehicle_location_updated"
],
"enabled": false
}
},
{
"company_webhook": {
"id": 43,
"url": "https://keeptruckin.com/callbacktest/a6a783",
"secret": "66a7368063cb21887f546c7af91be59c",
"format": "json",
"actions": [
"vehicle_location_received",
"vehicle_location_updated"
],
"enabled": false
}
},
{
"company_webhook": {
"id": 44,
"url": "https://keeptruckin.com/callbacktest/53a52c",
"secret": "4451dc96513b3a67107466dd2c4d9589",
"format": "json",
"actions": [
"vehicle_location_received",
"vehicle_location_updated"
],
"enabled": false
}
},
{
"company_webhook": {
"id": 45,
"url": "https://keeptruckin.com/callbacktest/6fb337",
"secret": "4177fbd88c30faaee03a4362648bd663",
"format": "json",
"actions": [
"vehicle_location_received",
"vehicle_location_updated"
],
"enabled": false
}
},
{
"company_webhook": {
"id": 46,
"url": "https://keeptruckin.com/callbacktest/8cd6da",
"secret": "6e41817a048b009435e5102fca17db55",
"format": "json",
"actions": [
"vehicle_location_received",
"vehicle_location_updated"
],
"enabled": false
}
}
],
"pagination": {
"per_page": 25,
"page_no": 1,
"total": 5
}
}
Here is what I have:
[DataContract]
public class KeepTruckinResponse
{
[DataMember(Name = "company_webhooks", EmitDefaultValue = false)]
public KeepTruckinCompanyWebHook[] WebHooks { get; set; }
[DataMember(Name = "pagination", EmitDefaultValue = false)]
public KeepTruckinPagination Pagination { get; set; }
public string RawJSON { get; set; }
}
[DataContract]
public class KeepTruckinPagination
{
[DataMember(Name = "per_page", EmitDefaultValue = false)]
public int PerPage { get; set; }
[DataMember(Name = "page_no", EmitDefaultValue = false)]
public int PageNumber { get; set; }
[DataMember(Name = "total", EmitDefaultValue = false)]
public int Total { get; set; }
}
[DataContract(Name = "company_webhook")]
public class KeepTruckinCompanyWebHook
{
[DataMember(Name = "id", EmitDefaultValue = false)]
public int Id { get; set; }
[DataMember(Name = "url", EmitDefaultValue = false)]
public string Url { get; set; }
}
Obviously, when I deserialize JSON I don't get KeepTruckinCompanyWebHook properties because the way they send collection is "nested". I almost have to create another object inside KeepTruckinCompanyWebHook with properties. But I'd like to keep my object model as it is. Is it possible with .NET serializer?
We use DataContractJsonSerializer like so:
var ser = new DataContractJsonSerializer(typeof(KeepTruckinResponse));
response = ser.ReadObject(ms) as KeepTruckinResponse;
At this point we don't want to use NewtonSoft.Json
Yes, this is possible, but you will need some custom code to do it.
It's a little ugly, but you can create a custom IDataContractSurrogate class to deserialize each JSON object inside the company_webhooks array into a Dictionary<string, Dictionary<string, object>>, and then copy the values from the nested dictionary structure into an instance of your KeepTruckinCompanyWebHook class. Here's the code you would need for the surrogate:
class MyDataContractSurrogate : IDataContractSurrogate
{
public Type GetDataContractType(Type type)
{
if (type == typeof(KeepTruckinCompanyWebHook))
{
return typeof(Dictionary<string, Dictionary<string, object>>);
}
return type;
}
public object GetDeserializedObject(object obj, Type targetType)
{
if (obj.GetType() == typeof(Dictionary<string, Dictionary<string, object>>) &&
targetType == typeof(KeepTruckinCompanyWebHook))
{
var webHook = new KeepTruckinCompanyWebHook();
var outerDict = (Dictionary<string, Dictionary<string, object>>)obj;
var innerDict = outerDict["company_webhook"];
foreach (PropertyInfo prop in GetDataMemberProperties(typeof(KeepTruckinCompanyWebHook)))
{
DataMemberAttribute att = prop.GetCustomAttribute<DataMemberAttribute>();
object value;
if (innerDict.TryGetValue(att.Name, out value))
{
prop.SetValue(webHook, value);
}
}
return webHook;
}
return obj;
}
public object GetObjectToSerialize(object obj, Type targetType)
{
if (obj.GetType() == typeof(KeepTruckinCompanyWebHook) &&
targetType == typeof(Dictionary<string, Dictionary<string, object>>))
{
var webHook = (KeepTruckinCompanyWebHook)obj;
var outerDict = new Dictionary<string, Dictionary<string, object>>();
var innerDict = new Dictionary<string, object>();
outerDict.Add("company_webhook", innerDict);
foreach (PropertyInfo prop in GetDataMemberProperties(typeof(KeepTruckinCompanyWebHook)))
{
DataMemberAttribute att = prop.GetCustomAttribute<DataMemberAttribute>();
innerDict.Add(att.Name, prop.GetValue(webHook));
}
return outerDict;
}
return obj;
}
private IEnumerable<PropertyInfo> GetDataMemberProperties(Type type)
{
return type.GetProperties().Where(p => p.CanRead && p.CanWrite && p.GetCustomAttribute<DataMemberAttribute>() != null);
}
// ------- The rest of these methods do not need to be implemented -------
public object GetCustomDataToExport(Type clrType, Type dataContractType)
{
throw new NotImplementedException();
}
public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
{
throw new NotImplementedException();
}
public void GetKnownCustomDataTypes(System.Collections.ObjectModel.Collection<Type> customDataTypes)
{
throw new NotImplementedException();
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
{
throw new NotImplementedException();
}
public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
{
throw new NotImplementedException();
}
}
To use the surrogate, you'll need to create an instance of DataContractJsonSerializerSettings and pass it to the DataContractJsonSerializer with the following properties set. Note that since we require the UseSimpleDictionaryFormat setting, this solution will only work with .Net 4.5 or later.
var settings = new DataContractJsonSerializerSettings();
settings.DataContractSurrogate = new MyDataContractSurrogate();
settings.KnownTypes = new List<Type> { typeof(Dictionary<string, Dictionary<string, object>>) };
settings.UseSimpleDictionaryFormat = true;
Here is a demo:
public class Program
{
public static void Main(string[] args)
{
string json = #"
{
""company_webhooks"": [
{
""company_webhook"": {
""id"": 42,
""url"": ""https://keeptruckin.com/callbacktest/842b02"",
""secret"": ""fe8b75de0a4e5898f0011faeb8c93654"",
""format"": ""json"",
""actions"": [
""vehicle_location_received"",
""vehicle_location_updated""
],
""enabled"": false
}
},
{
""company_webhook"": {
""id"": 43,
""url"": ""https://keeptruckin.com/callbacktest/a6a783"",
""secret"": ""66a7368063cb21887f546c7af91be59c"",
""format"": ""json"",
""actions"": [
""vehicle_location_received"",
""vehicle_location_updated""
],
""enabled"": false
}
},
{
""company_webhook"": {
""id"": 44,
""url"": ""https://keeptruckin.com/callbacktest/53a52c"",
""secret"": ""4451dc96513b3a67107466dd2c4d9589"",
""format"": ""json"",
""actions"": [
""vehicle_location_received"",
""vehicle_location_updated""
],
""enabled"": false
}
},
{
""company_webhook"": {
""id"": 45,
""url"": ""https://keeptruckin.com/callbacktest/6fb337"",
""secret"": ""4177fbd88c30faaee03a4362648bd663"",
""format"": ""json"",
""actions"": [
""vehicle_location_received"",
""vehicle_location_updated""
],
""enabled"": false
}
},
{
""company_webhook"": {
""id"": 46,
""url"": ""https://keeptruckin.com/callbacktest/8cd6da"",
""secret"": ""6e41817a048b009435e5102fca17db55"",
""format"": ""json"",
""actions"": [
""vehicle_location_received"",
""vehicle_location_updated""
],
""enabled"": false
}
}
],
""pagination"": {
""per_page"": 25,
""page_no"": 1,
""total"": 5
}
}";
var settings = new DataContractJsonSerializerSettings();
settings.DataContractSurrogate = new MyDataContractSurrogate();
settings.KnownTypes = new List<Type> { typeof(Dictionary<string, Dictionary<string, object>>) };
settings.UseSimpleDictionaryFormat = true;
KeepTruckinResponse response = Deserialize<KeepTruckinResponse>(json, settings);
foreach (KeepTruckinCompanyWebHook wh in response.WebHooks)
{
Console.WriteLine("Id: " + wh.Id + ", Url: " + wh.Url);
}
}
public static T Deserialize<T>(string json, DataContractJsonSerializerSettings settings)
{
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
var ser = new DataContractJsonSerializer(typeof(T), settings);
return (T)ser.ReadObject(ms);
}
}
public static string Serialize(object obj, DataContractJsonSerializerSettings settings)
{
using (MemoryStream ms = new MemoryStream())
{
var ser = new DataContractJsonSerializer(obj.GetType(), settings);
ser.WriteObject(ms, obj);
return Encoding.UTF8.GetString(ms.ToArray());
}
}
}
[DataContract]
public class KeepTruckinResponse
{
[DataMember(Name = "company_webhooks", EmitDefaultValue = false)]
public KeepTruckinCompanyWebHook[] WebHooks { get; set; }
[DataMember(Name = "pagination", EmitDefaultValue = false)]
public KeepTruckinPagination Pagination { get; set; }
public string RawJSON { get; set; }
}
[DataContract]
public class KeepTruckinPagination
{
[DataMember(Name = "per_page", EmitDefaultValue = false)]
public int PerPage { get; set; }
[DataMember(Name = "page_no", EmitDefaultValue = false)]
public int PageNumber { get; set; }
[DataMember(Name = "total", EmitDefaultValue = false)]
public int Total { get; set; }
}
[DataContract(Name = "company_webhook")]
public class KeepTruckinCompanyWebHook
{
[DataMember(Name = "id", EmitDefaultValue = false)]
public int Id { get; set; }
[DataMember(Name = "url", EmitDefaultValue = false)]
public string Url { get; set; }
}
Output:
Id: 42, Url: https://keeptruckin.com/callbacktest/842b02
Id: 43, Url: https://keeptruckin.com/callbacktest/a6a783
Id: 44, Url: https://keeptruckin.com/callbacktest/53a52c
Id: 45, Url: https://keeptruckin.com/callbacktest/6fb337
Id: 46, Url: https://keeptruckin.com/callbacktest/8cd6da
In my application I want to show a folder and its containing bookmarks. I try to achieve something like this:
folder Wikipedia
url a
url b
url ...
folder StackOverflow
url a
url b
Therefore I have to parse the following json string:
{
"checksum": "7d7205349eb64a4894aafc5ce074c0c0",
"roots": {
"bookmark_bar": {
"children": [ {
"children": [ {
"date_added": "13021579661026871",
"id": "28",
"name": "design patterns - Do you allow the Web Tier to access the DAL directly? - Stack Overflow",
"type": "url",
"url": "http://stackoverflow.com/questions/796656/do-you-allow-the-web-tier-to-access-the-dal-directly"
}, {
"date_added": "13021665700468056",
"id": "31",
"name": "VS 2010 Error when creating or opening projects - Stack Overflow",
"type": "url",
"url": "http://stackoverflow.com/questions/8403853/vs-2010-error-when-creating-or-opening-projects"
} ],
"date_added": "13021579680308871",
"date_modified": "13024947520078515",
"id": "29",
"name": "StackOverflow",
"type": "folder"
}, {
"children": [ {
"date_added": "13022096980978880",
"id": "45",
"name": "Dependency injection - Wikipedia, the free encyclopedia",
"type": "url",
"url": "http://en.wikipedia.org/wiki/Dependency_injection"
}, {
"date_added": "13024941326636844",
"id": "124",
"name": "Strategy pattern - Wikipedia, the free encyclopedia",
"type": "url",
"url": "http://en.wikipedia.org/wiki/Strategy_pattern"
} ],
"date_added": "13023315356559470",
"date_modified": "13024946156966435",
"id": "72",
"name": "Wiki",
"type": "folder"
}, {
"children": [ {
"date_added": "13023667785042757",
"id": "85",
"name": "Anemic Domain Model Illustrated | Frequent incoherent cogitation",
"type": "url",
"url": "http://vitamic.wordpress.com/2007/01/04/anemic-domain-model-illustrated/"
} ],
"date_added": "13023667668403520",
"date_modified": "13023668043391377",
"id": "82",
"name": "#Read",
"type": "folder"
}, {
"date_added": "13025102943539897",
"id": "130",
"name": "Modern UI for WPF - Home",
"type": "url",
"url": "http://mui.codeplex.com/wikipage?title=screenshots&referringTitle=Home"
} ],
"date_added": "13020681767991841",
"date_modified": "13025102947408897",
"id": "1",
"name": "Lesezeichenleiste",
"type": "folder"
}
},
"version": 1
}
I have tried the GroupBy Function, like this without success:
var items = jObject.Descendants()
.Where(x => x.Type == JTokenType.Object &&
x.Value<string>("type") != null)
.GroupBy(x => x.Value<string>("type"));
foreach (var item in items)
{
Console.WriteLine(item.Key.ToString());
foreach (var children in item)
{
Console.WriteLine(" " + children.Value<string>("name"));
}
}
I have also tried do apply the Join Function but I am missing a join property here. Can someone point me in the right direction please?
I would parse that json using concrete classes.
var root = JsonConvert.DeserializeObject<RootObj>(json);
Print(root.roots.bookmark_bar,"");
void Print(Node n,string padding)
{
Console.WriteLine(padding + "+" + n.name);
foreach(var url in n.children.Where(c => c.type == "url"))
{
Console.WriteLine(padding + "\t-" + url.name);
}
foreach (var folder in n.children.Where(c => c.type == "folder"))
{
Print(folder, padding + "\t");
}
}
public class Node
{
public string date_added { get; set; }
public string date_modified { get; set; }
public string id { get; set; }
public string name { get; set; }
public string type { get; set; }
public string url { get; set; }
public List<Node> children { get; set; }
}
public class Roots
{
public Node bookmark_bar { get; set; }
}
public class RootObj
{
public string checksum { get; set; }
public Roots roots { get; set; }
public int version { get; set; }
}
Above code is enough to parse your json, but if you want *date_modified* and *date_added* fields as DateTime, you can implement a JsonConverter class
var root = JsonConvert.DeserializeObject<RootObj>(json, new DateTimeConverter());
class DateTimeConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(DateTime);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return new DateTime(1970,1,1).Add(TimeSpan.FromTicks(long.Parse((string)reader.Value)));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then your Node class will be
public class Node
{
public DateTime date_added { get; set; }
public DateTime date_modified { get; set; }
public string id { get; set; }
public string name { get; set; }
public string type { get; set; }
public string url { get; set; }
public List<Node> children { get; set; }
}