Bagaimana cara mengubah View Model menjadi objek JSON di ASP.NET MVC?

156

Saya adalah pengembang Java, baru mengenal .NET. Saya sedang mengerjakan proyek .NET MVC2 di mana saya ingin memiliki tampilan parsial untuk membungkus widget. Setiap objek widget JavaScript memiliki objek data JSON yang akan diisi oleh data model. Kemudian metode untuk memperbarui data ini terikat dengan peristiwa ketika data diubah di widget atau jika data itu diubah di widget lain.

Kodenya kira-kira seperti ini:

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>

Apa yang saya tidak tahu adalah bagaimana mengirim data sebagai SomeModelViewdan kemudian dapat menggunakannya untuk mengisi widget serta mengubahnya menjadi JSON. Saya telah melihat beberapa cara sederhana untuk melakukannya di controller tetapi tidak dalam tampilan. Saya pikir ini adalah pertanyaan mendasar, tetapi saya sudah mencoba selama beberapa jam untuk menjadikannya apik.

Chris Stephens
sumber
1
Saya tahu ini adalah pertanyaan lama. Tetapi sampai hari ini ada cara yang lebih baik untuk melakukannya. Jangan gabungkan JSON inline dengan hasil Lihat Anda. JSON adalah serial dengan mudah melalui AJAX dan dapat diperlakukan seperti objek. Apa pun yang ada di JavaScript harus terpisah dari Tampilan. Anda dapat dengan mudah mengembalikan model tanpa upaya apa pun melalui Kontroler.
Piotr Kula

Jawaban:

346

Di MVC3 dengan pisau cukur @Html.Raw(Json.Encode(object))tampaknya melakukan trik.

Dave
sumber
1
+1 Saya menggunakan Html.Raw, tetapi tidak pernah menemukan Json.Encode dan hanya menggunakan JavaScriptSerializer untuk menambahkan string di controller ke model tampilan
AaronLS
5
Pendekatan ini berfungsi bahkan ketika Anda ingin meneruskan JSON yang dihasilkan ke Javascript. Razor mengeluh dengan squiggles hijau jika Anda meletakkan kode @ Html.Raw (...) sebagai parameter fungsi di dalam tag <script>, tetapi JSON memang membuatnya ke JS yang dipanggil. Sangat praktis & apik. +1
Carl Heinrich Hancke
1
Ini adalah cara untuk melakukannya sejak MVC3 dan harus dikembalikan dari pengontrol. Jangan menanamkan json ke dalam Viewresult Anda. Alih-alih membuat pengontrol untuk menangani JSON secara terpisah dan melakukan permintaan JSON melalui AJAX. Jika Anda memerlukan JSON pada tampilan Anda melakukan sesuatu yang salah. Baik menggunakan ViewModel atau yang lainnya.
Piotr Kula
3
Json.Encode mengkodekan array 2 dimensi saya menjadi array 1 dimensi di json. Newtonsoft.Json.JsonConvert.SerializeObject membuat serial dua dimensi dengan benar menjadi json. Jadi saya sarankan untuk menggunakan yang terakhir.
mono68
12
Saat menggunakan MVC 6 (mungkin juga 5) Anda harus menggunakan Json.Serializealih-alih Encode.
KoalaBear
31

Bagus sekali, Anda baru saja mulai menggunakan MVC dan Anda telah menemukan cacat besar pertamanya.

Anda tidak benar-benar ingin mengubahnya menjadi JSON dalam tampilan, dan Anda tidak benar-benar ingin mengubahnya di controller, karena tidak satu pun dari lokasi ini masuk akal. Sayangnya, Anda terjebak dengan situasi ini.

Hal terbaik yang saya temukan lakukan adalah mengirim JSON ke tampilan dalam ViewModel, seperti ini:

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

return View("viewname", viewModel);

lalu gunakan

<%= Model.JsonData %>

menurut Anda. Ketahuilah bahwa standar .NET JavaScriptSerializer cukup jelek.

melakukannya di pengontrol setidaknya membuatnya dapat diuji (walaupun tidak persis seperti di atas - Anda mungkin ingin mengambil ISerializer sebagai dependensi sehingga Anda dapat mengejeknya)

Perbarui juga, mengenai JavaScript Anda, itu akan menjadi praktik yang baik untuk membungkus SEMUA widget JS yang Anda miliki di atas seperti:

(
    // all js here
)();

dengan cara ini jika Anda meletakkan banyak widget di sebuah halaman, Anda tidak akan mendapatkan konflik (kecuali jika Anda perlu mengakses metode dari tempat lain di halaman, tetapi dalam hal ini Anda harus mendaftarkan widget dengan beberapa kerangka kerja widget tetap). Ini mungkin tidak menjadi masalah sekarang, tetapi akan menjadi praktik yang baik untuk menambahkan tanda kurung sekarang untuk menyelamatkan diri Anda dari banyak upaya di masa depan ketika itu menjadi persyaratan, itu juga praktik OO yang baik untuk merangkum fungsionalitas.

Andrew Bullock
sumber
1
Ini terlihat menarik. Saya hanya akan membuat salinan data sebagai json dan meneruskannya sebagai viewData tetapi dengan cara ini terlihat lebih menarik. Saya akan bermain dengan ini dan memberi tahu Anda. BTW ini adalah pertama kalinya saya memposting pertanyaan tentang stackoverflow dan butuh apa. 5 menit untuk mendapatkan respons yang baik, itu luar biasa !!
Chris Stephens
tidak ada yang salah dengan itu, itu hanya tidak dapat dikustomisasi, jika Anda ingin memformat nilai kustom Anda harus melakukannya terlebih dahulu, pada dasarnya membuat semuanya string :( ini paling menonjol dengan tanggal. Saya tahu ada solusi yang mudah, tetapi mereka tidak boleh diperlukan!
Andrew Bullock
@ Perbarui Saya akan melihat tentang menggunakan penghitung statis bersih dari widget itu untuk menghasilkan nama objek yang unik. Tetapi apa yang Anda tulis kelihatannya akan melakukan hal yang sama dan melakukannya lebih lambat. Juga saya memang melihat ke dalam mengikat pembuatan widget "new_widget_event 'ke objek tingkat halaman untuk melacak mereka tetapi alasan asli untuk melakukan itu menjadi OBE. Jadi saya mungkin meninjau kembali nanti. Terima kasih atas semua bantuan yang akan saya posting versi modifikasi dari apa yang Anda sarankan, tetapi jelaskan mengapa saya menyukainya lebih baik
Chris Stephens
2
saya menarik kembali pernyataan saya sebelumnya "tidak ada yang salah dengan itu". Ada yang salah dengan itu.
Andrew Bullock
Mengapa Anda mengatakan bahwa kami tidak dapat mengembalikan JSON dari controller. Itulah yang seharusnya dilakukan. Menggunakan JavaScriptSerializeratau Return Json(object)keduanya menggunakan serializers yang sama. Juga memposting JSON yang sama kembali ke controller akan membangun kembali objek untuk Anda selama Anda mendefinisikan model yang benar. Mungkin selama MVC2 itu adalah kelemahan utama .. tapi hari ini sangat mudah dan nyaman. Anda harus memperbarui jawaban Anda untuk mencerminkan ini.
Piotr Kula
18

Saya merasa cukup baik melakukannya seperti ini (penggunaan dalam tampilan):

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

Berikut ini adalah kelas ekstensi metode helper menurut:

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

Ini bukan super-sofanggih, tetapi memecahkan masalah di mana harus meletakkannya (dalam Pengontrol atau dalam tampilan?) Jawabannya jelas: tidak;)

Wolfgang
sumber
Ini bagus dan bersih menurut saya dan dibungkus dengan pembantu yang bisa digunakan kembali. Cheers, J
John
6

Anda dapat menggunakan Jsondari tindakan secara langsung,

Tindakan Anda akan menjadi seperti ini:

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

Edit

Hanya melihat bahwa Anda menganggap ini adalah Modeltampilan sehingga di atas tidak sepenuhnya benar, Anda harus membuat Ajaxpanggilan ke metode controller untuk mendapatkan ini, maka ascxtidak akan memiliki model sendiri, saya akan meninggalkan kode saya kalau-kalau itu berguna bagi Anda dan Anda dapat mengubah panggilan

Edit 2 cukup masukkan id ke dalam kode

Pharabus
sumber
1
tapi dia tidak bisa membuat ini menjadi tampilan, dia harus membuat panggilan ajax ke-2
Andrew Bullock
Terima kasih, saya awalnya melakukan ini menggunakan jQuery get json call dan berencana untuk membuat elemen HTML mengisi mereka dari json. Namun, pola yang kami ikuti saat ini adalah bahwa pandangan kami harus mengembalikan modelView dan itu adalah cara termudah untuk mengisi elemen HTML dasar seperti tabel dll. Saya bisa mengirim data yang sama dalam format JSON seperti ViewData tetapi tampaknya boros.
Chris Stephens
2
ANDA TIDAK boleh menyematkan JSON dengan Hasil Tampilan. OP harus tetap menggunakan MVC standar yang dipraktikkan dan mengembalikan model atau model tampilan atau melakukan AJAX. Sama sekali TANPA ALASAN untuk menyematkan JSON dengan tampilan. Itu hanya kode yang sangat kotor. Jawaban ini adalah cara yang benar dan cara yang direkomendasikan oleh Praktik Microsoft. Downvote tidak perlu ini jelas merupakan jawaban yang benar. Kita seharusnya tidak mendorong praktik pengkodean yang buruk. Entah JSON melalui AJAX atau Model melalui Tampilan. Tidak ada yang suka kode spageti dengan markup campuran!
Piotr Kula
Solusi ini akan menghasilkan masalah berikut: stackoverflow.com/questions/10543953/…
Jenny O'Reilly
2

@ Html.Raw (Json.Encode (objek)) dapat digunakan untuk mengonversi View Modal Object ke JSON

Priya Payyavula
sumber
1

Memperluas jawaban yang bagus dari Dave . Anda dapat membuat HtmlHelper sederhana .

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

Dan menurut Anda:

@Html.RenderAsJson(Model)

Dengan cara ini Anda dapat memusatkan logika untuk membuat JSON jika Anda, karena suatu alasan, ingin mengubah logika nanti.

smoksnes
sumber
0

Andrew memiliki respons yang hebat tetapi saya ingin sedikit mengubahnya. Cara ini berbeda adalah bahwa saya suka ModelViews saya tidak memiliki data overhead di dalamnya. Hanya data untuk objek. Tampaknya ViewData sesuai dengan tagihan untuk data di atas kepala, tapi tentu saja saya baru dalam hal ini. Saya sarankan melakukan sesuatu seperti ini.

Pengendali

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

Melihat

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

Apa ini bagi Anda adalah memberi Anda data yang sama di JSON Anda seperti di ModelView Anda sehingga Anda berpotensi mengembalikan JSON kembali ke controller Anda dan itu akan memiliki semua bagian. Ini mirip dengan hanya memintanya melalui JSONRequest namun itu membutuhkan satu panggilan lebih sedikit sehingga menghemat overhead Anda. BTW ini funky untuk Tanggal tetapi sepertinya utas lainnya.

Chris Stephens
sumber