JsonConvert.SerializeObject is not working properly. restData has the property SuccessfulActivities and it has some values. But When I serialize it is serializing to an empty value for SuccessfulActivities. like the below. Its.Net 6 project. Please help.
{ "SuccessfulActivities": [ {} ], "Status": "{"Type":"INFO","Message":"OK"}", "RequestId": "bcb8b790-2cd8-4b2b-b05a-414a3c7ceb0b", "TenantId": "11111111-1111-1111-1111-84ba20252626" }
public string SerializeRestResponse(IRestResponse restData)
{
JsonSerializerSettings jsonSettings = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, ContractResolver = new RestQueryResponseContractResolver() };
return JsonConvert.SerializeObject(restData, jsonSettings);
}
public class RestQueryResponseContractResolver : DefaultContractResolver
{
List<string> _properties;
public RestQueryResponseContractResolver()
{
_properties = new List<string>();
//_properties.Add(p.Name); This is just to show that I am adding other properties as well from different object type.
PropertyInfo[] props2 = typeof(IRestBotResponse).GetProperties();
foreach (PropertyInfo p in props2)
_properties.Add(p.Name);
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
property.ShouldSerialize = instance => _properties.Contains(property.PropertyName);
return property;
}
}
public interface IRestBotResponse : IRestResponse
{
/// <summary>
/// The list of successful conversation activities that were sent.
/// For the Tuple:
/// Item1 is the user identifier (the user's Azure AD ID)
/// Item2 is the ActivityID
/// </summary>
[Required]
List<(string, string)> SuccessfulActivities { get; }
}
I am able to get other properties properly. But it has a problem only with SuccessfulActivities and I think the reason is that SuccessfulActivities is a list of tuple List<(string, string)>.
I have read this answer, but it suggests using System.Text.Json, But I want to use NewtonSoft.Json as it's already being used in the project at many places. As its already in production working project so I want to make changes with minimal side effects.
Edit:
I am using this JSON response (responseJson) to send it to the client as the response of the HTTP request like this.
return new ContentResult()
{
Content = responseJson,
ContentType = "application/json; charset=utf-8",
StatusCode = (int)statusCode,
};
Edit 2:
As suggested by @Panagiotis Kanavos, I tried using the custom JsonConverter but it is also not considering the SuccessfulActivities. It's not even hitting the breakpoint on the methods WriteJson and ReadJson. Please find below the relevant implementation.
public class RestBotResponse : RestResponse, IRestBotResponse
{
public RestBotResponse()
{
}
[JsonConstructor]
public RestBotResponse(List<(string, string)> successfulActivities, string status, string tenantId, string requestId)
: base(status, requestId, tenantId)
{
SuccessfulActivities = successfulActivities;
}
[Required]
[JsonConverter(typeof(TupleListConverter<string, string>))]
public List<(string, string)> SuccessfulActivities { get; set; }
}
public abstract class RestResponse : IRestResponse
{
protected RestResponse()
{
}
protected RestResponse(string status, string requestId, string tenantId)
{
Status = status;
RequestId = requestId;
if (tenantId == null)
tenantId = string.Empty;
TenantId = tenantId;
}
[Required]
public string Status { get; set; }
public string RequestId { get; set; }
public string TenantId { get; set; }
}
public class TupleListConverter<T1, T2> : JsonConverter<List<(T1, T2)>>
{
public override void WriteJson(JsonWriter writer, List<(T1, T2)> value, JsonSerializer serializer)
{
var array = value.Select(t => new { Item1 = t.Item1, Item2 = t.Item2 }).ToArray();
serializer.Serialize(writer, array);
}
public override List<(T1, T2)> ReadJson(JsonReader reader, Type objectType, List<(T1, T2)> existingValue, bool hasExistingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanRead => false;
}
Edit 3:
Please find the implementation of RestQueryResponseContractResolver
public class RestQueryResponseContractResolver : DefaultContractResolver
{
List<string> _properties;
public RestQueryResponseContractResolver()
{
_properties = new List<string>();
// Include all ITeamUserInfo properties
PropertyInfo[] teamUserInfoProps = typeof(ITeamUserInfo).GetProperties();
foreach (PropertyInfo p in teamUserInfoProps)
_properties.Add(p.Name);
// Include all RestQueryResponse properties
PropertyInfo[] restResponseProps = typeof(RestQueryResponse).GetProperties();
foreach (PropertyInfo p in restResponseProps)
_properties.Add(p.Name);
//PropertyInfo[] props2 = typeof(IRestBotResponse).GetProperties();
//foreach (PropertyInfo p in props2)
// _properties.Add(p.Name);
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
property.ShouldSerialize = instance => _properties.Contains(property.PropertyName);
return property;
}
}