none
Problema al serializar una propiedad shadow con XmlSerializer RRS feed

  • Pregunta

  • Hola! Estoy teniendo un problema con una serie de clases que necesito serializar. Estas clases deben heredar de una clase que no puedo modificar. De modo simplificado, la jerarquía de las clases que necesito utilizar es la siguiente;

    <Serializable()> Public Class Entidad Public Property Id As Integer Public Property Nombre As String End Class <Serializable()> Public Class TipoDeDocumento Inherits Entidad <Serializable()> Public Enum eId DNI = 1 LC = 2 LE = 3 End Enum Public Shadows Property Id() As eId Get Return MyBase.Id End Get Set(value As eId) MyBase.Id = value End Set End Property

    End Class

    Module Module1

    Public Function Serializar(ByVal entidad As Entidad) As String
                Dim result As String
                Dim writer As New XmlSerializer(Me.GetType())
                Using sw As New StringWriter()
                    writer.Serialize(sw, Me)
                    result = sw.ToString()
                End Using
                Return result
            End Function

    End Module

    Si intento invocar al método TipoDeDocumento.Serializar(), se me produce la siguiente excepción al momento de crear el XmlSerializer:

    Member TipoDeDocumento.Id of type TipoDeDocumento.eId hides base class member Entidad.Id of type System.Int32. Use XmlElementAttribute or XmlAttributeAttribute to specify a new name.

    Si agrego a la propiedad shadowed alguno de los atributos mencionados en la excepción, se me sigue produciendo la misma excepción.

    Si a la propiedad shadowed le agrego el atributo <XmlIgnore>, se produce la siguiente excepcion:

    Unable to generate a temporary class (result=1).
    error CS0266: Cannot implicitly convert type 'int' to 'TipoDeDocumento.eId'. An explicit conversion exists (are you missing a cast?)

    Sin embargo, he probado los siguientes casos, en los que si se realiza la serialización, pero que no me sirven como solución:

    • Cambio el tipo de la propiedad shadow a Integer
    • Creo una nueva propiedad "Public Property Id2 as eId"
    • Creo una nueva propiedad "<XmlIgnore>Public Property Id2 as eId"

    La idea del método Serializar() es construir una extensión aplicable a cualquier entidad heredada de Entidad.

    Estoy estancado con esta funcionalidad. He buscado en internet pero no he encontrado ningún caso que resuelva mi inquietud. Espero que me puedan ayudar.

    Saludos

    Leonardo 



    Leo González

    viernes, 16 de octubre de 2015 19:34

Respuestas

  • "Leo González" escribió:

    > Sin embargo sigo obteniendo las mismas excepciones que mencionaba en el
    > post original. Utilicé los códigos de ejemplo que me mandaste y sigo
    > obteniendo los mismos errores cuando intento serializar la clase heredada.
    > El código completo que uso está en este link:
    >
    > https://www.dropbox.com/s/fa8qajjqbrlb9qs/Form1.vb?dl=0

    El código completo es el mismo que publiqué en mi respuesta anterior, y siento decirte que no puedo reproducir los errores que dices que te aparecen.

    El ejemplo lo he compilado con Visual Studio 2013 y 2015, utilizando .NET 4.0 y versiones superiores, ignorando en estos momentos el motivo por el cual continuas obteniendo dichos mensajes de errores.


    Enrique Martínez Montejo
            [MS MVP - VB]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.

    • Marcado como respuesta Leo González martes, 20 de octubre de 2015 11:45
    martes, 20 de octubre de 2015 7:41
    Moderador

Todas las respuestas

  • "Leo González" escribió:

    > Si intento invocar al método TipoDeDocumento.Serializar(), se me produce la siguiente
    > excepción al momento de crear el XmlSerializer:
    >
    > Member TipoDeDocumento.Id of type TipoDeDocumento.eId hides base class member Entidad.Id
    > of type System.Int32. Use XmlElementAttribute or XmlAttributeAttribute to specify a new name.
    >
    > Si agrego a la propiedad shadowed alguno de los atributos mencionados en la excepción, se me
    > sigue produciendo la misma excepción.


    Hola, Leo:

    No habría estado de más que hubieses publicado cómo has implementado los atributos XmlElementAttribute o XmlAttributeAttribute para que continúes obteniendo la misma excepción, ya que ésta se debe a que estás sombreando una propiedad en la clase derivada la cual tiene diferente tipo de dato que la existente en su clase base.

    > Si a la propiedad shadowed le agrego el atributo <XmlIgnore>, se produce la siguiente excepcion:
    >
    > Unable to generate a temporary class (result=1).
    > error CS0266: Cannot implicitly convert type 'int' to 'TipoDeDocumento.eId'. An explicit conversion
    > exists (are you missing a cast?)

    Es normal que obtengas ese error en tiempo de ejecución, ya que los tipos de datos de ambas propiedades no son iguales. Si tuvieras activada la instrucción Option Strict (que es lo recomendable), la excepción la obtendrías en tiempo de diseño, ya que ni tan siquiera podrías compilar el proyecto hasta que no hicieras la conversiones explícitas necesarias.

    >     Module Module1
    >
    >       Public Function Serializar(ByVal entidad As Entidad) As String
    >            Dim result As String
    >            Dim writer As New XmlSerializer(Me.GetType())
    >            Using sw As New StringWriter()
    >                writer.Serialize(sw, Me)
    >                result = sw.ToString()
    >            End Using
    >            Return result
    >        End Function
    >    End Module

    ¡Esto ya no lo entiendo! ¿Nos podrías explicar cómo has sido capaz de compilar el módulo en tu proyecto utilizando la palabra clave Me dentro de un Module?

    Que yo sepa, la palabra Me no se puede incluir en un Module, negándose el compilador de Visual Basic a compilar el proyecto, de ahí que te pregunte qué es lo que has hecho para compilar tu proyecto. ;-)

    La palabra clave Me se puede utilizar en un procedimiento existente en una clase (Class), y siempre que dicho procedimiento no se encuentre compartido (Shared), de ahí que tampoco se pueda utilizar en los procedimientos existentes en un Module, porque a la hora de compilar el proyecto, todos los procedimientos existentes en el Module estarán compartidos.

    > La idea del método Serializar() es construir una extensión aplicable
    > a cualquier entidad heredada de Entidad.

    Si es así, y salvo que haya algo por ahí que yo ignore, no vas a tener más remedio que sombrear aquellos miembros que tengan el mismo nombre y diferente tipo de dato (sombreados), como es el caso de la propiedad Id de tu clase TipoDeDocumento, y asignarle el atributo XmlIgnore:

    Public Class TipoDeDocumento

        <XmlIgnore()>
        Public Shadows Property Id() As eId
            Get
                Return CType(MyBase.Id, eId)  ' --> Hay que convertir explícitamente el valor a la enumeración eId.
            End Get
            Set(value As eId)
                MyBase.Id = value
            End Set
        End Property

    End Class

    Y para serializar/deserializar cualquier instancia de la clase Entidad o heredera de ella, inserta en tu Module los siguientes procedimientos:

    Friend Module Module1
    
        Public Function Serializar(ByVal entidad As Entidad) As String
    
            If (entidad Is Nothing) Then
                Return Nothing
            End If
    
            Dim writer As New XmlSerializer(entidad.GetType())
    
            Dim result As String = String.Empty
    
            Using sw As New StringWriter()
                writer.Serialize(sw, entidad)
                result = sw.ToString()
            End Using
    
            Return result
    
        End Function
    
        Public Function Deserializar(ByVal xmlString As String, ByVal espacioNombres As String) As Entidad
    
            If ((String.IsNullOrEmpty(xmlString)) OrElse (String.IsNullOrEmpty(espacioNombres))) Then
                Return Nothing
            End If
    
            Dim ty As Type = Type.GetType(espacioNombres)
            If (ty Is Nothing) Then
                Throw New ArgumentException("No se ha especificado el espacio de nombres del objeto que se desea deserializar.")
            End If
    
            Dim writer As New XmlSerializer(ty)
    
            Dim result As Object = Nothing
    
            Using sw As New StringReader(xmlString)
                result = writer.Deserialize(sw)
            End Using
    
            Return DirectCast(result, Entidad)
    
        End Function
    
    End Module

    Y para probar la serialización/deserialización, ejecuta lo siguiente:

        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    
            ' Serializar un objeto Entidad.
            '
            Dim ent As New Entidad()
            ent.Id = 1
            ent.Nombre = "Leo González"
    
            Dim xmlString As String = Module1.Serializar(ent)
    
            ' Deserializar el xml.
            '
            Dim ent2 As Entidad = Module1.Deserializar(xmlString, "WindowsApplication1.Entidad")
    
            Dim msg As String = String.Format("Nombre: {0}, Id: {1}", ent2.Nombre, ent2.Id)
            MessageBox.Show(msg)
    
        End Sub
    
        Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    
            ' Serializar un objeto TipoDeDocumento
            '
            Dim doc As New TipoDeDocumento()
            doc.Id = TipoDeDocumento.eId.DNI
            doc.Nombre = "Leo González"
    
            Dim xmlString As String = Module1.Serializar(doc)
    
            ' Deserializar el xml.
            '
            Dim doc2 As TipoDeDocumento = DirectCast(Module1.Deserializar(xmlString, "WindowsApplication1.TipoDeDocumento"), TipoDeDocumento)
    
            Dim msg As String = String.Format("Nombre: {0}, Id: {1}", doc2.Nombre, doc2.Id)
            MessageBox.Show(msg)
    
        End Sub

    Fíjate que cuando deserializas un objeto Entidad obtendrás el siguiente resultado:

        "Nombre: Leo González, Id: 1"

    Y cuando hagas lo propio con un objeto TipoDeDocumento, obtendrás éste otro resultado:

        "Nombre: Leo González, Id: DNI"

    Es decir, en el primero obtenemos un valor Integer correspondiente a la propiedad Id de la clase Entidad, y en el segundo obtenemos el valor DNI (del tipo de dato eID) correspondiente a la propiedad Id sombreada de la clase TipoDeDocumento.

    Por último indicarte lo siguiente. Observa cómo especifico el nombre del espacio de nombres de la clase que deseo deserializar:

        Dim doc2 As TipoDeDocumento = _
            DirectCast(Module1.Deserializar(xmlString, "WindowsApplication1.TipoDeDocumento"), TipoDeDocumento)

    WindowsApplication1 es el nombre de la raíz del proyecto que he utilizado para realizar la prueba y donde se encuentran definidas las clases Entidad y TipoDeDocumento. En tu caso, tendrás que modificar el nombre WindowsApplication1 por aquel que se corresponda con el nombre de la raíz de tu proyecto. ¿Me explico? ;-)

    Un saludo


    Enrique Martínez Montejo
            [MS MVP - VB]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.




    sábado, 17 de octubre de 2015 10:11
    Moderador
  • Hola Enrique! Muchas gracias por tu tiempo  y por tu respuesta. Las observaciones que hiciste de mi código son correctas. El método serializar antes estaba dentro del objeto TipoDeDocumento, por eso es que tenía las referencias hacia "ME".

    Por otro lado, la forma en que definí XmlElementAttribute o XmlAttributeAttribute son las siguientes:

    <XmlElement("Id2")>
    Public Shadows Property Id() As eId

    <XmlAttribute("Id2")>
    Public Shadows Property Id() As eId

    La clase heredada, finalmente me quedó así:

    <Serializable()>
    Public Class TipoDeDocumento
        Inherits Entidad
    
        <Serializable()>
        Public Enum eId
            DNI = 1
            LC = 2
            LE = 3
        End Enum
    
        <XmlIgnore()>
        Public Shadows Property Id() As eId
            Get
                Return CType(MyBase.Id, eId)
            End Get
            Set(value As eId)
                MyBase.Id = value
            End Set
        End Property
    End Class

    Sin embargo sigo obteniendo las mismas excepciones que mencionaba en el post original. Utilicé los códigos de ejemplo que me mandaste y sigo obteniendo los mismos errores cuando intento serializar la clase heredada. El código completo que uso está en este link:

    https://www.dropbox.com/s/fa8qajjqbrlb9qs/Form1.vb?dl=0

    Me estoy pasando algo por alto?

    Nuevamente gracias!

    Saludos


    Leo González

    lunes, 19 de octubre de 2015 12:46
  • "Leo González" escribió:

    > Sin embargo sigo obteniendo las mismas excepciones que mencionaba en el
    > post original. Utilicé los códigos de ejemplo que me mandaste y sigo
    > obteniendo los mismos errores cuando intento serializar la clase heredada.
    > El código completo que uso está en este link:
    >
    > https://www.dropbox.com/s/fa8qajjqbrlb9qs/Form1.vb?dl=0

    El código completo es el mismo que publiqué en mi respuesta anterior, y siento decirte que no puedo reproducir los errores que dices que te aparecen.

    El ejemplo lo he compilado con Visual Studio 2013 y 2015, utilizando .NET 4.0 y versiones superiores, ignorando en estos momentos el motivo por el cual continuas obteniendo dichos mensajes de errores.


    Enrique Martínez Montejo
            [MS MVP - VB]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.

    • Marcado como respuesta Leo González martes, 20 de octubre de 2015 11:45
    martes, 20 de octubre de 2015 7:41
    Moderador
  • Hola Enrique! Gracias por el aporte. El último dato que me tiraste era el que me faltaba para terminar de diagnosticar el problema. 

    La versión de framework en que corre nuestra solución es 3.5. Al proyecto de prueba intenté cambiar el framework y ya se ejecutó perfectamente. Veremos la posibilidad de migrar la solución a una versión más actualizada.

    Saludos


    Leo González

    martes, 20 de octubre de 2015 11:45