วิธีการแปลงมุมมองแบบจำลองเป็นวัตถุ JSON ใน ASP.NET MVC


156

ฉันเป็นผู้พัฒนา Java, ใหม่สำหรับ. NET ฉันกำลังทำงานกับโครงการ. NET MVC2 ที่ฉันต้องการให้มีมุมมองบางส่วนเพื่อห่อวิดเจ็ต วัตถุวิดเจ็ต JavaScript แต่ละตัวมีวัตถุข้อมูล JSON ที่จะถูกเติมด้วยข้อมูลแบบจำลอง ดังนั้นวิธีการอัปเดตข้อมูลนี้จะถูกผูกไว้กับเหตุการณ์เมื่อมีการเปลี่ยนแปลงข้อมูลในวิดเจ็ตหรือหากข้อมูลนั้นมีการเปลี่ยนแปลงในวิดเจ็ตอื่น

รหัสเป็นดังนี้:

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>

สิ่งที่ฉันไม่ทราบคือวิธีการส่งข้อมูลไปเป็นSomeModelViewแล้วจากนั้นสามารถใช้เพื่อเติมวิดเจ็ตรวมทั้งแปลงเป็น JSON ฉันได้เห็นวิธีง่าย ๆ จริง ๆ ในการควบคุม แต่ไม่ใช่ในมุมมอง ฉันคิดว่านี่เป็นคำถามพื้นฐาน แต่ฉันพยายามทำไปสักสองสามชั่วโมง


1
ฉันรู้ว่านี่เป็นคำถามเก่า แต่ ณ วันนี้มีวิธีที่ดีกว่าที่จะทำ อย่าผสม JSON แบบอินไลน์กับผลลัพธ์การดูของคุณ JSON เป็นอนุกรมได้อย่างง่ายดายผ่าน AJAX และสามารถปฏิบัติเหมือนเป็นวัตถุ ทุกสิ่งใน JavaScript ควรแยกออกจากมุมมอง คุณสามารถส่งคืนโมเดลได้อย่างง่ายดายโดยไม่ต้องใช้ความพยายามใด ๆ ผ่านทางคอนโทรลเลอร์
Piotr Kula

คำตอบ:


346

ใน mvc3 ด้วยมีดโกน@Html.Raw(Json.Encode(object))ดูเหมือนว่าจะทำเคล็ดลับ


1
+1 ฉันใช้ Html.Raw แต่ไม่เคยพบ Json.Encode และเพิ่งใช้ JavaScriptSerializer เพื่อเพิ่มสตริงในตัวควบคุมในรูปแบบมุมมอง
AaronLS

5
วิธีการนี้ใช้งานได้แม้ว่าคุณต้องการส่ง JSON ที่เป็นผลลัพธ์ไปยัง Javascript มีดโกนบ่นกับ squiggles สีเขียวถ้าคุณใส่รหัส @ Html.Raw (... ) เป็นพารามิเตอร์ฟังก์ชั่นภายในแท็ก <script> แต่ JSON ทำให้มันเป็น JS ที่แน่นอน มีประโยชน์มาก & เนียน +1
Carl Heinrich Hancke

1
นี่เป็นวิธีที่ทำได้ตั้งแต่ MVC3 และควรส่งคืนจากคอนโทรลเลอร์ อย่าฝัง json ลงใน Viewresult ของคุณ สร้างคอนโทรลเลอร์เพื่อจัดการ JSON แยกจากกันและทำการร้องขอ JSON ผ่าน AJAX หากคุณต้องการ JSON ในมุมมองคุณกำลังทำอะไรผิดพลาด ใช้ ViewModel หรืออย่างอื่น
Piotr Kula

3
Json.Encodeเข้ารหัสอาร์เรย์ 2 มิติของฉันเป็นอาร์เรย์ 1 มิติใน json Newtonsoft.Json.JsonConvert.SerializeObjectทำให้อนุกรมเป็นสองมิติอย่างเหมาะสมใน json ดังนั้นฉันแนะนำให้ใช้อันหลัง
mono68

12
เมื่อใช้ MVC 6 (อาจเป็น 5) คุณต้องใช้Json.Serializeแทน Encode
KoalaBear

31

ทำได้ดีมากคุณเพิ่งเริ่มใช้ MVC และคุณพบข้อบกพร่องที่สำคัญครั้งแรก

คุณไม่ต้องการแปลงเป็น JSON ในมุมมองจริง ๆ และคุณไม่ต้องการแปลงเป็นตัวควบคุมเนื่องจากตำแหน่งเหล่านี้ไม่เหมาะสม น่าเสียดายที่คุณติดอยู่กับสถานการณ์นี้

สิ่งที่ดีที่สุดที่ฉันพบว่าทำคือส่ง JSON ไปยังมุมมองใน ViewModel เช่นนี้:

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

return View("viewname", viewModel);

จากนั้นใช้

<%= Model.JsonData %>

ในมุมมองของคุณ ระวังว่า. NET JavaScriptSerializer มาตรฐานเป็นเรื่องน่าสนใจ

การทำมันในคอนโทรลเลอร์อย่างน้อยทำให้สามารถทดสอบได้ (แม้ว่าจะไม่เหมือนด้านบน - คุณอาจต้องการใช้ ISerializer เป็นการพึ่งพาเพื่อให้คุณสามารถเยาะเย้ย)

อัปเดตเกี่ยวกับ JavaScript ของคุณจะเป็นการดีถ้าจะห่อวิดเจ็ต JS ทั้งหมดที่คุณมีด้านบน:

(
    // all js here
)();

ด้วยวิธีนี้หากคุณใส่วิดเจ็ตหลายรายการในหน้าหนึ่งคุณจะไม่ได้รับข้อขัดแย้ง (เว้นแต่คุณจะต้องเข้าถึงวิธีการจากที่อื่นในหน้า แต่ในกรณีนี้คุณควรลงทะเบียนวิดเจ็ตด้วยกรอบเครื่องมือบางส่วนอยู่แล้ว) มันอาจจะไม่เป็นปัญหาในตอนนี้ แต่มันเป็นแนวปฏิบัติที่ดีในการเพิ่มวงเล็บตอนนี้เพื่อประหยัดความพยายามของตัวเองในอนาคตเมื่อมันกลายเป็นความต้องการมันเป็นแนวปฏิบัติที่ดีของ OO ที่จะห่อหุ้มฟังก์ชั่น


1
มันดูน่าสนใจ ฉันจะทำสำเนาข้อมูลเป็น json และส่งผ่านเป็น viewData แต่วิธีนี้ดูน่าสนใจยิ่งขึ้น ฉันจะเล่นกับสิ่งนี้และแจ้งให้คุณทราบ BTW นี่เป็นครั้งแรกที่ฉันโพสต์คำถามบน stackoverflow และใช้เวลาประมาณ 5 นาทีในการรับคำตอบที่ดีนั่นยอดเยี่ยมมาก !!
Chris Stephens

ไม่มีอะไรผิดปกติกับมันมันไม่สามารถปรับแต่งได้หากคุณต้องการการจัดรูปแบบค่าที่กำหนดเองใด ๆ ที่คุณต้องทำก่อนที่มือจะทำให้ทุกอย่างสตริง :( นี่คือที่โดดเด่นที่สุดกับวันที่ฉันรู้ว่ามีวิธีแก้ปัญหาง่าย ๆ จำเป็น!
Andrew Bullock

@ อัปเดตฉันจะดูเกี่ยวกับการใช้. net static counter ของวิดเจ็ตนั้นเพื่อสร้างชื่อวัตถุที่ไม่ซ้ำกัน แต่สิ่งที่คุณเขียนดูเหมือนว่ามันจะประสบความสำเร็จในสิ่งเดียวกันและทำมันสั่น นอกจากนี้ฉันได้พิจารณาการสร้างวิดเจ็ต "new_widget_event" กับวัตถุระดับหน้าเพื่อติดตาม แต่เหตุผลดั้งเดิมสำหรับการทำเช่นนั้นกลายเป็น OBE ดังนั้นฉันอาจกลับมาใหม่ในภายหลังขอบคุณสำหรับความช่วยเหลือทั้งหมดที่ฉันจะโพสต์ รุ่นที่ดัดแปลงจากสิ่งที่คุณแนะนำ แต่ให้อธิบายว่าทำไมฉันถึงชอบมากกว่านี้
Chris Stephens

2
ฉันเพิกถอนคำสั่งก่อนหน้าของฉัน "ไม่มีอะไรผิดปกติกับมัน" มีทุกอย่างผิดปกติกับมัน
Andrew Bullock

ทำไมคุณถึงบอกว่าเราไม่สามารถส่งคืน JSON จากคอนโทรลเลอร์ นั่นคือวิธีที่ควรจะทำ ใช้JavaScriptSerializerหรือReturn Json(object)ทั้งสองใช้ serializers เดียวกัน นอกจากนี้การโพสต์ JSON เดียวกันกลับไปที่ตัวควบคุมจะสร้างวัตถุให้คุณตราบใดที่คุณกำหนดรูปแบบที่ถูกต้อง บางทีในช่วง MVC2 มันเป็นข้อเสียเปรียบครั้งใหญ่ .. แต่วันนี้มันง่ายและสะดวกมาก คุณควรอัปเดตคำตอบเพื่อสะท้อนถึงสิ่งนี้
Piotr Kula

18

ฉันพบว่ามันน่ารักดีที่ทำเช่นนี้ (การใช้งานในมุมมอง):

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

นี่คือวิธีการขยายระดับผู้ช่วยตาม:

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

มันไม่ได้มีความซับซ้อนมาก แต่มันแก้ปัญหาที่จะวางไว้ (ในตัวควบคุมหรือในมุมมอง?) คำตอบที่เห็นได้ชัดคือ: ไม่;


นี่เป็นสิ่งที่ดีและสะอาดในความคิดของฉันและห่อด้วยตัวช่วยนำมาใช้ซ้ำได้ ไชโย, J
จอห์น

6

คุณสามารถใช้Jsonจากการกระทำโดยตรง

การกระทำของคุณจะเป็นดังนี้:

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

แก้ไข

เพิ่งเห็นว่าคุณคิดว่านี่เป็นModelมุมมองดังนั้นข้างต้นไม่ถูกต้องอย่างเคร่งครัดคุณจะต้องAjaxโทรไปที่วิธีการควบคุมเพื่อรับสิ่งนี้แล้วascxจะไม่มีรุ่นต่อ se ฉันจะทิ้งโค้ดไว้ ในกรณีที่เป็นประโยชน์กับคุณและคุณสามารถแก้ไขการโทรได้

แก้ไข 2 เพียงใส่รหัสลงในรหัส


1
แต่เขาไม่สามารถทำให้เรื่องนี้เป็นมุมมองได้เขาจะต้องโทรหาอาแจ็กซ์คนที่ 2
Andrew Bullock

ขอบคุณเดิมฉันทำสิ่งนี้โดยใช้ jQuery รับ json call และวางแผนที่จะให้องค์ประกอบ HTML เติมองค์ประกอบเหล่านั้นจาก json อย่างไรก็ตามรูปแบบที่เรากำลังติดตามอยู่ในขณะนี้คือมุมมองของเราควรส่งคืน modelView และเป็นวิธีที่ง่ายที่สุดในการเติมองค์ประกอบ HTML พื้นฐานเช่นตาราง ฯลฯ ฉันสามารถส่งข้อมูลเดียวกันในรูปแบบ JSON เป็น ViewData แต่ดูเหมือนสิ้นเปลือง
Chris Stephens

2
คุณไม่ควรฝัง JSON ด้วยผลการดู OP ต้องการยึดติดกับ MVC มาตรฐานที่ได้รับการฝึกฝนแล้วส่งคืนแบบจำลองหรือมุมมองหรือทำ AJAX ไม่มีเหตุผลอย่างแน่นอนที่จะฝัง JSON ด้วยมุมมอง นั่นเป็นรหัสที่สกปรกมาก คำตอบนี้เป็นวิธีที่ถูกต้องและวิธีที่แนะนำโดย Microsoft Practices downvote ไม่จำเป็นนี่เป็นคำตอบที่ถูกต้องแน่นอน เราไม่ควรส่งเสริมวิธีการเขียนโปรแกรมที่ไม่ดี ทั้ง JSON ผ่าน AJAX หรือโมเดลผ่าน Views ไม่มีใครชอบรหัสสปาเก็ตตี้ที่มีมาร์กอัปผสม!
Piotr Kula

การแก้ปัญหานี้จะส่งผลให้เกิดปัญหาดังต่อไปนี้: stackoverflow.com/questions/10543953/…
Jenny O'Reilly


1

ขยายคำตอบที่ดีจากเดฟ คุณสามารถสร้างง่ายHtmlHelper

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

และในมุมมองของคุณ:

@Html.RenderAsJson(Model)

วิธีนี้คุณสามารถรวมตรรกะในการสร้าง JSON ได้ด้วยเหตุผลบางประการหากคุณต้องการเปลี่ยนตรรกะในภายหลัง



0

แอนดรูว์ได้รับการตอบรับที่ดี แต่ฉันต้องการที่จะพูดเกินจริงเล็กน้อย วิธีที่แตกต่างคือฉันชอบ ModelViews ของฉันไม่มีข้อมูลค่าใช้จ่าย เพียงแค่ข้อมูลสำหรับวัตถุ ดูเหมือนว่า ViewData จะเหมาะกับการเรียกเก็บเงินสำหรับข้อมูลส่วนหัว แต่แน่นอนว่าฉันใหม่สำหรับเรื่องนี้ ฉันแนะนำให้ทำอะไรแบบนี้

ตัวควบคุม

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

ดู

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

สิ่งนี้ทำเพื่อคุณคือให้ข้อมูลเหมือนกันใน JSON ของคุณใน ModelView ของคุณเพื่อให้คุณสามารถส่งคืน JSON กลับไปที่คอนโทรลเลอร์ของคุณและมันจะมีชิ้นส่วนทั้งหมด สิ่งนี้คล้ายกับเพียงแค่ขอผ่านทาง JSONRequest แต่จะต้องใช้การโทรที่น้อยลงหนึ่งรายการเพื่อช่วยให้คุณประหยัดค่าใช้จ่าย BTW นี้เป็นขี้ขลาดสำหรับวันที่ แต่ดูเหมือนว่าหัวข้ออื่น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.