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