在 ASP.NET MVC 3 中将 JSON 反序列化为没有默认构造函数的对象

发布于 2024-12-03 14:55:36 字数 1580 浏览 0 评论 0原文

关于 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":"[email protected]","Name":"Joe Bloggs"}

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

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

我在这里发现了一个类似的问题(没有答案): 我可以吗使用 JavascriptSerializer 反序列化为不可变对象?

There are quite a few questions around JSON deserialization but a lot of them seem to be for MVC 1 or MVC 2. I don't seem to have found a satisfactory answer to this specifically for MVC 3.

I have an object with immutable properties and no default constructor, which I want to deserialize to in an ASP.NET MVC 3 application. Here is a simplified version:

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
}

An example controller action might be:

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

The JSON coming in is:

{"Address":"[email protected]","Name":"Joe Bloggs"}

What is the best way to get this to deserialize in MVC3? Is there some way of implementing a custom model binder or deserializer class that can handle this?

A solution that doesn't interfere with the object itself would be preferable (ie. a separate deserializer class, rather than adding attributes to properties, etc), although open to any good suggestions.

I found a similar question (with no answer) here: Can I deserialize to an immutable object using JavascriptSerializer?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

挥剑断情 2024-12-10 14:55:36

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

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

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": "[email protected]", "Name": "Joe Bloggs" }),
    contentType: 'application/json',
    succes: function (result) {
        alert('success');
    }
});

Is there some way of implementing a custom model binder or
deserializer class that can handle this?

Yes, you could write a custom model binder:

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

which will be registered in Application_Start:

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

and finally all that's left is to invoke the action:

$.ajax({
    url: '@Url.Action("ShowSomething")',
    type: 'POST',
    data: JSON.stringify({ "Address": "[email protected]", "Name": "Joe Bloggs" }),
    contentType: 'application/json',
    succes: function (result) {
        alert('success');
    }
});
饮惑 2024-12-10 14:55:36

编辑答案:

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

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

也就是说,它应该是:

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

如果你做出改变,它应该一切正常。

请记住:

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

EDITED ANSWER:

I misread the code, looked at the constructor parameters, instead of the properties.

The cause of your problem is the private set of the properties.

Ie, it should be:

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

If you make that change, it should all work.

Just remember:

The model binder looks for PROPERTIES, not the constructor!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文