En un anterior post vimos que podíamos serializar a JSON con las clases JavascriptSerializer y DataContractSerializer, pero en este post intentaremos entender un poco mejor cuando usar una u otra clase y cuales son sus ventajas y desventajas.
Vamos a trabajar con una clase Coche con las propiedades Marca (String), Deportivo (Boolean) y Caballos (Integer).
Public Class Coche
Private _marca As String
Public Property Marca() As String
Get
Return _marca
End Get
Set(ByVal value As String)
_marca = value
End Set
End Property
Private _deportivo As Boolean
Public Property Deportivo() As Boolean
Get
Return _deportivo
End Get
Set(ByVal value As Boolean)
_deportivo = value
End Set
End Property
Private _caballos As Integer
Public Property Caballos() As Integer
Get
Return _caballos
End Get
Set(ByVal value As Integer)
_caballos = value
End Set
End Property
End Class
Para trabajar con DataContractJsonSerializer es necesario agregar referencias a los ensamblados System.Runtime.Serialization y System.ServiceModel.Web.
Primero utilizaremos la clase System.Web.Script.Serialization.JavaScriptSerializer y después de la clase System.Runtime.Serialization.Json.DataContractJsonSerializer.
Primero JavaScriptSerializer:
Dim seat As New Coche
seat.Marca = "León"
seat.Deportivo = False
seat.Caballos = 110
Dim serializador As New System.Web.Script.Serialization.JavaScriptSerializer
Dim sb As New System.Text.StringBuilder
serializador.Serialize(seat, sb)
{"Marca":"León","Deportivo":false,"Caballos":110}
Como vemos no ha sido necesario adornar la clase con el atributo <Serializable()> ni especificar al serializador cual es el tipo del objeto que estamos serializando, y esto es porque JavascriptSerializer utiliza reflexión.
Segundo DataContractJsonSerializer:
Dim serializador As New System.Runtime.Serialization.Json.DataContractJsonSerializer(seat.GetType)
Dim ms As New System.IO.MemoryStream
serializador.WriteObject(ms, seat)
Dim textoJson As String = System.Text.Encoding.UTF8.GetString(ms.ToArray)
{"Caballos":110,"Deportivo":false,"Marca":"León"}
Como vemos el resultado es el mismo (o casi porque el orden las propiedades ha cambiado…) y además tampoco ha sido necesario adornar la clase con <Serializable()> ni <DataContract()> para serializar con este serializador, de nuevo utiliza reflexión. Por otro lado, si ha sido necesario especificar el tipo del objeto a serializar porque DataContractJsonSerializer.
Si agregamos el atributo <Serializable()> a nuestro clase Coche ahora los resultados son distintos:
JavascriptSerializer | {"Marca":"León","Deportivo":false,"Caballos":110} |
DataContractJsonSerializer | {"_caballos":110,"_deportivo":false,"_marca":"León"} |
Hay que fijarse que DataContractJsonSerializer parece que no se entiende muy bien con el atributo <Serializable()> y ha tomado el nombre de los campos privados en vez de el nombre de las propiedades públicas.
Si en vez de <Serializable()> agregamos <DataContract> a nuestra clase:
JavascriptSerializer | {"Marca":"León","Deportivo":false,"Caballos":110} |
DataContractJsonSerializer | {} |
Esto significa que JavascriptSerializer no utiliza el atributo <DataContract()>, pero DataContractJsonSerializer si lo utiliza y entonces espera que cada propiedad serializable esté marcada con el atributo <DataMember()>. De este modo, agregamos el atributo <DataMember()> a las propiedades de nuestra clase. Y ahora obtenemos:
JavascriptSerializer | {"Marca":"León","Deportivo":false,"Caballos":110} |
DataContractJsonSerializer | {"Caballos":110,"Deportivo":false,"Marca":"León"} |
Además el atributo DataMember nos permite especificar con que nombre de campo se serializará la propiedad, el orden, etc. http://msdn.microsoft.com/es-es/library/ms574795(v=VS.90).aspx
Por ejemplo, y serializando con DataContractJsonSerializer y con el siguiente adorno para la propiedad Marca: <System.Runtime.Serialization.DataMember(Name:="MiMarca")> _
El resultado será {"Caballos":110,"Deportivo":false,"MiMarca":"León"}.
Por otro lado, una importante característica que soporta JavascriptSerializer y que no soporta DataContractJsonSerializer es que JavascriptSerializer puede deserializar a un objeto diccionario además de a un tipo concreto. Esto significa que si nos llega un código Json desde cliente y no tenemos ninguna clase equivalente en la que deserializar, sólo podremos usar JavascriptSerializer para llevar a cabo esta operación (puesto que DataContractJsonSerializer siempre nos pide un tipo).
Dim texto As String = "{""Marca"":""León"",""Deportivo"":false,""Caballos"":110}"
'Conozco el tipo coche...
Dim nuevoCoche As Coche = serializador.Deserialize(Of Coche)(texto)
'No conozco el tipo coche...
Dim diccionario As Dictionary(Of String, Object) = _
CType(serializador.DeserializeObject(texto), Dictionary(Of String, Object))
Ahora haremos que nuestra clase tenga además una referencia a un objeto de tipo Volante:
Public Class Coche
Private _marca As String
Public Property Marca() As String
Get
Return _marca
End Get
Set(ByVal value As String)
_marca = value
End Set
End Property
Private _deportivo As Boolean
Public Property Deportivo() As Boolean
Get
Return _deportivo
End Get
Set(ByVal value As Boolean)
_deportivo = value
End Set
End Property
Private _caballos As Integer
Public Property Caballos() As Integer
Get
Return _caballos
End Get
Set(ByVal value As Integer)
_caballos = value
End Set
End Property
Private _volante As Volante
Public Property Volante() As Volante
Get
Return _volante
End Get
Set(ByVal value As Volante)
_volante = value
End Set
End Property
End Class
Public Class Volante
Private _forma As String
Public Property Forma() As String
Get
Return _forma
End Get
Set(ByVal value As String)
_forma = value
End Set
End Property
Private _radios As Integer
Public Property Radios() As Integer
Get
Return _radios
End Get
Set(ByVal value As Integer)
_radios = value
End Set
End Property
End Class
JavascriptSerializer | {"Marca":"León","Deportivo":false,"Caballos":110,"Volante":{"Forma":"Redonda","Radios":3}} |
DataContractJsonSerializer | {"Caballos":110,"Deportivo":false,"Marca":"León","Volante":{"Forma":"Redonda","Radios":3}} |
Y deserializando si conocer el tipo, tampoco hay ningún problema:
A raíz de todo lo expuesto podemos sacar las siguientes conclusiones:
- En comienzo (para una clase sin adornos), ambas clases (JavascriptSerializer y DataContractJsonSerializer) ofrecen el mismo texto Json de salida.
- Si nuestra clase está marcada como <Serializable()>, JavascriptSerializer funcionará correctamente pero entonces ya será necesario agregar también el adorno <DataContract()> y <DataMember()> para que siga funciona correctamente DataContractJsonSerializer.
- A través del atributo <DataMember()> es posible configurar ciertos aspectos de la serialización como el nombre, el orden, etc.
- Si no se tiene un tipo válido para la deserialización, JavascriptSerializer ofrece una alternativa deserializando a un objeto diccionario
- Supuestamente JavascriptSerializer está marcado como obsoleta así que Microsoft recomienda el uso de DataContractJsonSerializer.
Por último y si utilizamos ASP.NET AJAX, tenemos disponibles en cliente ciertos métodos para serializar y deserializar datos en Json. Para ello utilizaremos la clase (desde javascript) Sys.Serialization.JavaScriptSerializer y sus métodos serialize y deserialize. http://msdn.microsoft.com/en-us/library/bb310857.aspx
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<div>
<asp:HiddenField ID="HiddenField1" runat="server" />
<asp:HiddenField ID="HiddenField2" runat="server" />
<script type="text/javascript">
function pageLoad(sender, e) {
//recoger texto json generado desde el servidor
var value = $get("<%=HiddenField1.ClientID %>").value;
//deserializar a través de ASP.NET AJAX
var coche = Sys.Serialization.JavaScriptSerializer.deserialize(value);
alert(coche.Caballos);
}
</script>
</div>
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim seat As New Coche
seat.Marca = "León"
seat.Deportivo = False
seat.Caballos = 110
seat.Volante = New Volante
seat.Volante.Forma = "Redonda"
seat.Volante.Radios = 3
Dim serializador As New System.Web.Script.Serialization.JavaScriptSerializer
Dim sb As New System.Text.StringBuilder
serializador.Serialize(seat, sb)
HiddenField1.Value = sb.ToString
End Sub
También podemos deserializar json con javascript, bien sea a pelo, (que no se recomienda porque supone un riego de seguridad), con jQuery y también a través de los métodos nativos del navegador http://www.javascriptkit.com/jsref/json.shtml (ojo!, que sólo los navegadores modernos lo incluyen).
//deserializar con jQuery
coche = $.parseJSON(value);
alert(coche.Caballos);
//deserializar con javascript
/*
NO se recomienda porque podría suponer un problema de seguridad!!
*/
coche = eval("(" + value + ");");
alert(coche.Caballos);
Por último, desde http://www.json.org/js.html podemos ver cual es la sintaxis JSON y además descargarnos el fichero http://www.json.org/json2.js que nos ayudará a serializar y deserializar en cliente (porque no siempre tendremos ASP.NET AJAX y su framework de cliente disponible, o por otro lado tampoco tendremos la certeza de estar ejecutando una de las últimas versiones del navegador de turno).
Este fichero expone sólo 2 métodos (igual que el objeto nativo JSON que incluyen los navegadores modernos): stringify (para conseguir la representación en JSON de un objeto) y parse (para conseguir un objeto desde su representación en JSON). Sus llamadas básicas (que serán el 99% de las veces) son las siguientes:
//deserializar
coche = JSON.parse(value);
alert(coche.Caballos);
//serializar
value = JSON.stringify(coche);
alert(value);
Un saludo!