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;
}
}