Json.Net反序列化为C#派生类

时间:2017-04-14 14:19:20

标签: c# json serialization asp.net-web-api json.net

Json.Net 将其收到的对象反序列化为Control类的适当派生词。 (请参阅以下对问题的解释。另请注意,我认为这是解释问题所需的最少代码量。请提前感谢您查看此问题。)

我正在尝试将以下类序列化/反序列化为JSON。

public class Page {
    public Guid Id { get; set; }
    public Guid CustomerId { get; set; }
    public IList<Control> Controls { get; set; }
}

这是Control类:

public class Control : ControlBase
{
    public override Enums.CsControlType CsControlType { get { return Enums.CsControlType.Base; } }
}

这是ControlBase抽象类:

public abstract class ControlBase
{
    public Guid Id { get; set; }

    public virtual Enums.CsControlType CsControlType { get; }

    public Enums.ControlType Type { get; set; }

    public string PropertyName { get; set; }

    public IList<int> Width { get; set; }

    public string FriendlyName { get; set; }

    public string Description { get; set; }
}

以下是从Control:

派生的OptionsControl
public class OptionsControl : Control
{
    public override Enums.CsControlType CsControlType { get { return Enums.CsControlType.OptionsControl; } }

    public IDictionary<string, string> Options;
}

当序列化OptionsControl时,JSON如下所示。请注意,Json.Net添加了$type属性,因此(据推测)它可以将控件反序列化为OptionsControl。

              {  
                 "options":{  
                    "TN":"TN"
                 },
                 "csControlType":4,
                 "id":"00000000-0000-0000-0000-000000000000",
                 "type":4,
                 "propertyName":"addresses[0].state",
                 "width":[  
                    2,
                    2,
                    6
                 ],
                 "friendlyName":"State",
                 "description":null,
                 "$type":"MyApp.Infrastructure.Core.Models.UI.Controls.OptionsControl, MyApp.Infrastructure.Core"
              }

但是,当我尝试反序列化Page(包含基本控件和OptionsControls)时,所有控件都被反序列化到基本Control中,并忽略options属性(包括上面的状态列表)

这是我尝试反序列化(和更新)通过WebAPI服务收到的Page对象的方式:

    [HttpPut]
    [ActionName("UpdatePage")]
    public async Task<CommandResult> UpdatePageAsync([FromBody]Object page)
    {
        try
        {
            // Deserialize the page into the correct object
            // When I Debug.WriteLine the page.ToString(), the page still has the $type property at this point (before it is deserialized).
            var dsPage = return JsonConvert.DeserializeObject<Page>(page.ToString(), new JsonSerializerSettings
            {
                TypeNameHandling = TypeNameHandling.All
            });

            // The dsPage passed in here does not have any OptionsControls.
            // At this point, they have all been deserialized into the base Control, unfortunately.
            return await _uiCommandFacade.UpdatePageAsync(dsPage, msg);
        }
        catch (Exception ex)
        {
            return new CommandResult()
            {
                ErrorType = ErrorType.ControllerError,
                DeveloperMessage = $"Unable to update page",
                Exception = ex
            };
        }
    }

正如您所看到的,我正在使用TypeNameHandling.All(如文档中所述并在SO上多次提到)进行反序列化(就像我在序列化期间一样,这就是生成$type属性的原因在JSON中)。但是,当我反序列化Page对象时,Page对象中的任何OptionsControl被反序列化为常规基础Control,因此忽略Options属性(因此我的状态列表是没有更新/扔掉)。

如何让Json.Net正确反序列化我的控件及其派生词?

2 个答案:

答案 0 :(得分:3)

看起来问题可能是由于$type元数据属性不是控件的JSON中的第一个属性。通常,Json.Net首先需要此属性才能识别它。尝试添加

MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead
反序列化时

到您的设置。这应该允许Json.Net稍后在JSON中找到$type属性。

答案 1 :(得分:0)

我认为您应该在web-api启动类中设置配置。

尝试使用类似

的startup.cs文件
        GlobalConfiguration.Configuration.Formatters.Clear(); // or remove just the json one 

        var jsonformatter = new System.Net.Http.Formatting.JsonMediaTypeFormatter()
        {
            SerializerSettings = new Newtonsoft.Json.JsonSerializerSettings()
            {
                // all your configurations
                TypeNameHandling = Newtonsoft.Json.TypeNameHandling.All
            }
        };

        GlobalConfiguration.Configuration.Formatters.Add(jsonformatter);

我认为您丢失了信息,因为您在方法中获得了反序列化的Object项(页面),然后通过调用page.tostring()方法再次序列化它,然后使用序列化程序属性再次反序列化它。