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!
Hola, gracias por tu post, me ha sido muuy util para enviar una respuesta JSON desde web services escritos en VB.NET.
ResponderEliminarAhora necesito hacer en el cliente VB.NET 2010, la función que deserialize la respuesta JSON.
Me puedes ayudar con respecto a como seria la función para deserializar la respuesta JSON?
Mi función para serializar dentro de una DLL quedó asi:
Public Function ManageMobileUsers(UsrNmb As String, UsrPwd As String) As String
Dim TheReturnValue As New ReturnValues
Dim TheMobileUser As New MobileUser
TheReturnValue = ManageUsers(UsrNmb, UsrPwd)
TheMobileUser = TheReturnValue.MobileUser
Dim Serializador As New System.Runtime.Serialization.Json.DataContractJsonSerializer(TheMobileUser.GetType)
Dim Ms As New System.IO.MemoryStream
Serializador.WriteObject(Ms, TheMobileUser)
Dim TheStringJson As String = System.Text.Encoding.UTF8.GetString(ms.ToArray)
Return TheStringJson
End Function
El Web Service quedó así:
_
_
Public Function ManageMobileUsers(ByVal UsrNmb As String, ByVal UsrPwd As String, ByVal Release As String) As String
Dim appm As AppMembers = New AppMembers(UsrNmb)
Return appm.DBProc.ManageMobileUsers(UsrNmb, UsrPwd) ' Esta función ya devuelve JSON
End Function
Esta es la clase que serializo y necesito deserializar:
Public Class MobileUser
Private _status As String
Public Property status() As String
Get
Return _status
End Get
Set(ByVal value As String)
_status = value
End Set
End Property
Private _userId As String
Public Property userId() As String
Get
Return _userId
End Get
Set(ByVal value As String)
_userId = value
End Set
End Property
Private _name As String
Public Property name() As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
Private _rol As Integer
Public Property rol() As Integer
Get
Return _rol
End Get
Set(ByVal value As Integer)
_rol = value
End Set
End Property
End Class
Muchas gracias.
Luis
Muchas gracias me ha sido de gran ayuda!.
ResponderEliminar