VS Code XML DOM Management – Part 2

In the previous post, I have published only few set of functions related to XML DOM Management.

Here is a set of additional functions which could be useful when working with XML. It is not fully tested though, In case any issues in the function, please let me know.

You can download this codeunit from GitHub. Hope this helps. Thanks

codeunit 50100 "XML DOM Mgt."
{
    trigger OnRun();
    begin
    end;

    procedure AddElement(var pXMLNode: XmlNode; pNodeName: Text; pNodeText: Text; pNameSpace: Text; var pCreatedNode: XmlNode): Boolean
    var
        lNewChildNode: XmlNode;
    begin
        if pNodeText <> '' then
            lNewChildNode := XmlElement.Create(pNodeName, pNameSpace, pNodeText).AsXmlNode()
        else
            lNewChildNode := XmlElement.Create(pNodeName, pNameSpace).AsXmlNode();
        if pXMLNode.AsXmlElement().Add(lNewChildNode) then begin
            pCreatedNode := lNewChildNode;
            exit(true);
        end;
    end;

    procedure AddRootElement(var pXMLDocument: XmlDocument; pNodeName: Text; var pCreatedNode: XmlNode): Boolean
    begin
        pCreatedNode := XmlElement.Create(pNodeName).AsXmlNode();
        exit(pXMLDocument.Add(pCreatedNode));
    end;

    procedure AddRootElementWithPrefix(var pXMLDocument: XmlDocument; pNodeName: Text; pPrefix: Text; pNameSpace: text; var pCreatedNode: XmlNode): Boolean
    begin
        pCreatedNode := XmlElement.Create(pNodeName, pNameSpace).AsXmlNode();
        pCreatedNode.AsXmlElement().Add(XmlAttribute.CreateNamespaceDeclaration(pPrefix, pNameSpace));
        exit(pXMLDocument.Add(pCreatedNode));
    end;

    procedure AddElementWithPrefix(var pXMLNode: XmlNode; pNodeName: Text; pNodeText: Text; pPrefix: Text; pNameSpace: text; var pCreatedNode: XmlNode): Boolean
    var
        lNewChildNode: XmlNode;
    begin
        if pNodeText <> '' then
            lNewChildNode := XmlElement.Create(pNodeName, pNameSpace, pNodeText).AsXmlNode()
        else
            lNewChildNode := XmlElement.Create(pNodeName, pNameSpace).AsXmlNode();
        lNewChildNode.AsXmlElement().Add(XmlAttribute.CreateNamespaceDeclaration(pPrefix, pNameSpace));
        if pXMLNode.AsXmlElement().Add(lNewChildNode) then begin
            pCreatedNode := lNewChildNode;
            exit(true);
        end;
    end;

    procedure AddPrefix(var pXMLNode: XmlNode; pPrefix: Text; pNameSpace: text): Boolean
    begin
        pXMLNode.AsXmlElement().Add(XmlAttribute.CreateNamespaceDeclaration(pPrefix, pNameSpace));
        exit(true);
    end;

    procedure AddAttribute(var pXMLNode: XmlNode; pName: Text; pValue: Text): Boolean
    begin
        pXMLNode.AsXmlElement().SetAttribute(pName, pValue);
        exit(true);
    end;

    procedure AddAttributeWithNamespace(var pXMLNode: XmlNode; pName: Text; pNameSpace: text; pValue: Text): Boolean
    begin
        pXMLNode.AsXmlElement().SetAttribute(pName, pNameSpace, pValue);
        exit(true);
    end;

    procedure FindNode(pXMLRootNode: XmlNode; pNodePath: Text; var pFoundXMLNode: XmlNode): Boolean
    begin
        exit(pXMLRootNode.SelectSingleNode(pNodePath, pFoundXMLNode));
    end;

    procedure FindNodeWithNameSpace(pXMLRootNode: XmlNode; pNodePath: Text; pPrefix: Text; pNamespace: Text; var pFoundXMLNode: XmlNode): Boolean
    var
        lXmlNsMgr: XmlNamespaceManager;
        lXMLDocument: XmlDocument;
    begin

        if pXMLRootNode.IsXmlDocument() then
            lXmlNsMgr.NameTable(pXMLRootNode.AsXmlDocument().NameTable())
        else begin
            pXMLRootNode.GetDocument(lXMLDocument);
            lXmlNsMgr.NameTable(lXMLDocument.NameTable());
        end;
        lXMLNsMgr.AddNamespace(pPrefix, pNamespace);
        exit(pXMLRootNode.SelectSingleNode(pNodePath, lXmlNsMgr, pFoundXMLNode));
    end;

    procedure FindNodesWithNameSpace(pXMLRootNode: XmlNode; pXPath: Text; pPrefix: Text; pNamespace: Text; var pFoundXmlNodeList: XmlNodeList): Boolean
    var
        lXmlNsMgr: XmlNamespaceManager;
        lXMLDocument: XmlDocument;
    begin
        if pXMLRootNode.IsXmlDocument() then
            lXmlNsMgr.NameTable(pXMLRootNode.AsXmlDocument().NameTable())
        else begin
            pXMLRootNode.GetDocument(lXMLDocument);
            lXmlNsMgr.NameTable(lXMLDocument.NameTable());
        end;
        lXMLNsMgr.AddNamespace(pPrefix, pNamespace);
        exit(FindNodesWithNamespaceManager(pXMLRootNode, pXPath, lXmlNsMgr, pFoundXmlNodeList));
    end;

    procedure FindNodesWithNamespaceManager(pXMLRootNode: XmlNode; pXPath: Text; pXmlNsMgr: XmlNamespaceManager; var pFoundXmlNodeList: XmlNodeList): Boolean
    begin
        if not pXMLRootNode.SelectNodes(pXPath, pXmlNsMgr, pFoundXmlNodeList) then
            exit(false);
        if pFoundXmlNodeList.Count() = 0 then
            exit(false);
        exit(true);
    end;

    procedure FindNodeXML(pXMLRootNode: XmlNode; pNodePath: Text): Text
    var
        lFoundXMLNode: XmlNode;
    begin
        if pXMLRootNode.SelectSingleNode(pNodePath, lFoundXMLNode) then
            exit(lFoundXMLNode.AsXmlElement().InnerXml());
    end;

    procedure FindNodeText(pXMLRootNode: XmlNode; pNodePath: Text): Text
    var
        lFoundXMLNode: XmlNode;
    begin
        if pXMLRootNode.SelectSingleNode(pNodePath, lFoundXMLNode) then
            exit(lFoundXMLNode.AsXmlElement().InnerText());
    end;

    procedure FindNodeTextWithNameSpace(pXMLRootNode: XmlNode; pNodePath: Text; pPrefix: Text; pNamespace: Text): Text
    var
        lXmlNsMgr: XmlNamespaceManager;
        lXMLDocument: XmlDocument;
    begin

        if pXMLRootNode.IsXmlDocument() then
            lXmlNsMgr.NameTable(pXMLRootNode.AsXmlDocument().NameTable())
        else begin
            pXMLRootNode.GetDocument(lXMLDocument);
            lXmlNsMgr.NameTable(lXMLDocument.NameTable());
        end;
        lXMLNsMgr.AddNamespace(pPrefix, pNamespace);
        exit(FindNodeTextNs(pXMLRootNode, pNodePath, lXmlNsMgr));
    end;

    procedure FindNodeTextNs(pXMLRootNode: XmlNode; pNodePath: Text; pXmlNsMgr: XmlNamespaceManager): Text
    var
        lFoundXMLNode: XmlNode;
    begin
        if pXMLRootNode.SelectSingleNode(pNodePath, pXmlNsMgr, lFoundXMLNode) then
            exit(lFoundXMLNode.AsXmlElement().InnerText());
    end;

    procedure FindNodes(pXMLRootNode: XmlNode; pNodePath: Text; var pFoundXMLNodeList: XmlNodeList): Boolean
    begin
        if not pXMLRootNode.SelectNodes(pNodePath, pFoundXmlNodeList) then
            exit(false);
        if pFoundXmlNodeList.Count() = 0 then
            exit(false);
        exit(true);
    end;

    procedure GetRootNode(pXMLDocument: XmlDocument; var pFoundXMLNode: XmlNode): Boolean
    var
        lXmlElement: XmlElement;
    begin
        pXMLDocument.GetRoot(lXmlElement);
        pFoundXMLNode := lXmlElement.AsXmlNode();
    end;

    procedure FindAttribute(pXMLNode: XmlNode; var pXmlAttribute: XmlAttribute; pAttributeName: Text): Boolean
    begin
        exit(pXMLNode.AsXmlElement().Attributes().Get(pAttributeName, pXmlAttribute));
    end;

    procedure GetAttributeValue(pXMLNode: XmlNode; pAttributeName: Text): Text
    var
        lXmlAttribute: XmlAttribute;
    begin
        if pXMLNode.AsXmlElement().Attributes().Get(pAttributeName, lXmlAttribute) then
            exit(lXmlAttribute.Value());
    end;

    procedure AddDeclaration(var pXMLDocument: XmlDocument; pVersion: Text; pEncoding: Text; pStandalone: Text)
    var
        lXmlDeclaration: XmlDeclaration;
    begin
        lXmlDeclaration := XmlDeclaration.Create(pVersion, pEncoding, pStandalone);
        pXMLDocument.SetDeclaration(lXmlDeclaration);
    end;

    procedure AddGroupNode(var pXMLNode: XmlNode; pNodeName: Text)
    var
        lXMLNewChild: XmlNode;
    begin
        AddElement(pXMLNode, pNodeName, '', '', lXMLNewChild);
        pXMLNode := lXMLNewChild;
    end;

    procedure AddNode(var pXMLNode: XmlNode; pNodeName: Text; pNodeText: Text)
    var
        lXMLNewChild: XmlNode;
    begin
        AddElement(pXMLNode, pNodeName, pNodeText, '', lXMLNewChild);
    end;

    procedure AddLastNode(var pXMLNode: XmlNode; pNodeName: Text; pNodeText: Text)
    var
        lXMLNewChild: XmlNode;
        lXMLElement: XmlElement;
    begin
        AddElement(pXMLNode, pNodeName, pNodeText, '', lXMLNewChild);
        if pXMLNode.GetParent(lXMLElement) then
            pXMLNode := lXMLElement.AsXmlNode();
    end;

    procedure GetXmlNSMgr(pXMLRootNode: XmlNode; pPrefix: Text; pNamespace: Text; var pXmlNsMgr: XmlNamespaceManager): Text
    var
        lXMLDocument: XmlDocument;
    begin

        if pXMLRootNode.IsXmlDocument() then
            pXmlNsMgr.NameTable(pXMLRootNode.AsXmlDocument().NameTable())
        else begin
            pXMLRootNode.GetDocument(lXMLDocument);
            pXmlNsMgr.NameTable(lXMLDocument.NameTable());
        end;
        pXmlNsMgr.AddNamespace(pPrefix, pNamespace);
    end;

    procedure AddNameSpace(var pXmlNsMgr: XmlNamespaceManager; pPrefix: text; pNamespace: text);
    begin
        pXmlNsMgr.AddNamespace(pPrefix, pNamespace);
    end;

    procedure AddNamespaces(var pXmlNsMgr: XmlNamespaceManager; pXMLDocument: XmlDocument)
    var
        lXmlAttributeCollection: XmlAttributeCollection;
        lXmlAttribute: XmlAttribute;
        lXMLElement: XmlElement;
    begin
        pXmlNsMgr.NameTable(pXMLDocument.NameTable());
        pXMLDocument.GetRoot(lXMLElement);
        lXmlAttributeCollection := lXMLElement.Attributes();
        if lXMLElement.NamespaceUri() <> '' then
            pXmlNsMgr.AddNamespace('', lXMLElement.NamespaceUri());
        Foreach lXmlAttribute in lXmlAttributeCollection do
            if StrPos(lXmlAttribute.Name(), 'xmlns:') = 1 then
                pXmlNsMgr.AddNamespace(DELSTR(lXmlAttribute.Name(), 1, 6), lXmlAttribute.Value());
    end;

    procedure XMLEscape(pXMLText: Text): Text
    var       
        lNewXmlNode: XmlNode;
    begin        
        lNewXmlNode := XmlElement.Create('XMLEscape', '', pXMLText).AsXmlNode();        
        exit(lNewXmlNode.AsXmlElement().InnerXml());
    end;

    procedure LoadXMLDocumentFromText(pXMLText: Text; var pXMLDocument: XmlDocument)
    begin
        if pXMLText = '' then
            exit;
        XmlDocument.ReadFrom(pXMLText, pXMLDocument);
    end;

    procedure LoadXMLNodeFromText(pXMLText: Text; var pXMLNode: XmlNode)
    var
        lXmlDocument: XmlDocument;
    begin
        LoadXMLDocumentFromText(pXMLText, lXmlDocument);
        pXMLNode := lXmlDocument.AsXmlNode();
    end;

    procedure LoadXMLDocumentFromInStream(pInStream: InStream; var pXMLDocument: XmlDocument)
    begin
        XmlDocument.ReadFrom(pInStream, pXMLDocument);
    end;

    procedure LoadXMLNodeFromInStream(pInStream: InStream; var pXMLNode: XmlNode)
    var
        lXmlDocument: XmlDocument;
    begin
        LoadXMLDocumentFromInStream(pInStream, lXmlDocument);
        pXMLNode := lXmlDocument.AsXmlNode();
    end;

    procedure RemoveNamespaces(pXmlText: Text): Text
    var
        XMLDOMMgt: Codeunit "XML DOM Management";
    begin
        exit(XMLDOMMgt.RemoveNamespaces(pXmlText));
    end;

    procedure SetUTF88Declaration(var pXMLDocument: XmlDocument; pStandaloneTxt: Text);
    var
        lXmlDeclaration: XmlDeclaration;
    begin
        lXmlDeclaration := XmlDeclaration.Create('1.0', 'utf-8', pStandaloneTxt);
        pXMLDocument.SetDeclaration(lXmlDeclaration);
    end;

    procedure ReplaceInvalidXMLCharacters(pText: Text): Text
    var
        lText: Text;
    begin
        lText := pText;
        lText := lText.Replace('&', '&amp;');
        lText := lText.Replace('<', '&lt;');
        lText := lText.Replace('>', '&gt;');
        lText := lText.Replace('"', '&quot;');
        lText := lText.Replace('''', '&apos;');
        exit(lText);
    end;

    procedure RemoveXMLRestrictedCharacters(pXmlText: Text): Text
    var
        lXmlText: Text;
        lChar: array[30] of Char;
        lArrayLen: Integer;
        li: Integer;
    begin

        if pXmlText = '' then
            exit(pXmlText);

        lXmlText := pXmlText;

        lChar[1] := 1;
        lChar[2] := 2;
        lChar[3] := 3;
        lChar[4] := 4;
        lChar[5] := 5;
        lChar[6] := 6;
        lChar[7] := 7;
        lChar[8] := 8;
        lChar[9] := 11;
        lChar[10] := 12;
        lChar[11] := 14;
        lChar[12] := 15;
        lChar[13] := 16;
        lChar[14] := 17;
        lChar[15] := 18;
        lChar[16] := 19;
        lChar[17] := 20;
        lChar[18] := 21;
        lChar[19] := 22;
        lChar[20] := 23;
        lChar[21] := 24;
        lChar[22] := 25;
        lChar[23] := 26;
        lChar[24] := 27;
        lChar[25] := 28;
        lChar[26] := 29;
        lChar[27] := 30;
        lChar[28] := 31;
        lChar[29] := 127;

        lArrayLen := ArrayLen(lChar);
        for li := 1 to lArrayLen do
            lXmlText := DelChr(lXmlText, '=', lChar[li]);
        exit(lXmlText);
    end;
}

44 thoughts on “VS Code XML DOM Management – Part 2

  1. Thank’s Divesh for taking your time with this. It saved me some time!

    I added this to be able to add more namespaces to my root element:

    procedure AddPrefix(var pXMLNode: XmlNode; pPrefix: Text; pNameSpace: text): Boolean
    begin
    pXMLNode.AsXmlElement.Add(XmlAttribute.CreateNamespaceDeclaration(pPrefix, pNameSpace));
    exit(true);
    end;

    Like

  2. Pingback: AL and SOAP | larswestman.se

  3. Pingback: Creating Soap Request Message in AL | Divesh Bora – MSD NAV Blog

  4. Pingback: Creating Soap Request Message in AL - Microsoft Dynamics NAV Community

  5. Hi Divesh,
    I am working on API and wrote code in CAL for accessing each XML tag value. But now I want to write code in AL, but don’t know how to write. Please help me:

    **My XML Content [XmlText] snippet:**

    https://www.mywebsite.com/
    0_YdG/DRSVGCKyy6fx/fdasdY6avs=
    05/28/2019 11:50:14 PM
    0_i1G4fADRTVRDfdsac4GjD5R5UoOPks

    For reference you can look into my previous CAL code:
    **getToken(XmlText : Text) : Text**
    XmlDocument:=XmlDocument.XmlDocument;
    XmlDocument.LoadXml(XmlText);
    XMLNodeList := XmlDocument.GetElementsByTagName(‘Access_Token’);
    count:=XMLNodeList.Count;

    //First for loop to iterate the Access_Token Node– START
    FOR I := 0 TO count – 1 DO BEGIN
    DOMNode := XMLNodeList.Item(I);
    TempXMLNodeList:= DOMNode.ChildNodes;
    childcount:= TempXMLNodeList.Count;
    DOMChildNode:=TempXMLNodeList.Item(I);
    FOR J := 0 TO childcount – 1 DO BEGIN
    DOMChildNode:=TempXMLNodeList.Item(J);
    IF ( FORMAT(DOMChildNode.NodeType)=’Text’ ) OR ( FORMAT(DOMChildNode.NodeType)=’Element’ ) THEN BEGIN
    CurrentElementName:=DOMChildNode.Name;
    IF CurrentElementName = ‘Instance_Url’ THEN BEGIN
    Instance_URL := DOMChildNode.InnerText;
    END
    ELSE IF CurrentElementName = ‘YourToken’ THEN BEGIN
    TOKEN := DOMChildNode.InnerText;
    END
    ELSE IF CurrentElementName = ‘Expiration_date’ THEN BEGIN
    Expiration_date := DOMChildNode.InnerText;
    END
    ELSE IF CurrentElementName = ‘Refresh_Token’ THEN BEGIN
    Refresh_Token := DOMChildNode.InnerText;
    END
    END; // END of major If condition
    END; //Second for loop to iterate the child nodes Access_Token Node — END
    END;//First for loop to iterate the Parent or first node : Access_Token Node — END

    Thanks in Advance.

    Please tell or share code to read XML node values in AL, Most of the Features are not working.

    Like

    • Hi Tabrez,

      Below is the example to read xml in AL as per your requirement.

      codeunit 50101 “Read Xml”
      {
      trigger OnRun()
      var
      lXmlText: Text;
      begin
      lXmlText := ”;
      lXmlText += ”;
      lXmlText += ‘URl1’;
      lXmlText += ”;
      lXmlText += ‘TOKEN 1’;
      lXmlText += ”;
      lXmlText += ”;

      lXmlText += ”;
      lXmlText += ‘URl2’;
      lXmlText += ”;
      lXmlText += ‘TOKEN 2’;
      lXmlText += ”;
      lXmlText += ”;

      lXmlText += ”;
      lXmlText += ‘URL21’;
      lXmlText += ”;
      lXmlText += ‘TOKEN 21’;
      lXmlText += ”;
      lXmlText += ”;

      lXmlText += ”;
      ReadXml(lXmlText);
      end;

      local procedure ReadXml(pXmlText: Text);
      var
      XmlDOMMgt: Codeunit “XML DOM Mgt.”;
      lXmlDocument: XmlDocument;
      lXmlRootNode: XmlNode;
      lAccessTokenList: XmlNodeList;
      lNodeList: XmlNodeList;
      lXMLNode: XmlNode;
      lTempNode: XmlNode;
      begin
      XmlDOMMgt.LoadXMLDocumentFromText(pXmlText, lXmlDocument);
      XmlDOMMgt.GetRootNode(lXmlDocument, lXmlRootNode);
      if not XmlDOMMgt.FindNodes(lXmlRootNode, ‘Access_Token’, lAccessTokenList) then
      error(‘Node Access_Token not found’);
      foreach lXMLNode in lAccessTokenList do begin
      lNodeList := lXMLNode.AsXmlElement().GetChildNodes();
      foreach lTempNode in lNodeList do
      case lTempNode.AsXmlElement().Name() of
      ‘Instance_Url’:
      Message(lTempNode.AsXmlElement().InnerText());
      ‘YourToken’:
      Message(lTempNode.AsXmlElement().InnerText());
      end;
      end;
      end;
      }

      Hope this helps. Please like and share the post if you think it helped. Thanks

      Like

  6. Hi Divesh,

    We need to write the code in AL to load the xml file saved on server. In CAL the Code is like :
    procedure ReadPackageHeader(DecompressedFileName: Text)
    var
    XMLDOMManagement: Codeunit “XML DOM Management”;
    PackageXML: DotNet XmlDocument;
    begin
    if “Package File Name” ” then begin
    XMLDOMManagement.LoadXMLDocumentFromFile(DecompressedFileName,PackageXML);
    ReadPackageHeaderCommon(PackageXML);
    end else begin
    “Package Code” := ”;
    “Package Name” := ”;
    “Product Version” := ”;
    “Language ID” := 0;
    end;
    end;
    local procedure ReadPackageHeaderCommon(PackageXML: DotNet XmlDocument)
    var
    ConfigPackage: Record “Config. Package”;
    ConfigXMLExchange: Codeunit “Config. XML Exchange”;
    DocumentElement: DotNet XmlElement;
    LanguageID: Text;
    begin
    DocumentElement := PackageXML.DocumentElement;
    “Package Code” :=
    CopyStr(
    ConfigXMLExchange.GetAttribute(
    ConfigXMLExchange.GetElementName(ConfigPackage.FieldName(Code)),DocumentElement),
    1,MaxStrLen(“Package Code”));
    if “Package Code” = ” then
    Error(PackageDataNotDefinedErr,FieldCaption(“Package Code”));
    “Package Name” :=
    CopyStr(
    ConfigXMLExchange.GetAttribute(
    ConfigXMLExchange.GetElementName(ConfigPackage.FieldName(“Package Name”)),DocumentElement),
    1,MaxStrLen(“Package Name”));
    if “Package Name” = ” then
    Error(PackageDataNotDefinedErr,FieldCaption(“Package Name”));
    “Product Version” :=
    CopyStr(
    ConfigXMLExchange.GetAttribute(
    ConfigXMLExchange.GetElementName(ConfigPackage.FieldName(“Product Version”)),DocumentElement),
    1,MaxStrLen(“Product Version”));
    LanguageID := ConfigXMLExchange.GetAttribute(
    ConfigXMLExchange.GetElementName(ConfigPackage.FieldName(“Language ID”)),DocumentElement);
    if LanguageID ” then
    Evaluate(“Language ID”,LanguageID);
    Modify;
    end;
    I have tried the XmlDocument.ReadFrom but it is only useful when i have the xml in text variable. May i know to how we can load the xml files in AL programming.
    Thanks in Advance.

    Like

  7. Hi Divesh,
    First of all thank you very much for sharing this valuable informations and blog.
    I am facing an urgent problem to convert a report using dotnet to export xml, I wanted to re write the code using your method, I was successful to convert most of the code but I need your help to convert this function please :
    local procedure ExportSEPAFile()
    var

    XMLRootElement: XmlElement;
    XMLNodeCurr: XmlNode;
    XMLNewChild: XmlNode;
    begin
    XMLDOCMGT.LoadXMLDocumentFromText(”, XMLDomDoc);
    XMLRootElement := XMLDomDoc.DocumentElement; (this line is not supported in AL)
    XMLRootElement.SetAttribute(‘xmlns’, ‘urn:iso:std:iso:20022:tech:xsd:pain.001.001.02’);
    XMLRootElement.SetAttribute(‘xmlns:xsi’, ‘http://www.w3.org/2001/XMLSchemainstance’);
    XMLNodeCurr := XMLDomDoc.SelectSingleNode(‘Document’); (this line is not supported in AL)
    XMLDOMManagement.AddElement(XMLNodeCurr, ‘pain.001.001.02’, ”, ”, XMLNewChild);

    ExportGroupHeader(XMLNewChild);
    ExportPaymentInformation(XMLNewChild);

    XMLDomDoc.Save(FileName); (this line is not supported in AL)
    Clear(XMLDomDoc);
    end;

    THANKS IN ADVANCE

    Like

    • Hi Brahim,

      Please find the below code which will create xml file. I have not tested this, but it should work.

      procedure XMLExample()
      var
      lTempBlob: Record TempBlob temporary;
      lFileMgt: Codeunit “File Management”;
      lNavOutStream: OutStream;
      XMLDomDoc_Inv: XmlDocument;
      lXmlRootElement: XmlElement;
      XmlRootNode: XmlNode;
      lProcessingTxt: Label ”, Locked = true;
      lRootElementTxt: Label ”, Locked = true;
      begin
      //Add Processing Instructions with Root
      XMLDomDoc_Inv := XmlDocument.Create();
      XMLDomMgt.LoadXMLDocumentFromText(lProcessingTxt + lRootElementTxt, XMLDomDoc_Inv);

      //Get the Rootl Element
      XMLDomDoc_Inv.GetRoot(lXmlRootElement);
      XmlRootNode := lXmlRootElement.AsXmlNode();

      //Add Your Logic Here to Append the Elements to XML

      //Write to Outstream
      lTempBlob.Init();
      lTempBlob.Blob.CreateOutStream(lNavOutStream);
      XMLDomDoc_Inv.WriteTo(lNavOutStream);

      //Export the Outstream to File
      lFileMgt.BLOBExport(lTempBlob, ‘FileName.xml’, true);
      end;

      Let me know if you encounter any issues with this.

      Best Regards,
      Divesh

      Like

  8. Hi Divesh,
    Thank you very much for your solution,
    in my business logic this two lines generates errors :
    XMLRootElement.SetAttribute(‘xmlns’, ‘urn:iso:std:iso:20022:tech:xsd:pain.001.001.02’);
    XMLRootElement.SetAttribute(‘xmlns:xsi’, ‘http://www.w3.org/2001/XMLSchemainstance’);
    the error is : The prefix ” cannot be redefined from ” to ‘namespaceURI’ within the same start element tag

    Like

    • Hi Brahim,

      Please find the code which will help you to create prefix.

      procedure XMLExample()
      var
      XMLDomDoc: XmlDocument;
      lXMLRootElement: XmlElement;
      lXmlRootNode: XmlNode;
      lXMLNewChild: XmlNode;
      lHeaderTxt: Text;
      lElementName: Text[250];
      lXmlText: Text;
      PainURNTxt: Label ‘urn:iso:std:iso:20022:tech:xsd:pain.001.001.02’;
      XsiURNTxt: Label ‘http://www.w3.org/2001/XMLSchemainstance’;
      begin

      lHeaderTxt := ”;
      lHeaderTxt += ”;
      lHeaderTxt += ”;

      XMLDomMgt.LoadXMLDocumentFromText(lHeaderTxt, XMLDomDoc);

      XMLDomDoc.GetRoot(lXMLRootElement);
      lXmlRootNode := lXMLRootElement.AsXmlNode();

      //Element created with existing namspace which does not have prefix
      XMLDomMgt.AddElement(lXmlRootNode, ‘ALXML’, ”, PainURNTxt, lXMLNewChild);

      //Element created with existing namspace which have prefix
      XMLDomMgt.AddElement(lXmlRootNode, ‘ALXMLPrefix’, ”, XsiURNTxt, lXMLNewChild);

      //Element created with new namspace & prefix
      XMLDomMgt.AddElementWithPrefix(lXmlRootNode, ‘ALXMLPrefix’, ”, ‘Temp’, ‘http://temp.org’, lXMLNewChild);

      XMLDomDoc.WriteTo(lXmlText);
      Message(lXmlText);
      end;

      Below is the output:

      Hope this helps.

      Best Regards,
      Divesh

      Like

  9. Thank you very much. I began doing the same (adapting XML DOM mngt. functions to new Al objects) before I notice you already did it. Great work.

    Like

  10. Hi Dives,

    I have been following along with this article on how to create a custom workflow in BC (SaaS) https://www.linkedin.com/pulse/claim-workflow-template-mehdi-meddah/. I haven’t had any issues at all until the part where I created this procedure:

    procedure BuildVacancyTypeConditions(): Text
    var
    Vacancy: Record JobVacancies_PFC;
    begin
    exit(StrSubstNo(VacancyTypeCondnTxt, Encode(Vacancy.GetView(false))));
    end;

    The above procedure calls the Encode procedure which is as follows:

    procedure Encode(Text: Text): Text
    var
    XMLDOMManagement: Codeunit “XML DOM Management”;
    begin
    exit(XMLDOMManagement.XMLEscape(Text));
    end;

    The above procedure procedure references the XML DOM Management Codeunit and the XMLEscape procedure which is as follows:

    [Scope(‘OnPrem’)]
    procedure XMLEscape(Text: Text): Text
    var
    XMLDocument: DotNet XmlDocument;
    XMLNode: DotNet XmlNode;
    begin
    XMLDocument := XMLDocument.XmlDocument;
    XMLNode := XMLDocument.CreateElement(‘XMLEscape’);

    XMLNode.InnerText(Text);
    exit(XMLNode.InnerXml);
    end;

    I am unable to use this procedure in an Extension so I used your code from above:

    procedure XMLEscape(pText: Text): Text
    var
    lXMLDocument: XmlDocument;
    lRootXmlNode: XmlNode;
    lXmlNode: XmlNode;
    begin
    lXMLDocument := XmlDocument.Create();
    AddElement(lRootXmlNode, ‘XMLEscape’, pText, ”, lXmlNode);
    exit(lXmlNode.AsXmlElement().InnerXml());
    end;

    Unfortunately, I have an error: The name ‘AddElement’ does not exist in the current contextAL(AL0118). I am struggling to get this working and it is literally the only thing that is stopping me from getting my extension published. Do you know how I can get this working?

    Many thanks,
    James

    Like

    • Hi James,

      Thanks for reporting, I have fixed the issue. Below is the working code for XMLEscape.

      procedure XMLEscape(pXMLText: Text): Text
      var
      lNewXmlNode: XmlNode;
      begin
      lNewXmlNode := XmlElement.Create(‘XMLEscape’, ”, pXMLText).AsXmlNode();
      exit(lNewXmlNode.AsXmlElement().InnerXml());
      end;

      Like

  11. Divesh, did you think about make a git repo with this Codeunit? I think it is a nice job and worth to share and be supported there. You are doing the same job, but doing it in a blog post.

    Like

  12. Hi Diveshbora,
    Thanks for the post!

    Was wondering if you could help me out.

    emailtest
    test1234
    token123423434235

    Trying to get above – but without xmlns in the email, password & token element also without soap:header if you know how to.

    This is my code currently – using your example code.
    XMLDomMgt.AddElement(lBodyXmlNode, ‘veri’, ”, MilestoneApiNameSpace, lBodyXmlNode);
    XMLDomMgt.AddElement(lBodyXmlNode, ’email’, ’emailtest’, ”, lTempXmlNode);
    XMLDomMgt.AddElement(lBodyXmlNode, ‘password’, ‘test1234’, ”, lTempXmlNode);
    XMLDomMgt.AddElement(lBodyXmlNode, ‘token’, ‘token123423434235’, ”, lTempXmlNode);

    Like

  13. Hi Diveshbora,
    Thanks for the post!

    Was wondering if you could help me out.

    I am trying to pass this code to AL

    XMLDoc := XMLDoc.XmlDocument;

    XMLDOMManagement.AddRootElementWithPrefix(XMLDoc,’Envelope’,’soapenv’,SoapEnvTxt,RootXMLNode);
    XMLDOMManagement.AddDeclaration(XMLDoc,’1.0′,’UTF-8′,”);
    XMLDOMManagement.AddElementWithPrefix(RootXMLNode,’Header’,”,’soapenv’,SoapEnvTxt,CurrentXMlNode);
    XMLDOMManagement.AddElementWithPrefix(RootXMLNode,’Body’,”,’soapenv’,SoapEnvTxt,SoapBodyXMlNode);

    XMLNamespaceManager := XMLNamespaceManager.XmlNamespaceManager(RootXMLNode.OwnerDocument.NameTable);

    XML_functions.AddNameSpaceToRoot(‘urn’,UrnTxt);
    XML_functions.AddNameSpaceToRoot(‘xsi’,xsiTxt);
    XML_functions.AddNameSpaceToRoot(‘xsd’,xsdTxt);

    XMLDOMManagement.AddElementWithPrefix(CurrentXMlNode,’login’,”,’urn’,UrnTxt,CurrentXMlNode);
    XMLDOMManagement.AddAttributeWithPrefix(CurrentXMlNode,’encodingStyle’,’soapenv’,SoapEnvTxt,SoapEncodeTxt);

    XMLDOMManagement.AddElement(CurrentXMlNode,’username’,username,”,CurrentXMlNode);
    XMLDOMManagement.FindNode(CurrentXMlNode,’..’,CurrentXMlNode);

    XMLDOMManagement.AddElement(CurrentXMlNode,’apiKey’,apiKey,”,CurrentXMlNode);

    RequestText := XMLDoc.OuterXml;

    AddNameSpaceToRoot(NameSpace : Text;URI : Text)
    XMLDOMManagement.AddAttribute(RootXMLNode,’xmlns:’ + NameSpace,URI);
    XMLNamespaceManager.AddNamespace(‘NameSpace’,URI);

    Thank you.

    Like

    • Hi Luis,

      When you test with SoapUI tool it doesn’t require XML declaration or processing instructions to be given in the request. You have to remove them when testing with SOAPUI. Moreover there is also another issue in your code, Standalone should be either yes or no and it should not be blank. Please check and let me know if you still have issues. Thanks

      Like

      • I have noticed that I have the error when I send http because I have transferred the xml to my functionality in CAL and it responds correctly.
        This is my code for sending http in AL, do you see any error?

        Client: HttpClient;
        Content: HttpContent;
        Request: HttpRequestMessage;
        HttpresponseMess: HttpResponseMessage;
        StatusDescription: Text[250];
        Headers: HttpHeaders;
        Response: HttpResponseMessage;
        begin
        Content.WriteFrom(RequestText);
        Content.GetHeaders(Headers);
        Headers.Remove(‘Content-Type’);
        Headers.Add(‘Content-Type’, ‘text/xml;charset=utf-8’);
        Client.SetBaseAddress(WebServiceUrl);
        Client.Post(WebServiceUrl, Content, Response);
        Client.Send(Request, Response);
        Content := Response.Content;
        Content.ReadAs(ResponseText);

        Like

      • Hi

        There is an issue with the code, you are trying to invoke Client.Post and Client.Send. Client.Post should be enough if you are not using the HttpRequestMessage, but if you want to use the HttpRequestMessage then update the HttpContent to HttpRequestMessage & then use the Client.Send. You should not use both Client.Post and Client.Send, in your case Client.Post is enough. Please remove or comment out the Client.Send code and then test.
        More information about the HttpClient can be found in https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/methods-auto/httpclient/httpclient-data-type .

        Thanks

        Like

  14. Hi diveshbora,

    I have modified my code like this, the problem is that it keeps giving me httpstatuscode 500 and the responsetext, faultcode = sender, faultstring = invalid XML

    Client: HttpClient;
    Content: HttpContent;
    Request: HttpRequestMessage;
    HttpresponseMess: HttpResponseMessage;
    StatusDescription: Text[250];
    Headers: HttpHeaders;
    Response: HttpResponseMessage;
    Sucess: Boolean;
    Content2: HttpContent;

    begin
    Content.Clear();
    Client.Clear();
    Headers.Clear();
    Request.Content.Clear();
    HttpresponseMess.Content.Clear();
    HttpresponseMess.Headers.Clear();
    Sucess := false;
    //init parameters
    Request.SetRequestUri(WebServiceUrl);
    Request.Method := ‘POST’;
    Content.WriteFrom(RequestText);
    Content.GetHeaders(Headers);
    Headers.Remove(‘Content-Type’);
    //Headers.Add(‘Content-Type’, ‘application/x-www-form-urlencoded’);
    Headers.Add(‘Content-Type’, ‘application/xml’);
    Request.Content := Content;
    if Client.Send(Request, Response) then;

    //Retrieve Response
    Content2 := Response.Content();
    Content2.ReadAs(ResponseText);
    Sucess := Response.IsSuccessStatusCode;

    I don’t understand it because I take the same requesttext to my code in CAL and it works correctly …

    Thenk you.

    Like

  15. Hei,

    Have you managed to find a solution for the Transformation (XSL) functions included in the “XML DOM Mgt. ” codeunit? I cant seem to find a way to do this with the new AL XML variables.

    Like

  16. Hallo.

    Perhaps you can help me.
    I want to create this xml-Document. I was not able to define mor than pne namespace.

    XX?xml version=”1.0″ encoding=”UTF-8″?YY
    XXrsm:CrossIndustryInvoice
    xmlns:rsm=”urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100″
    xmlns:ram=”urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100″
    xmlns:udt=”urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100″
    xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
    xsi:schemaLocation=”urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:
    100 ../../../schemas/UN_CEFACT/CrossIndustryInvoice_100pD16B.xsd”YY
    XXxxxYYXX/xxxYY
    XX/rsm:CrossIndustryInvoiceYY

    Like

  17. Hi diveshbora,
    Thank you ever so much for sharing your knowledge and code.

    I’m having this issue with procedure AddElementWithPrefix. When I use it, i get this error: Microsoft.Dynamics.Nav.Types.Exceptions.NavNCLArgumentException
    Could you help me please?

    Cheers.

    Like

  18. Hi friend, I tested the codeunit and I receive this error on AddElement Function:

    Unable to convert from Microsoft.Dynamics.Nav.Runtime.NavXmlNode to Microsoft.Dynamics.Nav.Runtime.NavXmlElement.

    Like

  19. First of all, thank your for all your post, they are really helpful.
    I’m having some issues with namespaces.
    I must get this:

    But I get this:

    It must be a really easy question, but I don’t get the way…

    Thank you again

    Like

Leave a reply to Jesus Almaraz Martín Cancel reply