Hace un tiempo tuve que crear un interfasado entre MSDyn365FO yun sistema externo que devolvía los datos en XML. Decidí usar las clases XML de X++ (XmlDocument, XmlNodeList, XmlElement, etc…) para parsear el XML y acceder a los datos. Estas clases son horrorosas. Sacas el trabajo pero de una forma fea fea. Hay un método mejor para parsear XML o JSON en MSDyn365FO.

.NET al rescate

Hay una funcionalidad en Visual Studio que nos ayudará con esto, pero no está disponible para los proyectos de tipo Unified Oprations. Para usarlo sólo hay que abrir Visual Stuio y crear un proyecto de .NET. Ahora copias un ejemplo de XML que quieras parsear, vas al menú Edit, Paste Special, Paste XML As Classes:

Y con esto tendremos un contrato de datos con todos los elementos necesarios para acceder a todos los nodos del XML usando notación con puntos para accede a los datos! Por ejemplo, para este XML de ejemplo obtendremos este código:

namespace AASXMLHelper
{

    // NOTE: Generated code may require at least .NET Framework 4.5 or .NET Core/Standard 2.0.
    /// <remarks/>
    [System.SerializableAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
    [System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
    public partial class catalog
    {

        private catalogBook[] bookField;

        /// <remarks/>
        [System.Xml.Serialization.XmlElementAttribute("book")]
        public catalogBook[] book
        {
            get
            {
                return this.bookField;
            }
            set
            {
                this.bookField = value;
            }
        }
    }

    /// <remarks/>
    [System.SerializableAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
    public partial class catalogBook
    {

        private string authorField;

        private string titleField;

        private string genreField;

        private decimal priceField;

        private System.DateTime publish_dateField;

        private string descriptionField;

        private string idField;

        /// <remarks/>
        public string author
        {
            get
            {
                return this.authorField;
            }
            set
            {
                this.authorField = value;
            }
        }

        /// <remarks/>
        public string title
        {
            get
            {
                return this.titleField;
            }
            set
            {
                this.titleField = value;
            }
        }

        /// <remarks/>
        public string genre
        {
            get
            {
                return this.genreField;
            }
            set
            {
                this.genreField = value;
            }
        }

        /// <remarks/>
        public decimal price
        {
            get
            {
                return this.priceField;
            }
            set
            {
                this.priceField = value;
            }
        }

        /// <remarks/>
        [System.Xml.Serialization.XmlElementAttribute(DataType = "date")]
        public System.DateTime publish_date
        {
            get
            {
                return this.publish_dateField;
            }
            set
            {
                this.publish_dateField = value;
            }
        }

        /// <remarks/>
        public string description
        {
            get
            {
                return this.descriptionField;
            }
            set
            {
                this.descriptionField = value;
            }
        }

        /// <remarks/>
        [System.Xml.Serialization.XmlAttributeAttribute()]
        public string id
        {
            get
            {
                return this.idField;
            }
            set
            {
                this.idField = value;
            }
        }
    }


}

Podemos crear esto en una Class Library de .NET y consumirla desde Finance and Operations. Este es el método más rápido para usar todas las clases y miembros de estas. Seguramente se pueda implementar como clases de Dynamics 365 FnO, pero habrís que crear tantas clases como tipos distintos de nodos haya en el XML. Y el propósito original de esto era parsear el XML más rápido. Yo me quedaría con la librería de .NET.

Todos estos pasos también son válidos para un archivo JSON, copias el JSON, paste special y tenemos todas las clases para acceder a los datos.

Usándolo en Dynamics

Una vez tenemos la librería o las clases creadas en Microsoft Dynamics 365 (no hagáis esto anda) añadimos la referencia al proyecto y (siguiendo el ejemplo) hacemos lo siguiente:

catalog			catalog		= new catalog();
XmlSerializer	        serializer	= new XmlSerializer(catalog.GetType());
TextReader		sr	        = new StringReader(xmlSample);
        
catalog = serializer.Deserialize(sr);

catalogBook[]   books   = catalog.book;
catalogBook     book    = books.GetValue(0);

Declaramos una variable del mismo tipo que el nodo raíz (el principal), catalog en el ejemplo. Creamos un nuevo XmlSerializer usando nuestro tipo y creamos un TextReader a partir del XML como cadena. Finalmente necesitamos deserializar el XML y asignar el resultado al catalog y…

Como se ve, podemos acceder a los datos usando notación de puntos y las clases que se crearon al usar la función del pegado especial.

Con la ayuda de herramientas que no son específicas de la programación en X++ podemos conseguir esto, y, definitivamente, es más rápido que tener que parsear el XML usando las clases Xml de Dynamics.

¡Suscríbete!

Recibe un correo cuando se publique un nuevo post
Author

Microsoft Dynamics 365 Finance & Operations technical architect and developer. Business Applications MVP since 2020.

3 Comments

  1. Hola, muchas gracias por tu post. Tengo unas dudas al respecto, has utilizado esto para crear un XML? y al referenciar el proyecto me da el siguiente error: Common Language Runtime detected an invalid program. a lo mejor tienes una idea del origen

    gracias

    • Adrià Ariste Santacreu Reply

      Pues sólo lo he usado para parsear XML y JSON, no he intentado hacer el camino inverso. Entiendo que, por lo menos en la parte de .NET, te debería dejar montar tus clases, rellenar datos y luego serializarlo en un objeto.

      • He intentado realizarlo y al momento de probar se genera el siguiente error
        Common Language Runtime detected an invalid program.

        Alguna idea de como solucionarlo?
        gracias por tu ayuda

Write A Comment

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

ariste.info