【问题标题】:Deserializing JSON to object with no default constructor in ASP.NET MVC 3在 ASP.NET MVC 3 中将 JSON 反序列化为没有默认构造函数的对象
【发布时间】:2011-09-09 22:30:17
【问题描述】:

关于 JSON 反序列化有很多问题,但其中很多似乎是针对 MVC 1 或 MVC 2。我似乎没有找到专门针对 MVC 3 的令人满意的答案。

我有一个具有不可变属性且没有默认构造函数的对象,我想在 ASP.NET MVC 3 应用程序中对其进行反序列化。这是一个简化的版本:

public class EmailAddress
{
    public EmailAddress(string nameAndEmailAddress)
    {
        Name = parseNameFromNameAndAddress(nameAndEmailAddress);
        Address = parseAddressFromNameAndAddress(nameAndEmailAddress);
    }

    public EmailAddress(string name, string address)
    {
        Guard.Against<FormatException>(!isNameValid(name), "Value is invalid for EmailAddress.Name: [{0}]", name);
        Guard.Against<FormatException>(!isAddressValid(address), "Value is invalid for EmailAddress.Address: [{0}]", address);
        Name = name;
        Address = address;
    }

    public string Address { get; private set; }
    public string Name { get; private set; }

    // Other stuff
}

一个示例控制器操作可能是:

[HttpPost]
public ActionResult ShowSomething(EmailAddress emailAddress)
{
    return View(emailAddress)
}

传入的 JSON 是:

{"Address":"joe@bloggs.com","Name":"Joe Bloggs"}

在 MVC3 中反序列化的最佳方法是什么?是否有某种方法可以实现可以处理此问题的自定义模型绑定器或反序列化器类?

不干扰对象本身的解决方案会更好(即单独的反序列化器类,而不是向属性添加属性等),尽管对任何好的建议持开放态度。

我在这里发现了一个类似的问题(没有答案):Can I deserialize to an immutable object using JavascriptSerializer?

【问题讨论】:

  • 达夫曼,你混淆了你的概念。 MVC 3 自动反序列化 JsonValueProviderFactory 中的 JSON。然后,此反序列化信息可用于绑定到您的模型。 Darin 建议的解决方案适用于所有情况,即使 EmailAddress 类嵌入到不同的类中。
  • @counsellorben - 没错,感谢您为我澄清!

标签: json asp.net-mvc-3 deserialization


【解决方案1】:

是否有一些方法可以实现自定义模型绑定器或 可以处理这个的反序列化器类?

是的,您可以编写自定义模型绑定器:

public class EmailAddressModelBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        var addressKey = "Address";
        var nameKey = "Name";
        if (!string.IsNullOrEmpty(bindingContext.ModelName))
        {
            addressKey = bindingContext.ModelName + "." + addressKey;
            nameKey = bindingContext.ModelName + "." + nameKey;
        }

        var addressValue = bindingContext.ValueProvider.GetValue(addressKey);
        var nameValue = bindingContext.ValueProvider.GetValue(nameKey);
        if (addressValue == null || nameValue == null)
        {
            throw new Exception("You must supply an address and name");
        }
        return new EmailAddress(nameValue.AttemptedValue, addressValue.AttemptedValue);
    }
}

将在 Application_Start 中注册:

ModelBinders.Binders.Add(typeof(EmailAddress), new EmailAddressModelBinder());

最后剩下的就是调用动作了:

$.ajax({
    url: '@Url.Action("ShowSomething")',
    type: 'POST',
    data: JSON.stringify({ "Address": "joe@bloggs.com", "Name": "Joe Bloggs" }),
    contentType: 'application/json',
    succes: function (result) {
        alert('success');
    }
});

【讨论】:

  • 谢谢 - 这是一个很好的答案,对于问题中提到的特定情况很有用,其中 EmailAddress 是操作的模型。但是,我也有一些情况,其中 EmailAddress 是不同模型的属性 - 除了使用 ModelBinder 之外,还有更通用的反序列化技术吗?
  • 忽略最后一条评论 - 现在我更了解它是如何工作的,这正是我所需要的!
【解决方案2】:

编辑答案:

我看错了代码,查看的是构造函数参数,而不是属性。

问题的原因是属性的私有集。

也就是说,应该是:

public string Address { get; set; }
public string Name { get; set; }

如果您进行了该更改,则应该一切正常。

记住:

模型绑定器查找 PROPERTIES,而不是构造函数!

【讨论】:

  • 我不明白你的回答 - JSON 和属性都是相同的大小写,即“名称”和“名称”。此外 - 如果有默认构造函数和公共设置器,则反序列化工作。你的意思是构造函数参数也需要大小写相同吗?
  • 已确认这不起作用 - 也与文本框无关,因为数据是通过 JSON 从另一个应用程序发布的。
  • 糟糕,我正在查看构造函数参数。你的问题是私人集。模型绑定器将尝试查找公共属性,而不是具有正确参数的构造函数。
  • 确实 - 我已经撤回了对你的反对票 - 但是我特别问我如何反序列化为具有私有设置器且没有默认构造函数的类。仍在寻找解决方案!
  • 我想我以后会更仔细地阅读这个问题,这里的事情有点忙...
猜你喜欢
  • 1970-01-01
  • 2013-11-05
  • 2018-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-07
  • 1970-01-01
相关资源
最近更新 更多