Исключение Json и Circular Reference - PullRequest
39 голосов
/ 05 января 2010

У меня есть объект, который имеет круговую ссылку на другой объект. Учитывая отношения между этими объектами, это правильный дизайн.

Для иллюстрации

Machine => Customer => Machine

Как и ожидалось, я столкнулся с проблемой при попытке использовать Json для сериализации компьютера или объекта заказчика. В чем я не уверен, так это в том, как решить эту проблему, поскольку я не хочу нарушать отношения между объектами Machine и Customer. Какие есть варианты решения этой проблемы?

Редактировать

В настоящее время я использую метод Json, предоставляемый базовым классом контроллера . Так что сериализация, которую я делаю, так же проста, как:

Json(machineForm);

Ответы [ 9 ]

57 голосов
/ 05 января 2010

Обновление:

Не пытайтесь использовать NonSerializedAttribute, поскольку JavaScriptSerializer явно игнорирует его.

Вместо этого используйте ScriptIgnoreAttribute in 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
    // ...
}

Таким образом, когда вы вбрасываете Machine в метод Json, он пересекает отношение от Machine до Customer, но не пытается вернуться от Customer к Machine.

Отношения по-прежнему существуют для вашего кода, чтобы он выполнял, как пожелает, но JavaScriptSerializer (используемый методом Json) будет игнорировать его.

33 голосов
/ 14 апреля 2011

Я отвечаю на это, несмотря на его возраст, потому что это 3-й результат (в настоящее время) от Google для "циклической ссылки json.encode", и хотя я не согласен с ответами (полностью) выше, в котором используется ScriptIgnoreAttribute предполагает, что вы нигде в своем коде не захотите пройти через отношения в другом направлении для некоторого JSON. Я не верю в блокировку вашей модели из-за одного варианта использования.

Это вдохновило меня на использование этого простого решения.

Поскольку вы работаете в View в MVC, у вас есть Модель, и вы хотите просто назначить Модель для ViewData.Model в вашем контроллере, продолжайте и используйте запрос LINQ в вашем View, чтобы корректно сгладить данные удаляя неправильную циклическую ссылку для конкретного JSON, который вы хотите, вот так:

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

Или, если отношение "Машина -> Клиент" равно 1 .. * -> *, попробуйте:

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);
10 голосов
/ 12 июня 2013

На основании ответа TXL вы должны отключите отложенную загрузку и создание прокси, и вы можете использовать обычные методы для получения ваших данных.

Пример:

//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);
}
4 голосов
/ 08 сентября 2010

Используйте, чтобы иметь ту же проблему. Я создал простой метод расширения, который «сплющивает» объекты L2E в IDictionary. IDictionary правильно сериализован JavaScriptSerializer. Результирующий Json аналогичен прямой сериализации объекта.

Поскольку я ограничиваю уровень сериализации, циклические ссылки избегаются. Он также не будет включать 1-> n связанных таблиц (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);
    }

Метод My Action выглядит следующим образом:

    public JsonResult AsJson(int id) {
        var data = Find(id);
        var result = this.JsonFlatten(data);
        return Json(result, JsonRequestBehavior.AllowGet);
    }
2 голосов
/ 06 мая 2011

В Entity Framework версии 4 доступна опция: ObjectContextOptions.LazyLoadingEnabled

Если установить значение false, следует избегать проблемы «циклической ссылки». Однако вам придется явно загрузить свойства навигации, которые вы хотите включить.

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

1 голос
/ 05 января 2010

Поскольку, насколько мне известно, вы не можете сериализовать ссылки на объекты, а только копии, которые вы могли бы попробовать использовать немного грязного хака, который выглядит примерно так:

  1. Клиент должен сериализовать свою ссылку на машину как идентификатор машины
  2. Когда вы десериализуете код json, вы можете запустить над ним простую функцию, которая преобразует эти идентификаторы в правильные ссылки.
0 голосов
/ 19 декабря 2017

То, что я сделал, немного радикально, но мне не нужно это свойство, которое приводит к появлению неприятной ошибки, вызывающей циклическую ссылку, поэтому я установил его в null перед сериализацией.

SessionTickets result = GetTicketsSession();
foreach(var r in result.Tickets)
{
    r.TicketTypes = null; //those two were creating the problem
    r.SelectedTicketType = null;
}
return Json(result);

Если вам действительно нужны ваши свойства, вы можете создать модель представления, которая не содержит циклические ссылки, но, возможно, содержит некоторый идентификатор важного элемента, который вы могли бы использовать позже для восстановления исходного значения.

0 голосов
/ 25 августа 2015

У меня была такая же проблема на этой неделе, и я не мог использовать анонимные типы, потому что мне нужно было реализовать интерфейс, запрашивающий List<MyType>. После создания диаграммы, показывающей все взаимосвязи с навигацией, я обнаружил, что MyType имел двунаправленную связь с MyObject, что вызвало эту циклическую ссылку, поскольку они оба спасли друг друга.

Решив, что MyObject действительно не нужно знать MyType, и, таким образом, сделав однонаправленные отношения, эта проблема была решена.

0 голосов
/ 05 января 2010

Вам необходимо решить, какой объект является «корневым». Скажем, машина - это корень, тогда клиент - это подобъект машины. Когда вы сериализуете компьютер, он сериализует клиента как подобъект в JSON, а когда клиент сериализуется, он NOT сериализует свою обратную ссылку на компьютер. Когда ваш код десериализует машину, он десериализует подобъект клиента машины и восстанавливает обратную ссылку от клиента к машине.

Большинство библиотек сериализации предоставляют своего рода ловушку для изменения способа десериализации для каждого класса. Вам нужно было бы использовать этот хук, чтобы изменить десериализацию для класса машины, чтобы восстановить обратную ссылку в клиенте машины. Точно этот хук зависит от используемой вами библиотеки JSON.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...