J'essaie de faire une simple déclaration JSON, mais j'ai des problèmes. J'ai les éléments suivants ci-dessous.
public JsonResult GetEventData()
{
var data = Event.Find(x => x.ID != 0);
return Json(data);
}
Je reçois un HTTP 500 à l'exception de ce qui est indiqué dans le titre de cette question. J'ai aussi essayé
var data = Event.All().ToList()
Cela a donné le même problème.
Est-ce un bug ou mon implémentation?
Il semble qu'il existe des références circulaires dans votre hiérarchie d'objets qui ne sont pas prises en charge par le sérialiseur JSON. Avez-vous besoin de toutes les colonnes? Vous pouvez sélectionner uniquement les propriétés dont vous avez besoin dans la vue:
return Json(new
{
PropertyINeed1 = data.PropertyINeed1,
PropertyINeed2 = data.PropertyINeed2
});
Cela rendra votre objet JSON plus léger et plus facile à comprendre. Si vous avez plusieurs propriétés, AutoMapper peut être utilisé pour automatiquement mapper entre les objets DTO et les objets View.
J'ai eu le même problème et résolu par using Newtonsoft.Json;
var list = JsonConvert.SerializeObject(model,
Formatting.None,
new JsonSerializerSettings() {
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
});
return Content(list, "application/json");
Cela se produit réellement parce que les objets complexes sont ce qui fait que l'objet json résultant échoue. Et cela échoue car lorsque l'objet est mappé, il mappe les enfants, ce qui mappe leurs parents, en faisant une référence circulaire. Json prendrait un temps infini pour le sérialiser, il évite donc le problème avec l'exception.
Le mappage Entity Framework produit également le même comportement, et la solution consiste à supprimer toutes les propriétés indésirables.
Juste en expliquant la réponse finale, le code entier serait:
public JsonResult getJson()
{
DataContext db = new DataContext ();
return this.Json(
new {
Result = (from obj in db.Things select new {Id = obj.Id, Name = obj.Name})
}
, JsonRequestBehavior.AllowGet
);
}
Cela pourrait également être le cas si vous ne voulez pas que les objets se trouvent dans une propriété Result
:
public JsonResult getJson()
{
DataContext db = new DataContext ();
return this.Json(
(from obj in db.Things select new {Id = obj.Id, Name = obj.Name})
, JsonRequestBehavior.AllowGet
);
}
Pour résumer, il y a 4 solutions à cela:
Solution 1: désactivez ProxyCreation pour le DBContext et restaurez-le à la fin.
private DBEntities db = new DBEntities();//dbcontext
public ActionResult Index()
{
bool proxyCreation = db.Configuration.ProxyCreationEnabled;
try
{
//set ProxyCreation to false
db.Configuration.ProxyCreationEnabled = false;
var data = db.Products.ToList();
return Json(data, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(ex.Message);
}
finally
{
//restore ProxyCreation to its original state
db.Configuration.ProxyCreationEnabled = proxyCreation;
}
}
Solution 2: utilisation de JsonConvert en définissant ReferenceLoopHandling pour qu'il ignore les paramètres du sérialiseur.
//using using Newtonsoft.Json;
private DBEntities db = new DBEntities();//dbcontext
public ActionResult Index()
{
try
{
var data = db.Products.ToList();
JsonSerializerSettings jss = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
var result = JsonConvert.SerializeObject(data, Formatting.Indented, jss);
return Json(result, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(ex.Message);
}
}
Les deux solutions suivantes sont identiques, mais utiliser un modèle est préférable car il est typé fort.
Solution 3: renvoyez un modèle contenant uniquement les propriétés nécessaires.
private DBEntities db = new DBEntities();//dbcontext
public class ProductModel
{
public int Product_ID { get; set;}
public string Product_Name { get; set;}
public double Product_Price { get; set;}
}
public ActionResult Index()
{
try
{
var data = db.Products.Select(p => new ProductModel
{
Product_ID = p.Product_ID,
Product_Name = p.Product_Name,
Product_Price = p.Product_Price
}).ToList();
return Json(data, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(ex.Message);
}
}
Solution 4: renvoyer un nouvel objet dynamique contenant uniquement les propriétés nécessaires.
private DBEntities db = new DBEntities();//dbcontext
public ActionResult Index()
{
try
{
var data = db.Products.Select(p => new
{
Product_ID = p.Product_ID,
Product_Name = p.Product_Name,
Product_Price = p.Product_Price
}).ToList();
return Json(data, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(ex.Message);
}
}
JSON, comme XML et divers autres formats, est un format de sérialisation basé sur une arborescence. Il ne vous aimera pas si vous avez des références circulaires dans vos objets, comme le serait "l'arbre":
root B => child A => parent B => child A => parent B => ...
Il existe souvent des moyens de désactiver la navigation sur un certain chemin. Par exemple, avec XmlSerializer
, vous pouvez marquer la propriété parent comme XmlIgnore
. Je ne sais pas si cela est possible avec le sérialiseur json en question, ni si DatabaseColumn
dispose de marqueurs appropriés (très peu probable, car il faudrait référencer chaque API de sérialisation).
ajoutez [JsonIgnore]
aux propriétés de la machine virtuelle de votre modèle.
Cela est dû au nouveau modèle DbContext T4 utilisé pour générer les entités EntityFramework. Pour pouvoir effectuer le suivi des modifications, ces modèles utilisent le modèle Proxy, en encapsulant vos POCO de Nice. Cela provoque alors les problèmes lors de la sérialisation avec JavaScriptSerializer.
Alors les 2 solutions sont:
Vous pouvez désactiver la génération automatique de mandataires en la définissant dans la configuration du contexte.
context.Configuration.ProxyCreationEnabled = false;
Très bien expliqué dans l'article ci-dessous.
http://juristr.com/blog/2011/08/javascriptserializer-circular-reference/
Évitez de convertir l'objet table directement. Si des relations sont définies entre d'autres tables, cette erreur peut être générée. Vous pouvez plutôt créer une classe de modèle, affecter des valeurs à l'objet de classe, puis le sérialiser.
Utilisation de Newtonsoft.Json: Dans votre méthode Global.asax Application_Start, ajoutez cette ligne:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
Les réponses fournies sont bonnes, mais je pense qu’elles peuvent être améliorées en ajoutant une perspective "architecturale".
Enquête
La fonction MVC's Controller.Json
fait le travail, mais elle est très mauvaise pour fournir une erreur pertinente dans ce cas. En utilisant Newtonsoft.Json.JsonConvert.SerializeObject
, l'erreur spécifie exactement quelle est la propriété qui déclenche la référence circulaire. Ceci est particulièrement utile lors de la sérialisation de hiérarchies d'objets plus complexes.
Architecture appropriée
Il ne faut jamais essayer de sérialiser des modèles de données (par exemple, des modèles EF), car les propriétés de navigation de l'ORM constituent le chemin de la perdition en matière de sérialisation. Le flux de données devrait être le suivant:
Database -> data models -> service models -> JSON string
Les modèles de service peuvent être obtenus à partir de modèles de données à l'aide de mappeurs automatiques (par exemple, Automapper ). Bien que cela ne garantisse pas le manque de références circulaires, une conception appropriée devrait le faire: les modèles de service devraient contenir exactement ce que le consommateur de service requiert (c'est-à-dire les propriétés).
Dans les rares cas où le client demande une hiérarchie impliquant le même type d'objet à différents niveaux, le service peut créer une structure linéaire avec une relation parent-> enfant (en utilisant uniquement des identificateurs, pas des références).
Les applications modernes ont tendance à éviter de charger simultanément des structures de données complexes et les modèles de service doivent être minces. Par exemple.:
J'utilise le correctif, car utiliser Knockout dans les vues MVC5.
En action
return Json(ModelHelper.GetJsonModel<Core_User>(viewModel));
une fonction
public static TEntity GetJsonModel<TEntity>(TEntity Entity) where TEntity : class
{
TEntity Entity_ = Activator.CreateInstance(typeof(TEntity)) as TEntity;
foreach (var item in Entity.GetType().GetProperties())
{
if (item.PropertyType.ToString().IndexOf("Generic.ICollection") == -1 && item.PropertyType.ToString().IndexOf("SaymenCore.DAL.") == -1)
item.SetValue(Entity_, Entity.GetPropValue(item.Name));
}
return Entity_;
}
Vous pouvez remarquer les propriétés à l'origine de la référence circulaire. Ensuite, vous pouvez faire quelque chose comme:
private Object DeCircular(Object object)
{
// Set properties that cause the circular reference to null
return object
}