来自JSON的ASP.NET MVC 4递归模型绑定

本文关键字:递归 模型 绑定 MVC NET JSON ASP 来自 | 更新日期: 2023-09-27 18:27:16

我正在使用Fabric.js,将图形对象保存到数据库中。我们经常使用组,从而产生多个级别的嵌套图形对象。我已经创建了与从画布对象发回的值相匹配的类,它正确地绑定了绘图中的顶级项。然而,它没有绑定任何子项,我也不知道出了什么问题。

我的模型(为了简短起见,删除了一些属性:

public class DrawingObj
{
    public int ParentID { get; set; } //will be populated manually
    [JsonProperty("type")]
    public string Type { get; set; }
    [JsonProperty("originX")]
    public string OriginX { get; set; }
    [JsonProperty("originY")]
    public string OriginY { get; set; }
    ....
    [JsonProperty("objects")]
    public ICollection<DrawingObj> ChildObj { get; set; }
    [JsonProperty("points")]
    public ICollection<Point> Points { get; set; }
}
public class Point
{
    [JsonProperty("type")]
    public string Type { get; set; }
    [JsonProperty("x")]
    public decimal X { get; set; }
    [JsonProperty("y")]
    public decimal Y { get; set; }
}

这是Json。对不起,有很多。

{"objects":[{"type":"group","originX":"center","originY":"center","left":461,"top":278,"width":126,"height":247.5,"fill":"rgb(0,0,0)","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","objects":[{"type":"rounded-polygon","originX":"center","originY":"center","left":0,"top":0,"width":126,"height":247.5,"fill":"white","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","points":[{"type":"line","x":-63,"y":-123.75},{"type":"line","x":-63,"y":123.75},{"type":"line","x":63,"y":123.75},{"type":"line","x":63,"y":-123.75},{"type":"line","x":-63,"y":-123.75}]},{"type":"rounded-polygon","originX":"center","originY":"center","left":0,"top":0,"width":125,"height":246.25,"fill":"white","stroke":1,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","points":[{"type":"line","x":-62.5,"y":-123.125},{"type":"line","x":-62.5,"y":123.125},{"type":"line","x":62.5,"y":123.125},{"type":"line","x":62.5,"y":-123.125},{"type":"line","x":-62.5,"y":-123.125}]},{"type":"rounded-polygon","originX":"center","originY":"center","left":0,"top":0,"width":111.38,"height":231,"fill":"white","stroke":1,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","points":[{"type":"line","x":-55.6875,"y":-115.5},{"type":"line","x":-55.6875,"y":115.5},{"type":"line","x":55.6875,"y":115.5},{"type":"line","x":55.6875,"y":-115.5},{"type":"line","x":-55.6875,"y":-115.5}]},{"type":"rounded-polygon","originX":"center","originY":"center","left":0,"top":0,"width":96,"height":215.25,"fill":"#C9C9C9","stroke":1,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","points":[{"type":"line","x":-48,"y":-107.625},{"type":"line","x":-48,"y":107.625},{"type":"line","x":48,"y":107.625},{"type":"line","x":48,"y":-107.625},{"type":"line","x":-48,"y":-107.625}]},{"type":"rounded-polygon","originX":"center","originY":"center","left":0,"top":0,"width":91.5,"height":210.75,"fill":"black","stroke":1,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","points":[{"type":"line","x":-45.75,"y":-105.375},{"type":"line","x":-45.75,"y":105.375},{"type":"line","x":45.75,"y":105.375},{"type":"line","x":45.75,"y":-105.375},{"type":"line","x":-45.75,"y":-105.375}]},{"type":"rounded-polygon","originX":"center","originY":"center","left":0,"top":0,"width":90,"height":209.62,"fill":"#A5EDF2","stroke":1,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","points":[{"type":"line","x":-45,"y":-104.811},{"type":"line","x":-45,"y":104.811},{"type":"line","x":45,"y":104.811},{"type":"line","x":45,"y":-104.811},{"type":"line","x":-45,"y":-104.811}]},{"type":"custom-circle","originX":"center","originY":"center","left":0,"top":0,"width":20,"height":20,"fill":"white","stroke":2,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","radius":10},{"type":"named-text","originX":"center","originY":"center","left":0,"top":0,"width":6,"height":15.6,"fill":"black","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","text":"1","fontSize":12,"fontWeight":"normal","fontFamily":"Times New Roman","fontStyle":"","lineHeight":1.3,"textDecoration":"","textAlign":"left","path":null,"textBackgroundColor":"","useNative":true,"name":"label"}]},{"type":"group","originX":"center","originY":"center","left":598,"top":277,"width":126,"height":247.5,"fill":"rgb(0,0,0)","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","objects":[{"type":"rounded-polygon","originX":"center","originY":"center","left":0,"top":0,"width":126,"height":247.5,"fill":"white","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","points":[{"type":"line","x":-63,"y":-123.75},{"type":"line","x":-63,"y":123.75},{"type":"line","x":63,"y":123.75},{"type":"line","x":63,"y":-123.75},{"type":"line","x":-63,"y":-123.75}]},{"type":"rounded-polygon","originX":"center","originY":"center","left":0,"top":0,"width":125,"height":246.25,"fill":"white","stroke":1,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","points":[{"type":"line","x":-62.5,"y":-123.125},{"type":"line","x":-62.5,"y":123.125},{"type":"line","x":62.5,"y":123.125},{"type":"line","x":62.5,"y":-123.125},{"type":"line","x":-62.5,"y":-123.125}]},{"type":"rounded-polygon","originX":"center","originY":"center","left":0,"top":0,"width":111.38,"height":231,"fill":"white","stroke":1,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","points":[{"type":"line","x":-55.6875,"y":-115.5},{"type":"line","x":-55.6875,"y":115.5},{"type":"line","x":55.6875,"y":115.5},{"type":"line","x":55.6875,"y":-115.5},{"type":"line","x":-55.6875,"y":-115.5}]},{"type":"rounded-polygon","originX":"center","originY":"center","left":0,"top":0,"width":96,"height":215.25,"fill":"#C9C9C9","stroke":1,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","points":[{"type":"line","x":-48,"y":-107.625},{"type":"line","x":-48,"y":107.625},{"type":"line","x":48,"y":107.625},{"type":"line","x":48,"y":-107.625},{"type":"line","x":-48,"y":-107.625}]},{"type":"rounded-polygon","originX":"center","originY":"center","left":0,"top":0,"width":91.5,"height":210.75,"fill":"black","stroke":1,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","points":[{"type":"line","x":-45.75,"y":-105.375},{"type":"line","x":-45.75,"y":105.375},{"type":"line","x":45.75,"y":105.375},{"type":"line","x":45.75,"y":-105.375},{"type":"line","x":-45.75,"y":-105.375}]},{"type":"rounded-polygon","originX":"center","originY":"center","left":0,"top":0,"width":90,"height":209.62,"fill":"#A5EDF2","stroke":1,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","points":[{"type":"line","x":-45,"y":-104.811},{"type":"line","x":-45,"y":104.811},{"type":"line","x":45,"y":104.811},{"type":"line","x":45,"y":-104.811},{"type":"line","x":-45,"y":-104.811}]},{"type":"custom-circle","originX":"center","originY":"center","left":0,"top":0,"width":20,"height":20,"fill":"white","stroke":2,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","radius":10},{"type":"named-text","originX":"center","originY":"center","left":0,"top":0,"width":6,"height":15.6,"fill":"black","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","text":"2","fontSize":12,"fontWeight":"normal","fontFamily":"Times New Roman","fontStyle":"","lineHeight":1.3,"textDecoration":"","textAlign":"left","path":null,"textBackgroundColor":"","useNative":true,"name":"label"}]}],"background":""}

Ajax调用:

    var jsontext = JSON.stringify(canvas);
    $.ajax({
        url: '/Project/SaveDrawing/',
        data: jsontext,
        contentType: 'application/json; charset=utf-8',
        dataType: 'json',
        type: 'post',
        success: onSaved,
        error: function (data, textStatus) { alert(textStatus); }
    });

控制器:

    [HttpPost]
    public JsonResult SaveDrawing(ICollection<DrawingObj> objects)
    {
        try
        {
            if (objects != null)
            {
                bool saved = Drawing.SaveDrawing(objects);
                return Json(new { success = saved });
            }
        }
        catch (Exception ex)
        {
            error = ex.Message;
        }
        return Json(new { success = false, error = error });
    }

因此,我的两个顶级项完全按照预期绑定,但ChildObj和Points都为null。我不明白为什么这些集合没有绑定,因为我相信我的命名是正确的。

提前感谢您的帮助。

来自JSON的ASP.NET MVC 4递归模型绑定

很明显,ASP.NET MVC DefaultModelBinder在那里工作不正常。

一种方法是为对象创建自定义模型绑定器。首先,您必须添加一个额外的类来正确地将您发布的JSON映射到控制器。

[JsonObject]
public class ContainerObj
{
    [JsonProperty("objects")]
    public ICollection<DrawingObj> Objects { get; set; }
    [JsonProperty("background")]
    public string Background { get; set; }
}

然后,您将创建一个自定义模型绑定器,使用JSON.NET:将此对象反序列化为您需要的对象

public class ContainerObjModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType == typeof(ContainerObj))
        {
            var data = GetDocumentContents(controllerContext.HttpContext.Request);
            return JsonConvert.DeserializeObject<ContainerObj>(data);
        }
        return base.BindModel(controllerContext, bindingContext);
    }
    private string GetDocumentContents(System.Web.HttpRequestBase Request)
    {
        string documentContents;
        using (var receiveStream = Request.InputStream)
        {
            using (var readStream = new StreamReader(receiveStream, Encoding.UTF8))
            {
                readStream.BaseStream.Seek(0, SeekOrigin.Begin);
                documentContents = readStream.ReadToEnd();
            }
        }
        return documentContents;
    }
}

然后你可以在Global.asax:中注册这个自定义模型绑定器

 ModelBinders.Binders.Add(typeof(ContainerObj), new ContainerObjModelBinder());

最后,您的控制器会自动识别出需要使用自定义模型绑定器:

[HttpPost]
public JsonResult SaveDrawing(ContainerObj data)
{
    // Request processing
}

默认的模型绑定器不符合JSON.Net属性,因此子JSON元素"objects"被完全忽略。

只需在JSON中将子"objects"更改为"childObj"(以匹配模型属性名称),就可以使用默认的模型绑定器。

{ "objects": 
    [{ "type": "group", "originX": "center", "originY": "center", ... 
        "childObj": [{ "type": "rounded-polygon", "originX": "center", "originY": "center", 
    ...
}

否则,按照ZenCoder的建议,创建一个使用JSON.Net属性的自定义模型绑定器。