2011-04-13 791 views
11

Estoy intentando serializar una jerarquía de clases en una cadena Json usando DataContractJsonSerializer, en un servicio WCF. el comportamiento por defecto para serializar una clase derivada es añadir el siguiente par de valores clave para el objeto:Cómo serializar el tipo de clase pero no el espacio de nombres en una cadena Json usando DataContractJsonSerializer

"__type":"ClassName:#Namespace"

Mi problema es que los espacios de nombres son largos y que hinchan la cadena JSON. me gustaría intervenir de algún modo con la serialización y la salida de este lugar:

"__type":"ClassName"

y en deserialización intervenir de nuevo para señalar al espacio de nombres correcta (que sé que en tiempo de ejecución).

¿Hay alguna manera de hacer tal cosa?

+0

Envío sin el espacio de nombres parece muy hacky , sin embargo, o simplemente lo dejé por completo, o no lo hice ... – Nyerguds

+0

@Nyerguds depende de tus prioridades ... si, por ejemplo, te comunicas con un dispositivo con un ancho de banda extremadamente bajo, tienes que hacer compensaciones ... –

Respuesta

8

Añadiendo el parámetro de espacio de nombres al contrato de datos hace el truco. [DataContract(Namespace = "")]

+0

Esto suprimió la parte del espacio de nombres del atributo, pero no los dos puntos que siguen el nombre de clase. Entonces, +1 para algunos ahorros, pero aún no es una solución completa. – dlchambers

12

This page describe las circunstancias bajo las cuales se emite la propiedad __type. En resumen, en WCF, si usa un tipo derivado y un KnownTypeAttribute, obtendrá una propiedad __type.

Ejemplo:

Asumir

[DataContract] 
[KnownType(typeof(Subscriber))] 
public class Person { ... } 

[DataContract] 
public class Subscriber : Person { ... } 

Este código genera una propiedad __type:

var o = new Subscriber("Fleming"); 
    var serializer = new DataContractJsonSerializer(typeof(Person)); 
    serializer.WriteObject(Console.OpenStandardOutput(), o); 

Pero este código no:

var o = new Subscriber("Fleming"); 
    var serializer = new DataContractJsonSerializer(typeof(Subscriber)); 
    serializer.WriteObject(Console.OpenStandardOutput(), o); 

en cuenta que el segundo recorte usa una D CJS con el mismo tipo que el objeto que se serializa.

Para evitar el __type, no use los tipos derivados, o para ser precisos, use un serializador tipeado al tipo que realmente está serializando. Si la serialización se realiza implícitamente por un método WCF, entonces el método debe escribirse apropiadamente. En mi ejemplo, significa que debe usar un tipo de devolución de "Suscriptor", y no el tipo principal, "Persona".

El tipo __ se transmite a la secuencia JSON mediante el método (privado) WriteServerTypeAttribute en la clase (interna) System.Runtime.Serialization.Json.XmlJsonWriter. No hay una manera pública, documentada y respaldada de modificar eso, hasta donde yo sé.

Para evitar esto, tal vez necesite devolver una cadena del método WCF, realice la serialización usted mismo, y postprocesar el JSON emitido.


Si no le importa lo __type, pero sólo desea eliminar el espacio de nombres de calificación del valor, a continuación, poner sus tipos en el espacio de nombres global. En otras palabras, colóquelos fuera de cualquier declaración namespace en el código.

Ejemplo: Cuando los tipos de datos residen en un espacio de nombres, y cuando he usado un tipo derivado, el número de serie JSON se ve así:

{ 
    "__type":"Subscriber:#My.Custom.Namespace", 
    "Index":604455, 
    "Name":"Fleming", 
    "Id":580540 
} 

Cuando los tipos de datos residen en el espacio de nombres global, parece que esto:

{ 
    "__type":"Subscriber:#", 
    "Index":708759, 
    "Name":"Fleming", 
    "Id":675323 
} 
+0

gracias! Me temo que ese es el caso ... Tendré que hacerlo a mano ... –

0

Nota: he escrito esta respuesta abajo y luego di cuenta de que DataContractResolver no es compatible actualmente con DataContractJsonSerializer. Sin embargo, pronto estará con la próxima versión del marco. Esto también es útil si está buscando algo más que JSON.

**

Puede hacer esto con un DataContractResolver, que le permite asignar tipos a xsi: type (__type) de información y viceversa de manera personalizada.

Para hacer esto, echa un vistazo a this blog post on DataContractResolver, además this conceptual topic, además this sample

6

La respuesta de Cheeso fue excelente. Me hizo descubrir un refinamiento a limpiar el campo __type sin embargo:

En lugar de la eliminación de su subclase de su espacio de nombres puede agregar una propiedad como la siguiente:

[DataMember(Name = "__type")] 
public string SubclassType 
{ 
    get 
    { 
     return "Subscriber"; 
    } 
    set { } 
} 

Todavía se queden con el nombre feo " __type "pero encontré que porque estaba devolviendo una lista de subtipos, quería especificar el nombre del tipo de todos modos. Incluso podría devolver un valor de "" para reducir aún más el tamaño de la respuesta. También puede simplemente declarar la propiedad como:

public string __type 

pero he encontrado que para acentuar el hack por lo que me quedé con un nombre de propiedad correspondiente y luego le cambió el nombre.

-Joey

0

@Cheeso escribió:

Para evitar esto, tal vez había necesidad de devolver una cadena a partir del método de WCF , realizar la serialización a sí mismo, y post-proceso de la emitido JSON.

Así es cómo implementé ese postprocesamiento. Pensé que lo publicaría aquí. JIC podría ayudar a alguien más.

En primer lugar algunos repetitivo para mostrar cómo generar mi cadena JSON:

// Instantiate & populate the object to be serialized to JSON 
SomeClass xyz = new SomeClass(); 
... populate it ... 

// Now serialize it 
DataContractJsonSerializer ser = new DataContractJsonSerializer(xyz.GetType()); // Note xyz.GetType() 
... serialize the object to json, many steps omitted here for brevity ... 
string json = sr.ReadToEnd(); 

(serialización se basa en ejemplos de https://msdn.microsoft.com/en-us/library/bb412179%28v=vs.110%29.aspx)

Tenga en cuenta que la [DataContract] en SomeClass hace no incluyen la sintaxis (name="") que he visto sugerido en otra parte.Eso solo elimina el espacio de nombres del tipo __ a costa de tener que adornar TODOS sus atributos DataContract, lo cual satura su código. En cambio, mi post-procesador maneja el nombre del ensamblado en el campo __type.

Y ahora la cadena json contiene el texto JSON, pero desafortunadamente incluye todo ese "tipo basura" que no desea pero no puede suprimir.

Así que aquí es mi código de post procesamiento que elimina:

// This strips out that unsuppressable __type clutter generated by the KnownType attributes 
Attribute[] attrs = Attribute.GetCustomAttributes(xyz.GetType()); 
foreach (Attribute attr in attrs) 
{ 
    if (attr is KnownTypeAttribute) 
    { 
     KnownTypeAttribute a = (KnownTypeAttribute)attr; 
     string find = "\"__type\":\"" + a.Type.ReflectedType.Name + "." + a.Type.Name + ":#" + a.Type.Namespace + "\","; 
     json = json.Replace(find, ""); 
    } 
} 

Esto hace algunas suposiciones, lo más notablemente posible que el campo __type termina con una coma, lo que supone que otro campo le sigue, aunque (una) mis objetos siempre tienen al menos 1 campo y (b) he encontrado que el campo __type es siempre el 1º en la salida del objeto serializado.

Como siempre, puede que tenga que ajustar algo a su situación, pero creo que funciona bien para la mía.

+0

Tenga en cuenta que debe llamarlo recursivamente si tiene más de un nivel de objetos personalizados en el archivo completo. Al final, simplemente lo eliminé con una expresión regular: '" \ "__ tipo \" \\ s *: \\ s * \ "[^ \"] + \ "\\ s *,? \\ s *" ' – Nyerguds

0

Hace algún tiempo decidí este problema. Yo uso DataContractJsonSerializer Tendrás __type in json, si tu método de serialización tiene un parámetro de clase Base, pero le das subClass como parámetro. Más detalles:

[DataContract] 
[KnownType(typeof(B))] 
public abstract class A 
{ 
    [DataMember] 
    public String S { get; set; } 
} 

[DataContract] 
public class B : A 
{ 
    [DataMember] 
    public Int32 Age { get; set; } 
} 

public static String ToJson<T>(this T value) 
{ 
    var serializer = new DataContractJsonSerializer(typeof(T)); 
    using (var stream = new MemoryStream()) 
    { 
     serializer.WriteObject(stream, value); 
     return Encoding.UTF8.GetString(stream.ToArray()); 
    } 
} 

Existen dos métodos para la prueba:

public static void ReadTypeDerived(A type) 
{ 
    Console.WriteLine(ToJson(type)); 
} 

y

public static void ReadType<T>(T type) 
{ 
    Console.WriteLine(ToJson(type)); 
} 

En la primera prueba que augure tiene

"{\" __ Tipo \ ": \" B: # ConsoleApplication1 \ ", \" S \ ": \" Vv \ ", \" Age \ ": 10}"

En segundo:

"{\" S \ ": \" Vv \ "\ "Edad \": 10}"

+0

Pero, ¿y si el problema son los subtipos utilizados dentro de ese objeto? – Nyerguds

Cuestiones relacionadas