I have an issue deserializing a JSON object into my class.
I have a class DocumentListByPolicy
that is used as a List<>
in another class. For the document list, this is my code:
namespace MyNameSpace
{
[DataContract]
public class DocumentListByPolicy
{
[DataMember(Name = "documentCreatedDate")]
public DateTime DocumentCreatedDate { get; set; }
[DataMember(Name = "comment")]
public string VehicleVinNumber { get; set; }
// ..... more properties here
}
}
What I am trying to do is reference the "comment" property as VehicleVinNumber
which I am doing in a LINQ statement:
DocIDsByVIN = resp.LevelDocumentList.GroupBy(d => d.VehicleVinNumber.Trim())
.Select(l => l.OrderByDescending(d => d.DocumentCreatedDate).FirstOrDefault(d => d.DocumentId > 0))
.Where(r => r != null)
.ToDictionary(r => r.VehicleVinNumber.Trim(), r => r.DocumentId);
It compiles and runs but throws an error in the LINQ code because it says VehicleVinNumber
is null. If I change the name in the model back to "comment" or "Comment" the code works as expected. My impression with the DataMember
attribute was to be able to map the values coming back to my model property names, but this does not seem to happen. The mapping is not occurring.
Can anyone please tell me what I am doing wrong?
Thanks
The deserialization is being done by RestSharp extension methods.
public static partial class RestClientExtensions {
[PublicAPI]
public static RestResponse<T> Deserialize<T>(this IRestClient client, RestResponse response)
=> client.Serializers.Deserialize<T>(response.Request!, response, client.Options);
RestSharp does not use or support DataContractJsonSerializer
out of the box, so the default serializer does not recognise [DataContract]
or [DataMember]
. See the documentation for how to configure serialization in RestSharp.
DataContract Serialization was designed for use in WCF projects and is no longer commonly supported by 3rd party libraries, for JSON projects in .Net Framework Json.NET (Newtonsoft.Json) is the community default, RestSharp has support for this Json.NET serializer
If you change over to Json.NET serializer then you can use
JsonPropertyAttribute
to annotate your properties and specify the name in the json content to map to:[JsonProperty("comment")] public string VehicleVinNumber { get;set; }
In Json.NET it is not necessary to annotate all properties to go from camel case to Pascal case as the serializer by default is case insensitive, so your class only needs this minimal representation:
using Newtonsoft.Json; public class DocumentListByPolicy { public DateTime DocumentCreatedDate { get; set; } [JsonProperty("comment")] public string VehicleVinNumber { get; set; } // ..... more properties here }
If converting to Json.NET is not a viable option, you can add support for [DataContract]
using a custom serializer
public class RestDataContractJsonSerializer : IRestSerializer, ISerializer, IDeserializer
{
public string Serialize(object obj)
{
if (obj == null) return null;
var serializer = new DataContractJsonSerializer(obj.GetType());
using (var ms = new MemoryStream())
{
serializer.WriteObject(ms, obj);
return Encoding.UTF8.GetString(ms.ToArray());
}
}
public string Serialize(Parameter bodyParameter) => Serialize(bodyParameter.Value);
public T Deserialize<T>(RestResponse response)
{
if (response.Content == null) return default;
var serializer = new DataContractJsonSerializer(typeof(T));
using (var ms = new MemoryStream())
{
ms.Write(response.RawBytes, 0, response.RawBytes.Length);
ms.Seek(0, SeekOrigin.Begin);
return (T)serializer.ReadObject(ms);
}
}
public ContentType ContentType { get; set; } = ContentType.Json;
public ISerializer Serializer => this;
public IDeserializer Deserializer => this;
public DataFormat DataFormat => DataFormat.Json;
public string[] AcceptedContentTypes => ContentType.JsonAccept;
public SupportsContentType SupportsContentType
=> contentType => contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase);
}
This is an example implementation:
namespace RestSharpDemo
{
using System.Text;
using RestSharp;
using RestSharp.Serializers;
internal class Program
{
static void Main(string[] args)
{
var client = new RestClient("https://northwind.vercel.app/api/",
configureSerialization: s => s.UseSerializer<RestDataContractJsonSerializer>());
var request = new RestRequest("categories/3");
var response = client.ExecuteGet<NorthWindCategory>(request);
Console.WriteLine(response.Content);
Console.WriteLine("Id: {0}", response.Data.Id);
Console.WriteLine("Description: {0}", response.Data.Description);
Console.WriteLine("CategoryName: {0}", response.Data.CategoryName);
}
}
[DataContract]
public class NorthWindCategory
{
[DataMember(Name = "id")]
public int Id { get; set; }
[DataMember(Name = "description")]
public string Description { get; set; }
[DataMember(Name = "name")]
public string CategoryName { get; set; }
}
public class RestDataContractJsonSerializer : IRestSerializer, ISerializer, IDeserializer
{
public string Serialize(object obj)
{
if (obj == null) return null;
var serializer = new DataContractJsonSerializer(obj.GetType());
using (var ms = new MemoryStream())
{
serializer.WriteObject(ms, obj);
return Encoding.UTF8.GetString(ms.ToArray());
}
}
public string Serialize(Parameter bodyParameter) => Serialize(bodyParameter.Value);
public T Deserialize<T>(RestResponse response)
{
if (response.Content == null) return default;
var serializer = new DataContractJsonSerializer(typeof(T));
using (var ms = new MemoryStream())
{
ms.Write(response.RawBytes, 0, response.RawBytes.Length);
ms.Seek(0, SeekOrigin.Begin);
return (T)serializer.ReadObject(ms);
}
}
public ContentType ContentType { get; set; } = ContentType.Json;
public ISerializer Serializer => this;
public IDeserializer Deserializer => this;
public DataFormat DataFormat => DataFormat.Json;
public string[] AcceptedContentTypes => ContentType.JsonAccept;
public SupportsContentType SupportsContentType
=> contentType => contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase);
}
}