使用Newtonsoft序列化泛型类而不封装内部类型

问题描述 投票:0回答:1

假设有以下泛型类和内部类型类:

public class Generic<T>
    where T : class
{
    public bool IsSuccess { get; set; }
    public string Message { get; set; }
    public T Data { get; set; }
}

public class Specific
{
    public string Name { get; set; }
    public int Id { get; set; }
}

有没有办法让 Newtonsoft/Json.NET 序列化它们而不将它们封装在“数据”中,例如:

{"IsSuccess":true,"Message":"Success","Name":"Test","Id":1}

而不是:

{"IsSuccess":true,"Message":"Success","Data":{"Name":"Test","Id":1}}

简单的方法产生封装:

JsonConvert.SerializeObject(myGenericObject);

我知道我可以通过经典继承来实现这一点(

Specific : Generic
),但我正在尝试其他方法,我想在我不想要
Generic
属性的上下文中重用我的特定类。

c# json.net
1个回答
0
投票

是的,您可以通过创建自定义 JsonConverter 来完成此操作。但是,由于 Newtonsoft.Json 不太喜欢它们,因此您还需要传入 DefaultContractResolver,以便它能够使用新的转换器。

转换器可以改进,只是为了表明这是可能的。

您还必须使用 JsonConverter 属性来装饰您的 Generic:

[JsonConverter(typeof(GenericConverter<>))]
public class Generic<T> where T : class
{
    public bool IsSuccess { get; set; }
    public string Message { get; set; }
    public T Data { get; set; }
}

转换器:

public class GenericConverter<T> : JsonConverter<Generic<T>> where T : class
{
    public override void WriteJson(JsonWriter writer, Generic<T> value, JsonSerializer serializer)
    {
        JObject obj = new JObject(
            new JProperty("IsSuccess", value.IsSuccess),
            new JProperty("Message", value.Message)
        );

        // Check if Data is not null and add its properties to the JObject
        if (value.Data != null)
        {
            var dataObject = JObject.FromObject(value.Data, serializer);
            obj.Merge(dataObject, new JsonMergeSettings
            {
                MergeArrayHandling = MergeArrayHandling.Union
            });
        }
        obj.WriteTo(writer);
    }

    public override Generic<T> ReadJson(JsonReader reader, Type objectType, Generic<T> existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override bool CanRead => false;
}

合同解析者:

public sealed class GenericConverterContractResolver : DefaultContractResolver
{
    protected override JsonConverter ResolveContractConverter(Type objectType)
    {
        var typeInfo = objectType.GetTypeInfo();
        if (typeInfo.IsGenericType && !typeInfo.IsGenericTypeDefinition)
        {
            var jsonConverterAttribute = typeInfo.GetCustomAttribute<JsonConverterAttribute>();
            if (jsonConverterAttribute != null && jsonConverterAttribute.ConverterType.GetTypeInfo().IsGenericTypeDefinition)
            {
                return (JsonConverter)Activator.CreateInstance(jsonConverterAttribute.ConverterType.MakeGenericType(typeInfo.GenericTypeArguments), jsonConverterAttribute.ConverterParameters);
            }
        }
        return base.ResolveContractConverter(objectType);
    }
}

然后你像这样使用它:

var settings = new JsonSerializerSettings
{
    ContractResolver = new GenericConverterContractResolver()
};
string json = JsonConvert.SerializeObject(genericInstance, settings);
© www.soinside.com 2019 - 2024. All rights reserved.