Làm cách nào để chuyển đổi View Model thành đối tượng JSON trong ASP.NET MVC?


156

Tôi là một nhà phát triển Java, mới sử dụng .NET. Tôi đang làm việc trên một dự án .NET MVC2 nơi tôi muốn có một chế độ xem một phần để bọc một widget. Mỗi đối tượng tiện ích JavaScript có một đối tượng dữ liệu JSON sẽ được điền bởi dữ liệu mô hình. Sau đó, các phương pháp để cập nhật dữ liệu này bị ràng buộc với các sự kiện khi dữ liệu được thay đổi trong widget hoặc nếu dữ liệu đó bị thay đổi trong một widget khác.

Mã này là một cái gì đó như thế này:

MyController:

virtual public ActionResult DisplaySomeWidget(int id) {
  SomeModelView returnData = someDataMapper.getbyid(1);

  return View(myview, returnData);
}

myview.ascx:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<SomeModelView>" %>

<script type="text/javascript">
  //creates base widget object;
  var thisWidgetName = new Widget();

  thisWidgetName.updateTable = function() {
    //  UpdatesData
  };
  $(document).ready(function () {
    thisWidgetName.data = <% converttoJSON(model) %>
    $(document).bind('DATA_CHANGED', thisWidgetName.updateTable());
  });
</script>

<div><%:model.name%></div>

Những gì tôi không biết là làm thế nào để gửi dữ liệu qua SomeModelViewvà sau đó có thể sử dụng dữ liệu đó để điền vào tiện ích cũng như chuyển đổi dữ liệu đó thành JSON. Tôi đã thấy một số cách đơn giản thực sự để làm điều đó trong bộ điều khiển nhưng không phải trong chế độ xem. Tôi nghĩ rằng đây là một câu hỏi cơ bản nhưng tôi đã mất vài giờ để cố gắng thực hiện điều này.


1
Tôi biết đây là một câu hỏi cũ. Nhưng như ngày nay có nhiều cách tốt hơn để làm điều đó. Không trộn JSON nội tuyến với kết quả Xem của bạn. JSON dễ dàng nối tiếp thông qua AJAX và có thể được xử lý như các đối tượng. Mọi thứ trong JavaScript phải tách biệt với Chế độ xem. Bạn có thể dễ dàng trả lại các mô hình mà không cần bất kỳ nỗ lực nào thông qua Bộ điều khiển.
Piotr Kula

Câu trả lời:


346

Trong mvc3 với dao cạo @Html.Raw(Json.Encode(object))dường như làm được mẹo.


1
+1 Tôi đã sử dụng Html.Raw, nhưng không bao giờ tìm thấy Json.Encode và chỉ sử dụng JavaScriptSerializer để thêm chuỗi trong bộ điều khiển vào mô hình xem
AaronLS

5
Cách tiếp cận này hoạt động ngay cả khi bạn muốn chuyển JSON kết quả sang Javascript. Dao cạo phàn nàn với các squiggles màu xanh lá cây nếu bạn đặt mã @ Html.Raw (...) làm tham số hàm bên trong các thẻ <script>, nhưng JSON thực sự làm cho nó được gọi là JS. Rất tiện dụng & khéo léo. +1
Carl Heinrich Hancke

1
Đây là cách để làm điều đó kể từ MVC3 và nên được trả về từ bộ điều khiển. Đừng nhúng json vào Viewresult của bạn. Thay vào đó, hãy tạo một bộ điều khiển để xử lý JSON riêng biệt và thực hiện yêu cầu JSON thông qua AJAX. Nếu bạn cần JSON trên khung nhìn, bạn đang làm gì đó sai. Sử dụng ViewModel hoặc một cái gì đó khác.
Piotr Kula

3
Json.Encode mã hóa mảng 2 chiều của tôi thành mảng 1 chiều trong json. Newtonsoft.Json.JsonConvert.SerializeObject tuần tự hóa hai chiều chính xác thành json. Vì vậy, tôi đề nghị sử dụng cái sau.
mono68

12
Khi sử dụng MVC 6 (cũng có thể là 5), bạn cần sử dụng Json.Serializethay vì Encode.
KoalaBear

31

Làm tốt lắm, bạn chỉ mới bắt đầu sử dụng MVC và bạn đã tìm thấy lỗ hổng lớn đầu tiên của nó.

Bạn không thực sự muốn chuyển đổi nó thành JSON trong chế độ xem và bạn không thực sự muốn chuyển đổi nó trong bộ điều khiển, vì cả hai vị trí này đều không có ý nghĩa. Thật không may, bạn bị mắc kẹt với tình huống này.

Điều tốt nhất tôi tìm thấy là gửi JSON đến chế độ xem trong ViewModel, như thế này:

var data = somedata;
var viewModel = new ViewModel();
var serializer = new JavaScriptSerializer();
viewModel.JsonData = serializer.Serialize(data);

return View("viewname", viewModel);

sau đó sử dụng

<%= Model.JsonData %>

theo quan điểm của bạn. Xin lưu ý rằng .NET JavaScriptSerializer chuẩn là khá tào lao.

thực hiện nó trong bộ điều khiển ít nhất làm cho nó có thể kiểm tra được (mặc dù không chính xác như ở trên - bạn có thể muốn lấy ISerializer làm phụ thuộc để bạn có thể chế giễu nó)

Ngoài ra, cập nhật , liên quan đến JavaScript của bạn, sẽ là một cách tốt để bọc TẤT CẢ các tiện ích con bạn có ở trên như vậy:

(
    // all js here
)();

bằng cách này nếu bạn đặt nhiều widget trên một trang, bạn sẽ không bị xung đột (trừ khi bạn cần truy cập các phương thức từ nơi khác trong trang, nhưng trong trường hợp đó bạn vẫn nên đăng ký widget với một số khung widget). Bây giờ nó có thể không phải là vấn đề, nhưng sẽ tốt hơn nếu thêm dấu ngoặc ngay bây giờ để tiết kiệm cho bạn nhiều nỗ lực trong tương lai khi nó trở thành một yêu cầu, đó cũng là cách thực hành OO tốt để đóng gói chức năng.


1
Điều này có vẻ thú vị. Tôi sẽ chỉ tạo một bản sao của dữ liệu dưới dạng json và chuyển nó dưới dạng viewData nhưng theo cách này nó trông thú vị hơn. Tôi sẽ chơi với nó và cho bạn biết. BTW đây là lần đầu tiên tôi đăng câu hỏi lên stackoverflow và phải mất 0,5 phút để nhận được phản hồi tốt, điều đó thật tuyệt vời !!
Chris Stephens

Không có gì sai với nó, nó chỉ không thể tùy chỉnh, nếu bạn muốn bất kỳ định dạng giá trị tùy chỉnh nào bạn phải thực hiện trước đó, về cơ bản làm cho mọi thứ trở thành một chuỗi :( điều này nổi bật nhất với ngày. Tôi biết có cách giải quyết dễ dàng, nhưng chúng không nên cần thiết!
Andrew Bullock

@Update Tôi sẽ thấy về việc sử dụng bộ đếm tĩnh .net của tiện ích đó để tạo một tên đối tượng duy nhất. Nhưng những gì bạn viết có vẻ như nó sẽ hoàn thành điều tương tự và làm nó trơn tru. Ngoài ra, tôi đã xem xét việc ràng buộc việc tạo một widget "new_widget_event 'với một đối tượng cấp trang để theo dõi chúng nhưng lý do ban đầu để làm điều đó đã trở thành OBE. Vì vậy, tôi có thể xem lại điều đó sau. Cảm ơn vì tất cả sự giúp đỡ tôi sẽ đăng một phiên bản sửa đổi của những gì bạn đề xuất nhưng hãy giải thích lý do tại sao tôi thích nó hơn
Chris Stephens

2
tôi rút lại tuyên bố trước đây của mình "không có gì sai với nó". Có mọi thứ sai với nó.
Andrew Bullock

Tại sao bạn nói rằng chúng tôi không thể trả lại JSON từ bộ điều khiển. Đó là cách nó phải được thực hiện. Sử dụng JavaScriptSerializerhoặc Return Json(object)cả hai sử dụng cùng một serial serial. Ngoài ra, việc đăng cùng một JSON trở lại bộ điều khiển sẽ xây dựng lại đối tượng cho bạn miễn là bạn xác định đúng mô hình. Có thể trong MVC2, nó là một nhược điểm lớn .. nhưng ngày nay nó rất dễ sử dụng. Bạn nên cập nhật câu trả lời của bạn để phản ánh điều này.
Piotr Kula

18

Tôi thấy nó là khá tốt để làm điều đó như thế này (sử dụng trong chế độ xem):

    @Html.HiddenJsonFor(m => m.TrackingTypes)

Dưới đây là lớp mở rộng của phương thức trợ giúp:

public static class DataHelpers
{
    public static MvcHtmlString HiddenJsonFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
    {
        return HiddenJsonFor(htmlHelper, expression, (IDictionary<string, object>) null);
    }

    public static MvcHtmlString HiddenJsonFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
    {
        return HiddenJsonFor(htmlHelper, expression, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
    }

    public static MvcHtmlString HiddenJsonFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
    {
        var name = ExpressionHelper.GetExpressionText(expression);
        var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);

        var tagBuilder = new TagBuilder("input");
        tagBuilder.MergeAttributes(htmlAttributes);
        tagBuilder.MergeAttribute("name", name);
        tagBuilder.MergeAttribute("type", "hidden");

        var json = JsonConvert.SerializeObject(metadata.Model);

        tagBuilder.MergeAttribute("value", json);

        return MvcHtmlString.Create(tagBuilder.ToString());
    }
}

Nó không phải là siêu tinh vi, nhưng nó giải quyết vấn đề đặt nó ở đâu (trong Bộ điều khiển hoặc trong chế độ xem?) Câu trả lời rõ ràng là: không;)


Điều này là tốt đẹp và sạch sẽ theo ý kiến ​​của tôi và gói trong một người trợ giúp có thể tái sử dụng. Chúc mừng, J
John

6

Bạn có thể sử dụng Jsontrực tiếp từ hành động,

Hành động của bạn sẽ giống như thế này:

virtual public JsonResult DisplaySomeWidget(int id)
{
    SomeModelView returnData = someDataMapper.getbyid(id);
    return Json(returnData);
}

Biên tập

Chỉ cần thấy rằng bạn cho rằng đây là ModelChế độ xem nên ở trên không đúng hoàn toàn, bạn sẽ phải thực hiện Ajaxcuộc gọi đến phương thức điều khiển để có được điều này, ascxsau đó sẽ không có mô hình, tôi sẽ để lại mã của mình chỉ trong trường hợp nó hữu ích cho bạn và bạn có thể sửa đổi cuộc gọi

Chỉnh sửa 2 chỉ cần đặt id vào mã


1
nhưng anh ta không thể đưa ra điều này trong tầm nhìn, anh ta phải thực hiện cuộc gọi ajax thứ 2
Andrew Bullock

Cảm ơn, ban đầu tôi đã làm điều này bằng cách sử dụng một cuộc gọi jQuery nhận json và đang lên kế hoạch để các phần tử HTML điền vào chúng từ json. Tuy nhiên, mẫu mà chúng tôi đang theo dõi ngay bây giờ là các chế độ xem của chúng tôi sẽ trả về một modelView và đó là cách dễ nhất để đưa vào các phần tử HTML cơ bản như các bảng, v.v. Tôi có thể gửi cùng một dữ liệu ở định dạng JSON như ViewData nhưng điều đó có vẻ lãng phí.
Chris Stephens

2
YOu KHÔNG nên nhúng JSON với Kết quả xem. OP cần phải tuân thủ tiêu chuẩn MVC được thực hành và trả về một mô hình hoặc viewmodel hoặc thực hiện AJAX. Hoàn toàn KHÔNG CÓ LÝ DO để nhúng JSON với một khung nhìn. Đó chỉ là mã rất bẩn. Câu trả lời này là cách chính xác và cách được đề xuất bởi Microsoft Practice. Các downvote là không cần thiết đây chắc chắn là câu trả lời chính xác. Chúng ta không nên khuyến khích thực hành mã hóa xấu. JSON qua AJAX hoặc Model thông qua Lượt xem. Không ai thích mã spaghetti với đánh dấu hỗn hợp!
Piotr Kula

Giải pháp này sẽ dẫn đến vấn đề sau: stackoverflow.com/questions/10543953/ Khăn
Jenny O'Reilly

2

@ Html.Raw (Json.Encode (object)) có thể được sử dụng để chuyển đổi View Modal Object thành JSON



0

Mở rộng câu trả lời tuyệt vời từ Dave . Bạn có thể tạo một HtmlHelper đơn giản .

public static IHtmlString RenderAsJson(this HtmlHelper helper, object model)
{
    return helper.Raw(Json.Encode(model));
}

Và theo quan điểm của bạn:

@Html.RenderAsJson(Model)

Bằng cách này, bạn có thể tập trung logic để tạo JSON nếu bạn, vì một số lý do, muốn thay đổi logic sau này.


0

Andrew đã có một phản hồi tuyệt vời nhưng tôi muốn làm mờ đi một chút. Cách khác biệt là tôi thích ModelView của mình không có dữ liệu trên đầu. Chỉ là dữ liệu cho đối tượng. Có vẻ như ViewData phù hợp với hóa đơn cho dữ liệu trên đầu, nhưng tất nhiên tôi mới biết điều này. Tôi đề nghị làm một cái gì đó như thế này.

Bộ điều khiển

virtual public ActionResult DisplaySomeWidget(int id)
{
    SomeModelView returnData = someDataMapper.getbyid(1);
    var serializer = new JavaScriptSerializer();
    ViewData["JSON"] = serializer.Serialize(returnData);
    return View(myview, returnData);
}

Lượt xem

//create base js object;
var myWidget= new Widget(); //Widget is a class with a public member variable called data.
myWidget.data= <%= ViewData["JSON"] %>;

Điều này làm cho bạn là nó cung cấp cho bạn cùng một dữ liệu trong JSON của bạn như trong ModelView để bạn có thể trả lại JSON cho bộ điều khiển của bạn và nó sẽ có tất cả các phần. Điều này tương tự với việc chỉ yêu cầu nó thông qua JSONRequest tuy nhiên nó yêu cầu một cuộc gọi ít hơn để nó tiết kiệm cho bạn chi phí đó. BTW này là sôi nổi cho Ngày nhưng dường như là một chủ đề khác.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.