39 votes

Exception de référence Json et circulaire

J'ai un objet qui a une forme circulaire de référence à un autre objet. Compte tenu de la relation entre ces objets c'est la bonne conception.

Pour Illustrer

Machine => Customer => Machine

Comme prévu je rencontre un problème lorsque j'essaie d'utiliser Json pour sérialiser une machine ou d'un client de l'objet. Ce que je ne sais pas comment résoudre ce problème car je ne veux pas briser la relation entre la Machine et les objets de Client. Quelles sont les options pour résoudre ce problème?

Modifier

Actuellement, je suis en utilisant Json méthode fournie par le Contrôleur de la classe de base. Donc, la sérialisation, je suis en train de faire est de base en tant que:

Json(machineForm);

58voto

Aaronaught Points 73049

Mise à jour:

N'essayez pas d'utiliser NonSerializedAttribute, comme l' JavaScriptSerializer apparemment l'ignore.

Au lieu de cela, utilisez l' ScriptIgnoreAttribute en System.Web.Script.Serialization.

public class Machine
{
    public string Customer { get; set; }

    // Other members
    // ...
}

public class Customer
{
    [ScriptIgnore]
    public Machine Machine { get; set; }    // Parent reference?

    // Other members
    // ...
}

De cette façon, quand vous jetez une Machine dans la Json méthode, il va traverser la relation de l' Machine de Customer mais ne va pas essayer de remonter à partir de Customer de Machine.

La relation est toujours là pour votre code pour faire ce que bon lui semble, mais l' JavaScriptSerializer (utilisé par l' Json méthode) l'ignore.

33voto

eudaimos Points 460

Je suis la réponse à ce en dépit de son âge, car il est le 3ème résultat (pour l'instant) de Google pour "json.encoder référence circulaire" et même si je ne suis pas d'accord avec les réponses (complètement) ci-dessus, que l'utilisation de la ScriptIgnoreAttribute suppose que vous n'allez pas n'importe où dans votre code voulez traverser la relation dans l'autre sens, pour certains JSON. Je ne crois pas que le verrouillage de votre modèle en raison d'un cas d'utilisation.

Il ne m'inspirent à utiliser cette solution simple.

Depuis que vous travaillez dans une Vue en MVC, vous avez le Modèle et que vous souhaitez simplement affecter le Modèle de la ViewData.Modèle au sein de votre contrôleur, aller de l'avant et à l'utilisation d'une requête LINQ au sein de votre point de Vue à aplatir les données bien supprimer le contenu de la circulaire de référence pour le JSON que vous voulez comme ceci:

var jsonMachines = from m in machineForm
                   select new { m.X, m.Y, // other Machine properties you desire
                                Customer = new { m.Customer.Id, m.Customer.Name, // other Customer properties you desire
                              }};
return Json(jsonMachines);

Ou si la Machine -> de la relation Client est 1..* -> * ensuite, essayez:

var jsonMachines = from m in machineForm
                   select new { m.X, m.Y, // other machine properties you desire
                                Customers = new List<Customer>(
                                               (from c in m.Customers
                                                select new Customer()
                                                {
                                                   Id = c.Id,
                                                   Name = c.Name,
                                                   // Other Customer properties you desire
                                                }).Cast<Customer>())
                               };
return Json(jsonMachines);

10voto

Thomas Points 786

Sur la base de la réponse de txl, vous devez désactiver le chargement différé et la création de proxy et vous pouvez utiliser les méthodes habituelles pour récupérer vos données.

Exemple:

 //Retrieve Items with Json:
public JsonResult Search(string id = "")
{
    db.Configuration.LazyLoadingEnabled = false;
    db.Configuration.ProxyCreationEnabled = false;

    var res = db.Table.Where(a => a.Name.Contains(id)).Take(8);

    return Json(res, JsonRequestBehavior.AllowGet);
}
 

4voto

GvS Points 28137

Utiliser pour avoir le même problème. J'ai créé une simple extension de la méthode, qui "aplatit" L2E objets dans un IDictionary. Un IDictionary est sérialisé correctement par le JavaScriptSerializer. Le résultant Json est le même que directement de la sérialisation de l'objet.

Depuis que je limite le niveau de la sérialisation, les références circulaires sont évités. Il permettra aussi de ne pas comprendre: 1->n les tables liées (Entitysets).

    private static IDictionary<string, object> JsonFlatten(object data, int maxLevel, int currLevel) {
        var result = new Dictionary<string, object>();
        var myType = data.GetType();
        var myAssembly = myType.Assembly;
        var props = myType.GetProperties();
        foreach (var prop in props) {
            // Remove EntityKey etc.
            if (prop.Name.StartsWith("Entity")) {
                continue;
            }
            if (prop.Name.EndsWith("Reference")) {
                continue;
            }
            // Do not include lookups to linked tables
            Type typeOfProp = prop.PropertyType;
            if (typeOfProp.Name.StartsWith("EntityCollection")) {
                continue;
            }
            // If the type is from my assembly == custom type
            // include it, but flattened
            if (typeOfProp.Assembly == myAssembly) {
                if (currLevel < maxLevel) {
                    result.Add(prop.Name, JsonFlatten(prop.GetValue(data, null), maxLevel, currLevel + 1));
                }
            } else {
                result.Add(prop.Name, prop.GetValue(data, null));
            }
        }

        return result;
    }
    public static IDictionary<string, object> JsonFlatten(this Controller controller, object data, int maxLevel = 2) {
        return JsonFlatten(data, maxLevel, 1);
    }

Ma méthode d'Action ressemble à ceci:

    public JsonResult AsJson(int id) {
        var data = Find(id);
        var result = this.JsonFlatten(data);
        return Json(result, JsonRequestBehavior.AllowGet);
    }

2voto

txl Points 21

Dans le Cadre de l'Entité de la version 4, il existe une option disponible: ObjectContextOptions.LazyLoadingEnabled

Paramètre à false devrait éviter la "référence circulaire" problème. Cependant, vous devrez charger explicitement les propriétés de navigation que vous souhaitez inclure.

voir: http://msdn.microsoft.com/en-us/library/bb896272.aspx

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X