asp.net-mvc - tipo - newtonsoft json serialize vb net
Json y excepción de referencia circular (9)
Como, por lo que sé, no puedes serializar referencias a objetos, pero solo copias puedes intentar utilizar un poco de un truco sucio que dice algo como esto:
- El cliente debe serializar su referencia de máquina como la identificación de la máquina
- Cuando deserializas el código json, puedes ejecutar una función simple encima que transforma esos identificadores en referencias adecuadas.
Tengo un objeto que tiene una referencia circular a otro objeto. Dada la relación entre estos objetos, este es el diseño correcto.
Para ilustrar
Machine => Customer => Machine
Como es de esperar, me topa con un problema cuando trato de usar Json para serializar una máquina u objeto de cliente. De lo que no estoy seguro es de cómo resolver este problema ya que no quiero romper la relación entre los objetos de la Máquina y del Cliente. ¿Cuáles son las opciones para resolver este problema?
Editar
Actualmente estoy usando el método Json provisto por la clase base del Controlador . Entonces la serialización que estoy haciendo es tan básica como:
Json(machineForm);
Debe decidir cuál es el objeto "raíz". Supongamos que la máquina es la raíz, entonces el cliente es un subobjeto de la máquina. Cuando serializa la máquina, serializará al cliente como un subobjeto en el JSON, y cuando el cliente se serialice, NO serializará su referencia posterior a la máquina. Cuando su código deserializa la máquina, deserializará el subobjeto del cliente de la máquina y restablecerá la referencia retrospectiva del cliente a la máquina.
La mayoría de las bibliotecas de serialización proporcionan algún tipo de gancho para modificar cómo se realiza la deserialización para cada clase. Debería usar ese gancho para modificar la deserialización para la clase de máquina para restablecer la referencia inversa en el cliente de la máquina. Exactamente qué es ese gancho depende de la biblioteca JSON que esté utilizando.
En Entity Framework versión 4 , hay una opción disponible: ObjectContextOptions.LazyLoadingEnabled
Ponerlo en falso debería evitar el problema de ''referencia circular''. Sin embargo, tendrá que cargar explícitamente las propiedades de navegación que desea incluir.
Estoy respondiendo esto a pesar de su edad porque es el 3er resultado (actualmente) de Google para "referencia circular json.encode" y aunque no estoy de acuerdo con las respuestas (completamente) anteriores, en el que usar ScriptIgnoreAttribute supone que ningún lugar en tu código querrá atravesar la relación en la otra dirección para algunos JSON. No creo en bloquear su modelo debido a un caso de uso.
Me inspiró a usar esta solución simple.
Dado que está trabajando en una Vista en MVC, tiene el Modelo y simplemente desea asignar el Modelo a ViewData.Model dentro de su controlador, siga adelante y use una consulta LINQ dentro de su Vista para aplanar los datos eliminando muy bien la ofensa referencia circular para el JSON particular que desee de esta manera:
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);
O si la relación Máquina -> Cliente es 1 .. * -> * entonces intente:
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);
Lo que he hecho es un poco radical, pero no necesito la propiedad, lo que hace que el desagradable error causante de referencia circular, así que lo haya establecido en nulo antes de la serialización.
SessionTickets result = GetTicketsSession();
foreach(var r in result.Tickets)
{
r.TicketTypes = null; //those two were creating the problem
r.SelectedTicketType = null;
}
return Json(result);
Si realmente necesita sus propiedades, puede crear un modelo de vista que no contenga referencias circulares, pero tal vez guarde algún Id del elemento importante, que podría usar más adelante para restaurar el valor original.
Según la respuesta de txl, debe desactivar la carga lenta y la creación de proxy y puede usar los métodos normales para obtener sus datos.
Ejemplo:
//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);
}
También he tenido el mismo problema esta semana y no podía usar tipos anónimos porque necesitaba implementar una interfaz que solicitara una List<MyType>
. Después de hacer un diagrama que muestra todas las relaciones con la navegabilidad, descubrí que MyType
tenía una relación bidireccional con MyObject
que causó esta referencia circular, ya que ambos se salvaron entre sí.
Después de decidir que MyObject
realmente no necesitaba saber MyType
y, por lo tanto, convertirlo en una relación unidireccional, este problema se resolvió.
Use para tener el mismo problema. He creado un método de extensión simple, que "aplana" objetos L2E en un IDictionary. Un IDictionary se serializa correctamente mediante JavaScriptSerializer. El Json resultante es lo mismo que serializar directamente el objeto.
Como limito el nivel de serialización, se evitan las referencias circulares. Tampoco incluirá tablas vinculadas 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);
}
Mi método de acción se ve así:
public JsonResult AsJson(int id) {
var data = Find(id);
var result = this.JsonFlatten(data);
return Json(result, JsonRequestBehavior.AllowGet);
}
Actualizar:
No intente utilizar NonSerializedAttribute
, ya que JavaScriptSerializer
aparentemente lo ignora.
En su lugar, use 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 esta forma, cuando Json
una Machine
al método Json
, atravesará la relación de Machine
a Customer
pero no intentará regresar de Customer
a Machine
.
La relación sigue ahí para que su código haga lo que le plazca, pero JavaScriptSerializer
(utilizado por el método Json
) lo ignorará.