I am sending a XML to a aspnet core web api. The value for the namespace prefix cfdi:
is defined in a containing node:
<cfdi:Comprobante>
<cfdi:Conceptos>
</cfdi:Conceptos>
<cfdi:Addenda>
<bfa2:AddendaBuzonFiscal version="2.0" xmlns:bfa2="http://www.buzonfiscal.com/ns/addenda/bf/2"><bfa2:TipoDocumento nombreCorto="FAC" descripcion="Factura"/><bfa2:CFD totalConLetra="CINCUENTA Y DOS MIL QUINIENTOS OCHENTA Y NUEVE PESOS 64/100 M.N." observaciones="OBSERVACIONES"/><bfa2:Extra atributo="ClaveTransportista" valor="00328"/><bfa2:Extra atributo="NoRelacionPemex" valor="1-2"/>
<bfa2:Extra atributo="NoConvenio" valor="5"/>
</bfa2:AddendaBuzonFiscal>
<Encabezado NumOrden="" NumFacturaOriginal="" FechaDePedido=""/>
<Envio Calle="" NoExterior="" Colonia="" Localidad="" Municipio="" Estado="" Pais="" CodigoPostal="" NombreEnviar=""/><Detalle OrdenCompraLinea="10" GRNumber="GRN"/><Detalle OrdenCompraLinea="10" GRNumber="GRN"/><Detalle OrdenCompraLinea="10" GRNumber="GRN"/>
</cfdi:Addenda>
</cfdi:Comprobante>
To deserialize this I made the class Comprobante
:
public class Comprobante : IValidatableObject
{
[Required]
[XmlArray("Conceptos"), XmlArrayItem(typeof(Concepto), ElementName = "Concepto")]
public List<Concepto> Conceptos { get; set; }
public Addenda Addenda { get; set; }
}
Everything is mapped to the class properties but the Addenda
node could receive anything -- any number of valid XML nodes -- so I don´t have a class definition. I.e. the Addenda
node could contain n nodes that I don’t know about, the information is validated in the recipient end. For example a customer could ask to add a node with a PO number, another could ask for buyer name. Etc.
If I need to get all the Addenda
node content as string, how should I declare it in the class?
You can deserialize arbitrary, free-form XML data using XmlSerializer
by marking target properties with [XmlAnyElement]
.
E.g. you can define your Addenda
type as follows:
[XmlRoot("Comprobante", Namespace = "http://cfdi")]
public class Comprobante : IValidatableObject
{
[Required]
[XmlArray("Conceptos"), XmlArrayItem(typeof(Concepto), ElementName = "Concepto")]
public List<Concepto> Conceptos { get; set; }
public Addenda Addenda { get; set; }
}
public class Addenda
{
[XmlAnyElement]
[XmlText]
public XmlNode[] Nodes { get; set; }
}
Sample working .Net fiddle #1.
Or, you could eliminate the Addenda
type completely and replace it with an XmlElement
property in the containing type:
[XmlRoot("Comprobante", Namespace = "http://cfdi")]
public class Comprobante : IValidatableObject
{
[Required]
[XmlArray("Conceptos"), XmlArrayItem(typeof(Concepto), ElementName = "Concepto")]
public List<Concepto> Conceptos { get; set; }
[XmlAnyElement("Addenda")]
public XmlElement Addenda { get; set; }
}
Sample working .Net fiddle #2.
Notes:
When applied without an element name, [XmlAnyElement]
specifies that the member is an array of XmlElement
or XmlNode
objects which will contain all arbitrary XML data that is not bound to some other member in the containing type.
When applied with an element name (and optional namespace), [XmlAnyElement("Addenda")]
specifies that the member is a either a single XmlElement
object or an array of such objects, and will contain all arbitrary XML elements named <Addenda>
. Using this form eliminates the need for the extra Addenda
type.
Combining [XmlText]
with [XmlAnyElement]
allows arbitrary mixed content to be deserialized.
If you are using .NET Core you may need to nuget System.Xml.XmlDocument.