How to use XML DOM

Hi all! Please help - I’m going nuts with that XML DOM [V] (it’s the first time I have to deal with it [:I]) All I need is to create a simple XML Document, using the XML DOM. I am looking for a SIMPLE - step by step - discription of how to do this in NAVISION. Please, do not advice any of the existing XML stuff in NAVISION - I’m done with this, also MSDN and other Web-Sources are not really helpful … So, could anybody give me a simple code example? Thanks in advance! (Now I need a coffee, see ya)

I agree, its like the old C/FRONT days, remember when all you wanted was a ‘hello world’ example.

It’s not that hard to do. If you use version 4.0, you don’t even need any of this, you can simply use XMLPorts. Copy/Paste away, just a little sample to create a sales order XML document. I removed a lot of the code of the one we use, because of references to customer specific functionality, so there may be variables that are not used. I also have not tested this particular codeunit yet, so you may have to tweak it to make it actually work. You should get an idea of how to use the DOM though. OBJECT Codeunit 99999 Create Sales Order XML { OBJECT-PROPERTIES { Date=06/13/05; Time=[ 8:50:21 AM]; Modified=Yes; Version List=; } PROPERTIES { OnRun=BEGIN SalesHeader.SETFILTER("Document Type",'=%1',SalesHeader."Document Type"::Order); IF FORM.RUNMODAL(45,SalesHeader) = ACTION::LookupOK THEN CreateXMLDoc(SalesHeader); END; } CODE { VAR XMLDOMDocument@1105400002 : Automation "{F5078F18-C551-11D3-89B9-0000F81FE221} 3.0:{F6D90F11-9C73-11D3-B32E-00C04F990BB4}:'Microsoft XML, v3.0'.DOMDocument"; CurrNode@1105400001 : Automation "{F5078F18-C551-11D3-89B9-0000F81FE221} 3.0:{2933BF80-7B36-11D2-B20E-00C04F983E60}:'Microsoft XML, v3.0'.IXMLDOMNode"; NewChild@1105400000 : Automation "{F5078F18-C551-11D3-89B9-0000F81FE221} 3.0:{2933BF80-7B36-11D2-B20E-00C04F983E60}:'Microsoft XML, v3.0'.IXMLDOMNode"; CompanyInfo@1105400003 : Record 79; Customer@1105400008 : Record 18; SalesHeader@1105400005 : Record 36; SalesLine@1105400004 : Record 37; XMLDOMManagement@1105400009 : Codeunit 6224; DocNameSpace@1105400007 : Text[200]; FieldValue@1105400006 : Text[50]; PROCEDURE CreateXMLDoc@1105400000(parSalesHeader@1105400000 : Record 36) ReturnCode : Integer; BEGIN IF NOT CREATE(XMLDOMDocument) THEN ERROR('could not create DOM Document'); SalesLine.SETRANGE("Document Type",SalesLine."Document Type"::Order); SalesLine.SETRANGE( "Document No.",parSalesHeader."No."); IF NOT CompanyInfo.FIND('-') THEN ERROR('could not find company info'); XMLDOMDocument.loadXML := '<navision_sales_order/>'; CurrNode := XMLDOMDocument.documentElement; DocNameSpace := ''; WITH XMLDOMManagement DO BEGIN //******** general_tab node IF AddElement( CurrNode,'general_tab', '',DocNameSpace,NewChild) > 0 THEN EXIT(270); CurrNode := NewChild; IF AddElement( CurrNode,'doc_num', parSalesHeader."No.", DocNameSpace,NewChild) > 0 THEN EXIT(270); IF AddElement( CurrNode,'doc_dt', FormatDate(parSalesHeader."Document Date"), DocNameSpace,NewChild) > 0 THEN EXIT(270); //******* sellto_cust node, for sell-to customer information IF AddElement( CurrNode,'sellto_cust', '',DocNameSpace,NewChild) > 0 THEN EXIT(270); CurrNode := NewChild; Customer.GET(parSalesHeader."Sell-to Customer No."); IF AddElement( CurrNode,'slt_nav_num', parSalesHeader."Sell-to Customer No.", DocNameSpace,NewChild) > 0 THEN EXIT(271); IF AddElement( CurrNode,'slt_name', parSalesHeader."Sell-to Customer Name", DocNameSpace,NewChild) > 0 THEN EXIT(270); IF AddElement( CurrNode,'slt_name2', parSalesHeader."Sell-to Customer Name 2", DocNameSpace,NewChild) > 0 THEN EXIT(270); IF AddElement( CurrNode,'slt_address', parSalesHeader."Sell-to Address", DocNameSpace,NewChild) > 0 THEN EXIT(270); IF AddElement( CurrNode,'slt_address2', parSalesHeader."Sell-to Address 2", DocNameSpace,NewChild) > 0 THEN EXIT(270); IF AddElement( CurrNode,'slt_city', parSalesHeader."Sell-to City", DocNameSpace,NewChild) > 0 THEN EXIT(270); IF AddElement( CurrNode,'slt_state', parSalesHeader."Sell-to County", DocNameSpace,NewChild) > 0 THEN EXIT(270); IF AddElement( CurrNode,'slt_zip', parSalesHeader."Sell-to Post Code", DocNameSpace,NewChild) > 0 THEN EXIT(270); IF AddElement( CurrNode,'slt_country', parSalesHeader."Sell-to Country Code", DocNameSpace,NewChild) > 0 THEN EXIT(270); //******** back to the general_tab node CurrNode := CurrNode.parentNode; IF AddElement( CurrNode,'purch_ord_num', parSalesHeader."Your Reference", DocNameSpace,NewChild) > 0 THEN EXIT(270); //******* back in root node CurrNode := CurrNode.parentNode; //********** shipping_tab node IF AddElement( CurrNode,'shipping_tab', '',DocNameSpace,NewChild) > 0 THEN EXIT(270); CurrNode := NewChild; //*********** spt_address node, for shipping address information IF AddElement( CurrNode,'spt_address', '',DocNameSpace,NewChild) > 0 THEN EXIT(270); CurrNode := NewChild; IF AddElement( CurrNode,'spt_nav_num', parSalesHeader."Ship-to Code", DocNameSpace,NewChild) > 0 THEN EXIT(270); IF AddElement( CurrNode,'spt_name', parSalesHeader."Ship-to Name", DocNameSpace,NewChild) > 0 THEN EXIT(270); IF AddElement( CurrNode,'spt_name2', parSalesHeader."Ship-to Name 2", DocNameSpace,NewChild) > 0 THEN EXIT(270); IF AddElement( CurrNode,'spt_address', parSalesHeader."Ship-to Address", DocNameSpace,NewChild) > 0 THEN EXIT(270); IF AddElement( CurrNode,'spt_address2', parSalesHeader."Ship-to Address 2", DocNameSpace,NewChild) > 0 THEN EXIT(270); IF AddElement( CurrNode,'spt_city', parSalesHeader."Ship-to City", DocNameSpace,NewChild) > 0 THEN EXIT(270); IF AddElement( CurrNode,'spt_state', parSalesHeader."Ship-to County", DocNameSpace,NewChild) > 0 THEN EXIT(270); IF AddElement( CurrNode,'spt_zip', parSalesHeader."Ship-to Post Code", DocNameSpace,NewChild) > 0 THEN EXIT(270); IF AddElement( CurrNode,'spt_country', parSalesHeader."Ship-to Country Code", DocNameSpace,NewChild) > 0 THEN EXIT(270); IF AddElement( CurrNode,'spt_cont', parSalesHeader."Ship-to Contact", DocNameSpace,NewChild) > 0 THEN EXIT(270); //********** back to the shipping_tab node CurrNode := CurrNode.parentNode; //*********** back to root node CurrNode := CurrNode.parentNode; //********* lines node IF AddElement( CurrNode,'lines', '',DocNameSpace,NewChild) > 0 THEN EXIT(270); CurrNode := NewChild; //******** find actual sales lines before creating line nodes IF SalesLine.FIND('-') THEN BEGIN REPEAT //********* line node IF AddElement( CurrNode,'line', '',DocNameSpace,NewChild) > 0 THEN EXIT(270); CurrNode := NewChild; IF AddElement( CurrNode,'line_type', FORMAT(SalesLine.Type), DocNameSpace,NewChild) > 0 THEN EXIT(271); IF AddElement( CurrNode,'item_num', SalesLine."No.", DocNameSpace,NewChild) > 0 THEN EXIT(270); IF AddElement( CurrNode,'descr', SalesLine.Description, DocNameSpace,NewChild) > 0 THEN EXIT(270); IF AddElement( CurrNode,'quantity', FormatDecimal(SalesLine.Quantity), DocNameSpace,NewChild) > 0 THEN EXIT(270); IF AddElement( CurrNode,'u_of_m', SalesLine."Unit of Measure Code", DocNameSpace,NewChild) > 0 THEN EXIT(270); IF AddElement( CurrNode,'u_price', FormatDecimal(SalesLine."Unit Price"), DocNameSpace,NewChild) > 0 THEN EXIT(270); IF AddElement( CurrNode,'amount', FormatDecimal(SalesLine.Amount), DocNameSpace,NewChild) > 0 THEN EXIT(270); //******** back to lines node CurrNode := CurrNode.parentNode; UNTIL SalesLine.NEXT = 0; //**** END; END; //*** NOTE: You must have a valid folder C:\XMLTest\ before you comment out the following line //XMLDOMDocument.save('C:\XMLTest\' + parSalesHeader."No." + 'NavisionSalesOrder.xml'); END; PROCEDURE GetXMLDoc@1105400001(VAR parXMLDOMDocument@1105400000 : Automation "{F5078F18-C551-11D3-89B9-0000F81FE221} 3.0:{F6D90F11-9C73-11D3-B32E-00C04F990BB4}:'Microsoft XML, v3.0'.DOMDocument"); BEGIN parXMLDOMDocument := XMLDOMDocument; END; PROCEDURE FormatBoolean@10(Value@1000 : Boolean) : Text[3]; BEGIN EXIT(FORMAT(Value)); END; PROCEDURE FormatInteger@11(Value@1000 : Integer) : Text[30]; BEGIN EXIT(FORMAT(Value)); END; PROCEDURE FormatDecimal@12(Value@1000 : Decimal) : Text[50]; BEGIN EXIT(FORMAT(Value, 0, '<Precision,2:><Sign><Integer><Decimals><Comma,.>')); END; PROCEDURE FormatDate@13(Value@1000 : Date) : Text[10]; BEGIN EXIT(FORMAT(Value,0,'<Month,2>-<Day,2>-<Year4>')); END; EVENT XMLDOMDocument@1105400002::ondataavailable@198(); BEGIN END; EVENT XMLDOMDocument@1105400002::onreadystatechange@-609(); BEGIN END; BEGIN END. } } Let me know if you have any questions :).

i haven’t got a simple example any more but a short 2 page overview of dom basics (germanelanguage) including some examples which can be easily transfered to a navision code. maybe you can send me a mail and i will send it to you?

Hi Daniel! Yep, I think this will help - thanks a lot! Hi Alexander! It would be great if you could send me that doc Stryk.Joerg@Apollo-Optik.com Thanks!

Alexander, I am also interested. David Singleton Denster thanks…

You’re welcome. Always nice to be able to help. [8D]

Here we go again … Well, the hints you gave helped very much - to CREATE a XML doc. Now I have the “opposite” problem [V][Duh!] I need a SIMPLE example of how to open an XML doc (actually that’s solved), to access an element (there are examples within NAVISION) and its attributes (that’s my “real” problem)! Hope you have as good advice as before [:D] Thanx in advance!

Sure no problem. I don’t have the time to create a sample for you though, so take a look at the standard Navision codeunit number 99008518 - XML Document-Transform. That’s where I got my ideas. To read the attributes you will probably need to declare an attributes type variable (part of the XMLDOM objet model) and search through it. I am not sure of there is an example of how to read attributes in there, I’d have to play around with it.

Jörg, Maybe these codelines solve your problem: … //Get Attribute ProcessStatus NodeMap := CurrNode.attributes; CurrNode2 := NodeMap.getNamedItem(‘ProcessStatus’); IF STRLEN(CurrNode2.text) > 0 THEN PStatus := CurrNode2.nodeTypedValue; //‘Processed’ … /Karl

Hmmm … I LOVE THIS FORUM [:p][Yeah!] Thanks for your replies - now I could solve my issue! Well, in “recognition of your contribution” I decided to share the result of this “struggle”: This time, my goal was to create a small function to import “Currency Exchange Rates” via XML from the “European Central Bank”. Here the source: Variables: XMLDOMDocument, Automation,'Microsoft XML, v3.0'.DOMDocument CurrNode, Automation, 'Microsoft XML, v3.0'.IXMLDOMNode CurrNode2, Automation, 'Microsoft XML, v3.0'.IXMLDOMNode XMLDOMhttp, Automation, 'Microsoft XML, v3.0'.XMLHTTP CurrNodeList, Automation, 'Microsoft XML, v3.0'.IXMLDOMNodeList CurrNodeList2, Automation, 'Microsoft XML, v3.0'.IXMLDOMNodeList XMLDOMMgmt, Codeunit, XML DOM Management Currency, Record, Currency CurrencyExchRate, Record, Currency Exchange Rate CurrencyURL, Text, 250 AttributeText, Text, 30 NewCurrencyDate, Date NewCurrencyCode, Code, 10 NewCurrencyRate, Decimal NoOfCurr, Integer Day, Integer Month, Integer Year, Integer i, Integer Window, Dialog TextConstants: Text000, Currency Exchange Rates updated. Text001, Could not import XML Currency Exchange Rates. Text002, Import Currency Exchange Rates from XML ...\Currency Date #1########\Currency Code #2########\Currency Rate #3########\@4@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Function: GetCurrencyExchRateXML() // Imports Currency Exchange Rates from EUROPEAN CENTRAL BANK via XML // Base on EUR: 1 EUR = <Rate> <Currency> CurrencyURL := 'http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml'; IF ISCLEAR(XMLDOMhttp) THEN CREATE(XMLDOMhttp); XMLDOMhttp.open('GET', CurrencyURL, TRUE); YIELD; XMLDOMhttp.send(); FOR i := 1 TO 500 DO YIELD; IF ISCLEAR(XMLDOMDocument) THEN CREATE(XMLDOMDocument); XMLDOMDocument := XMLDOMhttp.responseXML(); XMLDOMMgmt.SetNormalCase(); CurrNode := XMLDOMDocument.documentElement; // Get attribute "Time" IF XMLDOMMgmt.FindNode(CurrNode, '/gesmes:Envelope/Cube/Cube', CurrNode2) THEN BEGIN CurrNode := CurrNode2; IF XMLDOMMgmt.FindNode(CurrNode, '@time', CurrNode2) THEN BEGIN IF STRLEN(CurrNode2.text) > 0 THEN AttributeText := CurrNode2.text; EVALUATE(Day, COPYSTR(AttributeText, 9, 2)); EVALUATE(Month, COPYSTR(AttributeText, 6, 2)); EVALUATE(Year, COPYSTR(AttributeText, 1, 4)); NewCurrencyDate := DMY2DATE(Day, Month, Year); // Get atributes "Currency" and "Rate" IF XMLDOMMgmt.FindNodes(CurrNode, '/gesmes:Envelope/Cube/Cube/Cube', CurrNodeList) THEN BEGIN NoOfCurr := CurrNodeList.length; Window.OPEN(Text002); Window.UPDATE(1, NewCurrencyDate); FOR i := 1 TO NoOfCurr DO BEGIN CurrNode := CurrNodeList.item(i - 1); IF XMLDOMMgmt.FindNode(CurrNode, '@currency', CurrNode2) THEN BEGIN EVALUATE(NewCurrencyCode, CurrNode2.text); Window.UPDATE(2, NewCurrencyCode); IF XMLDOMMgmt.FindNodes(CurrNode, '@rate', CurrNodeList2) THEN BEGIN CurrNode2 := CurrNodeList2.nextNode(); EVALUATE(NewCurrencyRate, CONVERTSTR(CurrNode2.text, '.', ',')); Window.UPDATE(2, NewCurrencyRate); // Insert new Currency Exchange Rate IF Currency.GET(NewCurrencyCode) THEN BEGIN IF NOT CurrencyExchRate.RECORDLEVELLOCKING THEN CurrencyExchRate.LOCKTABLE(TRUE); CurrencyExchRate.INIT; CurrencyExchRate."Starting Date" := NewCurrencyDate; CurrencyExchRate."Currency Code" := NewCurrencyCode; CurrencyExchRate."Relational Currency Code" := ''; CurrencyExchRate."Exchange Rate Amount" := NewCurrencyRate; CurrencyExchRate."Relational Exch. Rate Amount" := 1; CurrencyExchRate."Adjustment Exch. Rate Amount" := NewCurrencyRate; CurrencyExchRate."Relational Adjmt Exch Rate Amt" := 1; CurrencyExchRate."Fix Exchange Rate Amount" := CurrencyExchRate."Fix Exchange Rate Amount"::"Relational Currency"; IF NOT CurrencyExchRate.INSERT(TRUE) THEN; END; END; END; Window.UPDATE(4, ROUND(10000 / NoOfCurr * i, 1)); CurrNode := CurrNodeList.nextNode(); END; MESSAGE(Text000); END; END; END ELSE ERROR(Text001); Finally, the function is called from Form 5 “Currencies”. Well, maybe that’s not optimized - feel free to improve [;)] - but maybe you want to use this! Kind regards,

Hey that is an awesome function. Congrats. [8D]

Well, now I’m getting hot on this … [:P][Yeah!] This - slightly different - function is to import some historic (since April 1999) Exchange Rates from the ECB: GetCurrencyExchRateHistoryXML() // Imports Currency Exchange Rates from EUROPEAN CENTRAL BANK via XML // History Data since 01.04.1999 // Base on EUR: 1 EUR = <Rate> <Currency> CurrencyURL := 'http://www.ecb.int/stats/eurofxref/eurofxref-hist.xml'; IF ISCLEAR(XMLDOMhttp) THEN CREATE(XMLDOMhttp); XMLDOMhttp.open('GET', CurrencyURL, TRUE); YIELD; XMLDOMhttp.send(); FOR i := 1 TO 500 DO YIELD; IF ISCLEAR(XMLDOMDocument) THEN CREATE(XMLDOMDocument); XMLDOMDocument := XMLDOMhttp.responseXML(); XMLDOMMgmt.SetNormalCase(); CurrNode := XMLDOMDocument.documentElement; IF XMLDOMMgmt.FindNodes(CurrNode, '/gesmes:Envelope/Cube/Cube/Cube', CurrNodeList) THEN BEGIN Window.OPEN(Text002); NoOfCurr := CurrNodeList.length; FOR i := 1 TO NoOfCurr DO BEGIN IF XMLDOMMgmt.FindNode(CurrNodeList.item(i - 1), '@currency', CurrNode2) THEN BEGIN EVALUATE(NewCurrencyCode, CurrNode2.text); Window.UPDATE(2, NewCurrencyCode); IF XMLDOMMgmt.FindNodes(CurrNodeList.item(i - 1), '@rate', CurrNodeList2) THEN BEGIN CurrNode3 := CurrNodeList2.nextNode(); EVALUATE(NewCurrencyRate, CONVERTSTR(CurrNode3.text, '.', ',')); Window.UPDATE(3, NewCurrencyRate); IF XMLDOMMgmt.FindNode(CurrNodeList.item(i - 1).parentNode, '@time', CurrNode4) THEN BEGIN IF STRLEN(CurrNode4.text) > 0 THEN AttributeText := CurrNode4.text; EVALUATE(Day, COPYSTR(AttributeText, 9, 2)); EVALUATE(Month, COPYSTR(AttributeText, 6, 2)); EVALUATE(Year, COPYSTR(AttributeText, 1, 4)); NewCurrencyDate := DMY2DATE(Day, Month, Year); Window.UPDATE(1, NewCurrencyDate); IF Currency.GET(NewCurrencyCode) THEN BEGIN IF NOT CurrencyExchRate.RECORDLEVELLOCKING THEN CurrencyExchRate.LOCKTABLE(TRUE); CurrencyExchRate.INIT; CurrencyExchRate."Starting Date" := NewCurrencyDate; CurrencyExchRate."Currency Code" := NewCurrencyCode; CurrencyExchRate."Relational Currency Code" := ''; CurrencyExchRate."Exchange Rate Amount" := NewCurrencyRate; CurrencyExchRate."Relational Exch. Rate Amount" := 1; CurrencyExchRate."Adjustment Exch. Rate Amount" := NewCurrencyRate; CurrencyExchRate."Relational Adjmt Exch Rate Amt" := 1; CurrencyExchRate."Fix Exchange Rate Amount" := CurrencyExchRate."Fix Exchange Rate Amount"::"Relational Currency"; IF NOT CurrencyExchRate.INSERT(TRUE) THEN; END; IF (NewCurrencyDate <> OldCurrencyDate) THEN BEGIN OldCurrencyDate := NewCurrencyDate; COMMIT; END; END; END; Window.UPDATE(4, ROUND(10000 / NoOfCurr * i, 1)); END; SLEEP(100); END; MESSAGE(Text000); END ELSE ERROR(Text001); So, what’s up next !?

can you send mentioned example also to me ?

thanks

The code is all there, just copy it

Just one small pointer, the Currency Exchange Rates from EUROPEAN CENTRAL BANK via XML are text nodes, so the decimal qualifier is the period in text “.” some countries like Sweden use a comma “,” so it will cause a problem in this instance.

David.

Just some background the problem with the comma arose with a multi langauge company databases where the ECB Exchange Rates were used to triangulate the currencies all went well until sweden, the EUR, GBP, USD, SEK were all local currencies in different companies, and we used the ECB rates as a base to calculate all other currencies.

David