sharp serialize serializar deserialize deserializar c# .net xml-serialization

serializar - serializer c#



Corregir la serialización XML y la deserialización de tipos "mixtos" en.NET (3)

Mi tarea actual implica escribir una biblioteca de clases para procesar archivos CDA de HL7.
Estos archivos CDA HL7 son archivos XML con un esquema XML definido, por lo que utilicé xsd.exe para generar clases .NET para la serialización y deserialización XML.

El esquema XML contiene varios tipos que contienen el atributo mixed = "true" , especificando que un nodo XML de este tipo puede contener texto normal mezclado con otros nodos XML.
La parte relevante del esquema XML para uno de estos tipos se ve así:

<xs:complexType name="StrucDoc.Paragraph" mixed="true"> <xs:sequence> <xs:element name="caption" type="StrucDoc.Caption" minOccurs="0"/> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="br" type="StrucDoc.Br"/> <xs:element name="sub" type="StrucDoc.Sub"/> <xs:element name="sup" type="StrucDoc.Sup"/> <!-- ...other possible nodes... --> </xs:choice> </xs:sequence> <xs:attribute name="ID" type="xs:ID"/> <!-- ...other attributes... --> </xs:complexType>

El código generado para este tipo se ve así:

/// <remarks/> [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute(TypeName="StrucDoc.Paragraph", Namespace="urn:hl7-org:v3")] public partial class StrucDocParagraph { private StrucDocCaption captionField; private object[] itemsField; private string[] textField; private string idField; // ...fields for other attributes... /// <remarks/> public StrucDocCaption caption { get { return this.captionField; } set { this.captionField = value; } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute("br", typeof(StrucDocBr))] [System.Xml.Serialization.XmlElementAttribute("sub", typeof(StrucDocSub))] [System.Xml.Serialization.XmlElementAttribute("sup", typeof(StrucDocSup))] // ...other possible nodes... public object[] Items { get { return this.itemsField; } set { this.itemsField = value; } } /// <remarks/> [System.Xml.Serialization.XmlTextAttribute()] public string[] Text { get { return this.textField; } set { this.textField = value; } } /// <remarks/> [System.Xml.Serialization.XmlAttributeAttribute(DataType="ID")] public string ID { get { return this.idField; } set { this.idField = value; } } // ...properties for other attributes... }

Si deserializo un elemento XML donde el nodo de párrafo tiene este aspecto:

<paragraph>first line<br /><br />third line</paragraph>

El resultado es que los elementos y las matrices de texto se leen así:

itemsField = new object[] { new StrucDocBr(), new StrucDocBr(), }; textField = new string[] { "first line", "third line", };

A partir de esto, no hay forma posible de determinar el orden exacto del texto y los otros nodos.
Si vuelvo a serializar esto, el resultado se ve exactamente así:

<paragraph> <br /> <br />first linethird line </paragraph>

El serializador predeterminado solo serializa los elementos primero y luego el texto.

Intenté implementar IXmlSerializable en la clase StrucDocParagraph para poder controlar la deserialización y la serialización del contenido, pero es bastante complejo ya que hay muchas clases involucradas y no llegué a una solución todavía porque no sé si el esfuerzo da sus frutos.

¿Existe algún tipo de solución fácil para este problema, o incluso es posible realizar una serialización personalizada a través de IXmlSerializable ? ¿O debería simplemente usar XmlDocument o XmlReader / XmlWriter para procesar estos documentos?


Para resolver este problema tuve que modificar las clases generadas:

  1. Mueva el XmlTextAttribute de la propiedad Text a la propiedad Items y agregue el parámetro Type = typeof(string)
  2. Eliminar la propiedad de Text
  3. Quitar el campo textField

Como resultado, el código generado (modificado) se ve así:

/// <remarks/> [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute(TypeName="StrucDoc.Paragraph", Namespace="urn:hl7-org:v3")] public partial class StrucDocParagraph { private StrucDocCaption captionField; private object[] itemsField; private string idField; // ...fields for other attributes... /// <remarks/> public StrucDocCaption caption { get { return this.captionField; } set { this.captionField = value; } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute("br", typeof(StrucDocBr))] [System.Xml.Serialization.XmlElementAttribute("sub", typeof(StrucDocSub))] [System.Xml.Serialization.XmlElementAttribute("sup", typeof(StrucDocSup))] // ...other possible nodes... [System.Xml.Serialization.XmlTextAttribute(typeof(string))] public object[] Items { get { return this.itemsField; } set { this.itemsField = value; } } /// <remarks/> [System.Xml.Serialization.XmlAttributeAttribute(DataType="ID")] public string ID { get { return this.idField; } set { this.idField = value; } } // ...properties for other attributes... }

Ahora si deserializo un elemento XML donde el nodo de párrafo se ve así:

<paragraph>first line<br /><br />third line</paragraph>

El resultado es que la matriz de elementos se lee así:

itemsField = new object[] { "first line", new StrucDocBr(), new StrucDocBr(), "third line", };

Esto es exactamente lo que necesito , el orden de los artículos y su contenido es correcto .
Y si vuelvo a serializar esto, el resultado vuelve a ser correcto:

<paragraph>first line<br /><br />third line</paragraph>

Lo que me señaló en la dirección correcta fue la respuesta de Guillaume, también pensé que debía ser posible de esta manera. Y luego estaba esto en la documentación de MSDN para XmlTextAttribute :

Puede aplicar el XmlTextAttribute a un campo o propiedad que devuelve una matriz de cadenas. También puede aplicar el atributo a una matriz de tipo Objeto, pero debe establecer la propiedad Tipo en cadena. En ese caso, cualquier cadena insertada en la matriz se serializa como texto XML.

Así que la serialización y la deserialización funcionan correctamente ahora, pero no sé si hay otros efectos secundarios. Tal vez ya no sea posible generar un esquema de estas clases con xsd.exe, pero no lo necesito de todos modos.


Qué pasa

itemsField = new object[] { "first line", new StrucDocBr(), new StrucDocBr(), "third line", };

?


Tuve el mismo problema que este, y encontré esta solución de alterar los .cs generados por xsd.exe. Aunque funcionó, no me sentía cómodo modificando el código generado, ya que tendría que acordarme de hacerlo siempre que regenerara las clases. También condujo a un código extraño que tuvo que probar y convertir a XmlNode [] para los elementos mailto.

Mi solución fue repensar el xsd. Abandoné el uso del tipo mixto y esencialmente definí mi propio tipo mixto.

Yo tenia esto

XML: <text>some text <mailto>[email protected]</mailto>some more text</text> <xs:complexType name="text" mixed="true"> <xs:sequence> <xs:element minOccurs="0" maxOccurs="unbounded" name="mailto" type="xs:string" /> </xs:sequence> </xs:complexType>

y cambiado a

XML: <mytext><text>some text </text><mailto>[email protected]</mailto><text>some more text</text></mytext> <xs:complexType name="mytext"> <xs:sequence> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="text"> <xs:complexType> <xs:simpleContent> <xs:extension base="xs:string" /> </xs:simpleContent> </xs:complexType> </xs:element> <xs:element name="mailto"> <xs:complexType> <xs:simpleContent> <xs:extension base="xs:string" /> </xs:simpleContent> </xs:complexType> </xs:element> </xs:choice> </xs:sequence> </xs:complexType>

Mi código generado ahora me da una clase myText:

public partial class myText{ private object[] itemsField; /// <remarks/> [System.Xml.Serialization.XmlElementAttribute("mailto", typeof(myTextTextMailto))] [System.Xml.Serialization.XmlElementAttribute("text", typeof(myTextText))] public object[] Items { get { return this.itemsField; } set { this.itemsField = value; } } }

el orden de los elementos ahora se conserva en la serilización / deserialización, pero tengo que probar / cast to / program contra los tipos myTextTextMailto y myTextText .

Solo pensé en lanzar eso como un enfoque alternativo que funcionó para mí.