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('&', '&'); lText := lText.Replace('<', '<'); lText := lText.Replace('>', '>'); lText := lText.Replace('"', '"'); lText := lText.Replace('''', '''); 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; }
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;
LikeLike
Hi Lars Westman,
Thank you, I didn’t had time to include more function’s. I will add this function to the codeunit. Thanks again
LikeLike
Pingback: AL and SOAP | larswestman.se
Pingback: Creating Soap Request Message in AL | Divesh Bora – MSD NAV Blog
Pingback: Creating Soap Request Message in AL - Microsoft Dynamics NAV Community
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.
LikeLike
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
LikeLike
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.
LikeLike
Hi Vikalp,
Which Version of the Product are you using ? Do you want to use the Rapid Start through code ? Please let me know the case.
LikeLike
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
LikeLike
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
LikeLike
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
LikeLike
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
LikeLike
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.
LikeLike
Thank you!
LikeLike
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
LikeLike
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;
LikeLike
Thank you Divesh!
LikeLike
Welcome!
LikeLiked by 1 person
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.
LikeLike
Thanks a lot for your excellent suggestions. I will create a git repo and move the code there.
LikeLike
Great!!!
LikeLike
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);
LikeLike
Looks like my xml paste wasnt allowed – trying again.
emailtest
1234
543212
LikeLike
Asked the question on the forum
https://community.dynamics.com/business/f/dynamics-365-business-central-forum/395337/creating-xml-soap-request-in-al
LikeLike
Hi Chris,
Sorry for the delay, I have replied to you in the https://community.dynamics.com/business/f/dynamics-365-business-central-forum/395337/creating-xml-soap-request-in-al .
Pasting the same solution here.
Below changes are required to get the desired output.
procedure CreateSoapMessage()
var
lXmlDocument: XmlDocument;
lEnvolopeXmlNode: XmlNode;
lHeaderXmlNode: XmlNode;
lBodyXmlNode: XmlNode;
lTempXmlNode: XmlNode;
lXMLText: Text;
ApiNameSpace: Label ‘http://api.url.com”‘;
begin
CreateSoapDocument(lXmlDocument, 1, lEnvolopeXmlNode, lHeaderXmlNode, lBodyXmlNode);
XMLDomMgt.AddElement(lBodyXmlNode, ‘VerifyToken’, ”, ApiNameSpace, lBodyXmlNode);
XMLDomMgt.AddElement(lBodyXmlNode, ’email’, ‘some@email.com’, ApiNameSpace, lTempXmlNode);
XMLDomMgt.AddElement(lBodyXmlNode, ‘password’, ‘password123’, ApiNameSpace, lTempXmlNode);
XMLDomMgt.AddElement(lBodyXmlNode, ‘token’, ‘token123423434235’, ApiNameSpace, lTempXmlNode);
lXmlDocument.WriteTo(lXMLText);
Message(lXMLText);
end;
Pass the ApiNameSpace also to email, password & token to get rid of the xmlns. Hope this helps.
Best Regards,
Divesh
LikeLike
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.
LikeLike
Hi Luis Munoz Castro,
Did you check this below post? https://diveshboramsdnavblog.wordpress.com/2018/05/24/creating-soap-request-message-in-al/
Hope most of the things are covered there. Please let me know if you need some help. Thanks
LikeLike
Hi
Thank you.
I compose the xml well but when sending it via http I always receive the same error:
SenderInvalid XML
I copy and paste the message of the execution of Soap test page in the SOAPUI program and it also gives error.
But if i remove the line , I run in SOAPUI and it works correctly.
LikeLike
Sorry, the line is:
?xml version=”1.0″ encoding=”UTF-8″ standalone=””?
LikeLike
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
LikeLike
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);
LikeLike
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
LikeLike
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.
LikeLike
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.
LikeLike
Hallo.
Perhaps you can help me.
I want to create this xml-Document. I was not able to define mor than pne namespace.
………..
LikeLike
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
LikeLike
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.
LikeLike
Hi Jose Joaquin,
Can you pleease provide me more information about the issue and send me your code to diveshbora@gmail.com?
Thanks
LikeLike
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.
LikeLike
This is awesome, thanks Divesh you saved my live
LikeLike
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
LikeLike
Sorry, I need to get
”
and I get
”
LikeLike
Thanks ,you saved our time Bro.
LikeLike