【问题标题】:Change property name to the derived class name it has been instantiated with on serialization with Json.net将属性名称更改为在使用 Json.net 进行序列化时已实例化的派生类名称
【发布时间】:2020-04-29 07:13:07
【问题描述】:

我正在尝试使用最新版本的 NewtonSoft.JSON 在 .NET(标准)中序列化(和反序列化)我的对象。

这是我的课程:

public class BaseRequest
{
    public string UserName{ get; set; }
}

派生自 BaseRequest 的请求(我还有许多其他此类派生自 BaseRequest 的类):

public class GetDeviceRequest : BaseRequest
{
    public string SerialNumber { get; set; }
}

我序列化到 Json 的请求是用类创建的:

public class CommunicationRequest
{
    public CommunicationRequest() { }

    public CommunicationRequest(BaseRequest baseRequest, string version ="1.1")
    {
        this.Version = version;
        this.Request = baseRequest;
    }

    public string Version { get; set; } = "1.1";
    public BaseRequest Request { get; set; }
}

我用于序列化的代码。我还没有测试过反序列化,但它应该以同样的方式工作

GetDeviceRequest getDeviceRequest = new GetDeviceRequest()
{
    SerialNumber = "123456789",
    UserName = "john.doe"
};

CommunicationRequest request = new CommunicationRequest(getDeviceRequest);

var settings = new JsonSerializerSettings
{
    ContractResolver = new CamelCasePropertyNamesContractResolver()
};

var json = JsonConvert.SerializeObject(request, settings);

我得到的 JSON 格式:

{
   "version":"1.1",
   "request":{      
         "serialNumber":"123456789",
         "username":"john.doe"
   }
}

我想要什么:

{
   "version":"1.1",
   "getDeviceRequest":{      
         "serialNumber":"123456789",
         "username":"john.doe"
   }
}

我需要根据派生类的名称将RequestJsonProperty 名称动态更改为GetDeviceRequest

我本来可以做的:

[JsonProperty("DerievedClassName")]
public BaseRequest Request { get; set; }

这显然对我不起作用,因为对象没有被实例化,我无法让类型使用像 Request.GetType() 这样的东西。

似乎方法是实现我的ICustomContractResolver,但我无法为我的用例获得一个有效的CustomContractResolver

有没有一种方法可以按照我想要的方式进行序列化?

【问题讨论】:

    标签: c# .net json serialization json.net


    【解决方案1】:

    您将需要为您的 CommunicationRequest 类定制一个 JsonConverter 来处理动态属性名称,并且还能够在反序列化时将其解析回正确的类型(假设您想使用它进行往返) .以下内容应该适合您的需求,但可能需要进行一些调整。

    public class CommunicationRequestConverter : JsonConverter
    {
        NamingStrategy NamingStrategy { get; set; }
    
        public CommunicationRequestConverter(NamingStrategy namingStrategy)
        {
            NamingStrategy = namingStrategy;
        }
    
        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(CommunicationRequest);
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            CommunicationRequest req = (CommunicationRequest)value;
            JObject jo = new JObject(
                new JProperty(GetPropertyName("Version"), req.Version),
                new JProperty(GetPropertyName(req.Request.GetType().Name), 
                              JObject.FromObject(req.Request, serializer))
            );
            jo.WriteTo(writer);
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JObject jo = JObject.Load(reader);
    
            string versionPropertyName = GetPropertyName("Version");
            JProperty requestProperty = jo.Properties().FirstOrDefault(p => p.Name != versionPropertyName);
    
            Type baseRequestType = Assembly.GetAssembly(typeof(BaseRequest)).GetTypes()
                .Where(t => t.IsClass && GetPropertyName(t.Name) == requestProperty.Name)
                .First();
    
            CommunicationRequest req = new CommunicationRequest
            {
                Version = (string)jo[versionPropertyName],
                Request = (BaseRequest)requestProperty.Value.ToObject(baseRequestType, serializer)
            };
    
            return req;
        }
    
        private string GetPropertyName(string name)
        {
            return NamingStrategy.GetPropertyName(name, false);
        }
    }
    

    像这样设置您的序列化程序设置:

    var ns = new CamelCaseNamingStrategy();
    var settings = new JsonSerializerSettings { 
        ContractResolver = new DefaultContractResolver { NamingStrategy = ns },
        Converters = new List<JsonConverter> { new CommunicationRequestConverter(ns) },
        Formatting = Formatting.Indented
    };
    

    这是一个往返演示:https://dotnetfiddle.net/kufBae

    【讨论】:

    • 比我从您的评论中获得帮助而编写的解决方案更干净。您的代码运行良好,并且更易于阅读。谢谢!
    • 很高兴我能帮上忙。
    猜你喜欢
    • 2012-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多