2010-12-14 10 views
4

J'ai des problèmes pour recevoir des valeurs json de ma requête javascript/jQuery vers mon contrôleur.ASP.NET MVC2 Lecture de données JSON avec le contrôleur

MyClass se présente comme suit:

function MyClass() { 
    this.MyString = null; 
    this.MyInt = null; 
    this.Subs = null; 
} 

Ma demande se présente comme suit:

var testData1 = new MyClass(); 
testData1.MyInt = 1234; 
testData1.MyString = "abcDEF"; 
testData1.Subs = new Array(); 
var testData2 = new MyClass(); 
testData2.MyInt = 5678; 
testData2.MyString = "GHIjkl"; 
testData1.Subs.push(testData2); 
var jsonData = JSON.stringify(testData1); 
var self = this; 
$.ajax({ 
    url: '/Home/Request', 
    type: 'POST', 
    dataType: 'json', 
    data: jsonData, 
    contentType: 'application/json; charset=utf-8', 
    success: function (x) { 
     self.ParseResult(x); 
    } 
}); 

Maintenant, j'ai un contrôleur:

public JsonResult Request(MyClass myObj) 
{ 
    var answer = ... 
    return Json(answer, JsonRequestBehavior.DenyGet); 
} 

Avec la classe suivante:

public class MyClass 
{ 
    public string MyString { get; set; } 
    public int MyInt { get; set; } 
    public List<MyClass> Subs { get; set; } 
} 

Tous les noms dans jsonData sont exactement les mêmes que dans ma classe "MyClass". Mais il n'y a pas de valeurs dans myObj.

Où est le problème. Y at-il quelque chose que je peux faire pour que cette cartographie fonctionne correctement?

Merci beaucoup à l'avance,

Chris

MISE À JOUR:

Merci pour votre repley. J'ai utilisé le JavascriptSerializer. Mais j'ai le problème, que myString est null:

public JsonResult Data(string myString) 
{ 
    JavaScriptSerializer serializer = new JavaScriptSerializer(); 
    var data = serializer.Deserialize<MyClass>(myString); 
var answer = ... 
    return Json(answer, JsonRequestBehavior.DenyGet); 
} 

Où est la valeur? Devrais-je prendre la valeur à partir des données de demande?

Ward @ Dave

Vous deuxième solution fonctionne. Mais j'ai un problème avec les sous-marins.

var testData1 = new MyClass(); 
testData1.MyInt = 1234; 
testData1.MyString = "abcDEF"; 
testData1.Subs = new Array(); 
for (var i = 0; i < 10; i++) { 
    var testData2 = new MyClass(); 
    testData2.MyInt = i; 
    testData2.MyString = "abcDEF"; 
    testData1.Subs.push(testData2); 
} 

Je reçois 10 Subs dans mon contrôleur, mais tous sont vides. Que puis-je faire?

Ward @ Dave, @ALL

En utilisant les paramètres traditionnels mes 10 Subs sont bot vides, ils ne sont pas là. Le nombre d'abonnements est 0 (pas NULL). J'ai essayé de changer le type de sous de liste en IEnumerable mais cela n'a pas aidé. Savez-vous quoi que ce soit d'autre que je puisse faire pour que Subs remplisse mon contrôleur?

Ok, merci Dave Ward, je vais utiliser la méthode JSON.

Pour quelqu'un d'autre d'avoir le même problème ce code contrôleur pourrait être de l'aide:

MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(myString)); 
var serializer = new DataContractJsonSerializer(typeof(MyClass)); 
MyClass se = serializer.ReadObject(ms) as MyClass; 
ms.Close(); 

Répondre

3

En ce moment, vous envoyez la chaîne JSON comme le corps de votre POST. MVC n'a aucun moyen de connecter les points et de comprendre que vous vouliez qu'il fournisse cette chaîne entière en tant que paramètre nommé myString.

Pour ce faire, modifier vos paramètres de données côté client comme ceci:

$.ajax({ 
    url: '/Home/Request', 
    type: 'POST', 
    dataType: 'json', 
    data: { myString: jsonData }, 
    contentType: 'application/json; charset=utf-8', 
    success: function (x) { 
    // If ParseResult is some sort of JSON deserializing, don't do that. 
    // When the dataType is set to 'json', jQuery handles this for you 
    // automatically before the success handler is called. 
    self.ParseResult(x); 
    } 
}); 

Lorsque vous fournissez jQuery un objet en tant que paramètre de données, il URLEncodes automatiquement avant de l'envoyer, donc quelque chose comme ceci sera POSTé au serveur:

myString={json:data, here:etc} 

MVC le ramassez comme paramètre myString comme vous le souhaitez puis, et vous pouvez procéder à la désérialisation avec JavaScriptSerializer.

De plus, à moins que vous ne fassiez quelque chose de plus compliqué du côté client que ce que vous montrez, la classe JavaScript n'est pas nécessaire. Quelque chose comme cela fonctionnerait:

var testData2 = { 
    MyInt: 5678, 
    MyString: "GHIjkl", 
} 

var testData1 = { 
    MyInt: 1234, 
    MyString: "abcDEF", 
    Subs: [ testData2 ] 
} 

Cela dit, pourquoi utilisez-vous JSON pour la demande? Si vous acceptez un paramètre du type personnalisé dans votre action, le liant modèle MVC est assez bon hydratant ce type de données d'entrée URL encodée standard:

public JsonResult Data(MyClass request) 
{ 
    // request.MyInt, request.MyString, etc should exist here. 

    var answer = ... 

    // It's okay to accept URLEncoded input parameters, but still return JSON. 
    return Json(answer, JsonRequestBehavior.DenyGet); 
} 

Ensuite, l'appelant ne nécessite pas la sérialisation JSON côté client :

var testData2 = { 
    MyInt: 5678, 
    MyString: "GHIjkl", 
} 

var testData1 = { 
    MyInt: 1234, 
    MyString: "abcDEF", 
    Subs: [ testData2 ] 
} 

$.ajax({ 
    url: '/Home/Request', 
    type: 'POST', 
    traditional: true, 
    dataType: 'json', 
    data: testData1 
    success: function (x) { 
    self.ParseResult(x); 
    } 
}); 

Il est un peu plus simple tout autour, et est plus rapide puisque vous avez enlevé une couche de sérialisation sur le côté client et désérialisation sur le côté serveur.

+0

Merci pour votre réponse. Votre deuxième solution fonctionne très bien. Le seul problème que j'ai avec les "Subs". J'ai mis à jour mon poste original pour mieux formater mon message. – Chris

+0

Je crois que vous devrez également utiliser la sérialisation "traditionnelle" de jQuery. J'ai mis à jour le code $ .ajax() dans ma réponse. –

+0

Avec la sérialisation "traditionnelle", le nombre de sous-composants du contrôleur est 0. – Chris

1

Dans mon expérience je dois passer dans le JSON comme une chaîne et désérialiser il. J'utilise Newtonsoft pour ce faire.

public ActionResult DoAjax(string jsonRequest) 
{ 
    JsonSerializer serializer = new JsonSerializer(); 

    StringReader sr = new StringReader(jsonRequest); 
    Newtonsoft.Json.JsonTextReader reader = new JsonTextReader(sr); 

    MyClass obj = (MyClass)serializer.Deserialize(reader, typeof(MyClass)); 

    //...do other stuff 
} 
+1

Je suis d'accord avec cette approche. J'ajouterais que vous pouvez également utiliser le JavaScriptSerializer intégré dans .NET pour effectuer la désérialisation. Voir ce lien pour plus d'informations http://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptserializer.aspx –

+0

@Hector - très cool. Je ne savais pas qu'il y avait un sérialiseur intégré pour JSON. +1 pour m'apprendre quelque chose de nouveau. :) – Josh

+1

Je n'ai jamais eu à le faire, mais il y a quelques astuces autour de la désérialisation de MVC. Fondamentalement, si vous mélangez des ints, des décimales et des cordes, les choses peuvent devenir salissantes. Un int en tant que chaîne ("1") se liera à une chaîne ou int, mais une décimale en tant que chaîne ne sera désérialisée en une chaîne. Je ne pouvais pas obtenir "1.1" pour lier à une propriété décimale. Je n'ai aucune idée de comment l'équipe MVC manque ces cas d'utilisation. Il y a aussi des bizarreries autour des structures imbriquées, mais je ne me souviens pas du cas d'utilisation spécifique que j'ai rencontré. – Ryan

1

sur un mode similaire à celui de josh, vous pouvez utiliser un JSON ActionFilter à la:

// requires newtonsoft.json.dll 
public class JsonFilter : ActionFilterAttribute 
{ 
    public string Param { get; set; } 
    public Type JsonDataType { get; set; } 
    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     if (filterContext.HttpContext 
      .Request.ContentType.Contains("application/json")) 
     { 
      string inputContent; 
      using (var sr = new StreamReader(filterContext.HttpContext 
           .Request.InputStream)) 
      { 
       inputContent = sr.ReadToEnd(); 
      } 

      var result = JsonConvert.DeserializeObject(inputContent, JsonDataType); 
      filterContext.ActionParameters[Param] = result; 
     } 
     else 
      try 
      { 
       // we should do a loop looking for json here ONLY if there's a callback 
       // i.e. we're calling jsonP 
       if (filterContext.HttpContext.Request.QueryString["callback"] != null) 
       { 
        string inputContent = 
         Enumerable.Where(filterContext.HttpContext 
             .Request.QueryString.Keys.Cast<string>() 
             .Select(qs => filterContext.HttpContext 
             .Request.QueryString[qs]), query => !string.IsNullOrEmpty(query)) 
             .FirstOrDefault(query => query.IndexOf("{") == 0); 

        var result = JsonConvert.DeserializeObject(inputContent, JsonDataType); 
        filterContext.ActionParameters[Param] = result; 
       } 
      } 
      catch (Exception e) 
      { 
       // do nothing 
       filterContext.ActionParameters[Param] = null; 
      } 
    } 
} 
utilisation

(dans ce typeof cas (IList)), mais peut être toute catégorie à savoir typeof (myClass):

[JsonFilter(Param = "jsonData", JsonDataType = typeof(IList<FundPropertyWeekSplit>))] 
public virtual ActionResult AddFundPropertyWeekSplit(IList<FundPropertyWeekSplit> jsonData) 
{ 
    // code to deal with the newly created strongly typed object 
} 

-je utiliser ce (un peu trop !!) dans le magasin ...

+0

Très cool, Jim. Merci d'avoir partagé. – Josh

+0

josh - pas de soucis ... –

Questions connexes