How to add DOCTYPE to xml file using xmlport in Version 2009

April_Chen
Member Posts: 2
Hello all,
One of my client require a xml file with the following declaration:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE cXML SYSTEM "http://xml.cXML.org/schemas/cXML/1.2.009/InvoiceDetail.dtd">
The first declaration is generated automatically when I run the xmlport I created but not the second one. Does anyone know how? Your help would be greatly appreciated.
Thank you very much.
April
One of my client require a xml file with the following declaration:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE cXML SYSTEM "http://xml.cXML.org/schemas/cXML/1.2.009/InvoiceDetail.dtd">
The first declaration is generated automatically when I run the xmlport I created but not the second one. Does anyone know how? Your help would be greatly appreciated.
Thank you very much.
April
0
Comments
-
I suggest to use the following method.
Run you XMLPORT and export it to a stream. Load that to a MSXMLDOM and call a function that will add any additional Namespace or DOCTYPE
Here is implementation of this. In my example
XML is a blob field and I'm creating a stream that I'm loading into a MSXMLDOMCALCFIELDS(XML); XML.CREATEINSTREAM(instr); MSDOM.async := FALSE; MSDOM.load(instr); ChangeXMLFile(MSDOM); MSDOM.save('C:\xmlfilewithDocType.xml');
ChangeXMLFile is the function that will add namespace or DOCTYPE or any other structure that is not be done by xmlport
//MSDOM2 is passed by reference.
//XMLNode is of type 'Microsoft XML, v6.0'.IXMLDOMNode
//Separator is of type text
//Ch is of type charChangeXMLFile(VAR MSDOM2 : Automation "'Microsoft XML, v6.0'.DOMDocument") IF ISCLEAR(MSDOM) THEN BEGIN CREATE(MSDOM); MSDOM.async := FALSE; END; Ch10 := 10; CH13 := 13; CH9 := 9; Separator := FORMAT(CH13) + FORMAT(Ch10) + FORMAT(CH9); MSDOM.loadXML('<?xml version="1.0" encoding="utf-8"?>' + Separator + '<!DOCTYPE cXML SYSTEM "http://xml.cXML.org/schemas/cXML/1.2.009/InvoiceDetail.dtd">' + Separator); XMLNode := MSDOM.documentElement.firstChild; XMLNode2 := MSDOM2.documentElement.firstChild; XMLNode.appendChild(XMLNode2); MSDOM2 := MSDOM;
0 -
I had the same problem than April but could make ara3n's solution work (maybe because i was not using nav2009 ?).
So i came up with an other workaround.fnk_doIt(par_re_Item : Record Item) // Create file fi_XMLFile.CREATE(te_XMLFilePath + te_XMLFileName); // Create OutStream re_TempBlob.Blob.CREATEOUTSTREAM(os_OutStream); // Run XML-Port and write in OutStream xp_Item.SETTABLEVIEW(par_re_Item); xp_Item.SETDESTINATION(os_OutStream); xp_Item.EXPORT; // Load it into a Blob re_TempBlob.Blob.CREATEINSTREAM(is_InStream); // TODO: look for the first representation of a linebreak(or something else), instead of reading a hardcoded number of chars // Read and write line 1 IN_NumberOfChars := 56; // Number of chars from line 1 FOR i:=1 TO IN_NumberOfChars*2 DO BEGIN // *2 because XML-Port generates 4 byte blocks and a Char has 2 bytes is_InStream.READ(CH_char); fi_XMLFile.WRITE(CH_char); END; // Make a line break // Xml-port makes a line break like this: 0D 00 0A 00 (Hexadezimal) // The write function writes minimal 4 bytes, so you can't write those chars(0D,0A,00) one by one // 655373 stands for 00 0A 00 0D; it starts writing with the last byte fi_XMLFile.WRITE(655373); // Make your extra line TE_InsertText := '<!DOCTYPE Transaction PUBLIC BlaBlaBla'; FOR i:=1 TO STRLEN(TE_InsertText) DO BEGIN fi_XMLFile.WRITE(FORMAT(TE_InsertText[i])); END; // Read and write rest WHILE NOT is_InStream.EOS DO BEGIN is_InStream.READ(CH_char); fi_XMLFile.WRITE(CH_char); END; // Close file fi_XMLFile.CLOSE;
Variables:Name DataType Subtype Length re_TempBlob Record TempBlob xp_Item XMLport Item te_InsertText Text 1024 te_XMLFileName Text 30 te_XMLFilePath Text 900 ch_char Char i Integer in_NumberOfChars Integer fi_XMLFile File is_InStream InStream os_OutStream OutStream
Not really a "nice" way to do it, but it worked for me.
greetings0 -
Hi All,
( Using NAV2009 SP1 build 6.00.29626 Classic Client SQL Database)
Trying to work with ara3n's solution, but coming up with an error that the Automation has not been instantiated, that I have to create it or assign it. This occurs in the ChangeXMLFile function on the XMLNode assignment.
If I comment out the ChangeXMLFile function call. It creates the XML file as expected just without the <!DOCTYPE...> node.
I tried it with the "XMLNode := MSDOM.documentElement.firstChild;" first, and that created an error, so then I tried "XMLNode := MSDOM.documentElement();" which moved the error down to the "XMLNode.appendChild(XMLNode2);" line.
What is missing?
Thanks for your help.VAR XMLDOMDocument: Automation :'Microsoft XML, v6.0'.DOMDocument; MSDOM: Automation :'Microsoft XML, v6.0'.DOMDocument; OutboundInvoice: Record 112; TempBlob: Record 99008535; TempOutstream: OutStream; TempInstream: InStream; OutboundInvoiceXML: XMLport 50001; XMLNode: Automation :'Microsoft XML, v6.0'.IXMLDOMNode; XMLNode2: Automation :'Microsoft XML, v6.0'.IXMLDOMNode; Separator: Text[30]; Ch10: Char; Ch13: Char; Ch9: Char; PROCEDURE AddDoctype(); IF ISCLEAR(XMLDOMDocument) THEN BEGIN CREATE(XMLDOMDocument); XMLDOMDocument.async := FALSE; END; OutboundInvoice.SETCURRENTKEY("Sell-to Customer No.","External Document No."); OutboundInvoice.SETRANGE(OutboundInvoice."No.",'PI103815'); IF NOT OutboundInvoice.FIND('-') THEN ERROR('No Invoice Found'); TempBlob.Blob.CREATEOUTSTREAM(TempOutstream); OutboundInvoice.SETRANGE(OutboundInvoice."No.",'PI103815'); OutboundInvoiceXML.SETTABLEVIEW(OutboundInvoice); OutboundInvoiceXML.SETDESTINATION(TempOutstream); IF OutboundInvoiceXML.EXPORT THEN BEGIN TempBlob.Blob.CREATEINSTREAM(TempInstream); XMLDOMDocument.load(TempInstream); ChangeXMLFile(XMLDOMDocument); XMLDOMDocument.save('C:\xmlfilewithDocType.xml'); MESSAGE('Xml Export completed! C:\xmlfilewithDocType.xml Created'); END ELSE ERROR('Error exporting'); PROCEDURE ChangeXMLFile(VAR MSDOM2: Automation:'Microsoft XML, v6.0'.DOMDocument"); IF ISCLEAR(MSDOM) THEN BEGIN CREATE(MSDOM); MSDOM.async := FALSE; END; Ch10 := 10; Ch13 := 13; Ch9 := 9; Separator := FORMAT(Ch13) + FORMAT(Ch10) + FORMAT(Ch9); MSDOM.loadXML('<?xml version="1.0" encoding="utf-8" standalone="no" ?>' + Separator + '<!DOCTYPE cXML SYSTEM "http://xml.cXML.org/schemas/cXML/1.2.009/InvoiceDetail.dtd">' + Separator); //XMLNode := MSDOM.documentElement.firstChild; //XMLNode2 := MSDOM2.documentElement.firstChild; XMLNode := MSDOM.documentElement(); XMLNode2 :=MSDOM2.documentElement(); XMLNode.appendChild(XMLNode2); MSDOM2 := MSDOM;
0 -
Try and add before
MSDOM..documentElement.firstChild;
MSDOM.SAVE('c:\test1.xml');
MSDOM2.SAVE('c:\test2.xml');
And make sure they have valid xml files.0 -
MSDOM2 is valid, MSDOM is not.
I assume that this line is not working?MSDOM.loadXML('<?xml version="1.0" encoding="utf-8" standalone="no" ?>' + Separator + '<!DOCTYPE cXML SYSTEM "http://xml.cXML.org/schemas/cXML/1.2.009/InvoiceDetail.dtd">' + Separator);
if I change this to a text instead, and stream it into a file, it creates an XML file, but I still can't open it to get the nodes and move them into the XML port generated one.insertText := '<?xml version="1.0" encoding="utf-8" standalone="no" ?>' + Separator + '<!DOCTYPE cXML SYSTEM "http://xml.cXML.org/schemas/cXML/1.2.009/InvoiceDetail.dtd">' + Separator; XMLFile.CREATE('C:\Temp.XML'); XMLFile.CREATEOUTSTREAM(writeStream); writeStream.WRITETEXT(insertText); XMLFile.CLOSE;
I am sure I am missing something small.0 -
When created the second MSDOM, you need to add a root element.
MSDOM.loadXML('<?xml version="1.0" encoding="utf-8" standalone="no" ?>' + Separator + '<!DOCTYPE cXML SYSTEM "http://xml.cXML.org/schemas/cXML/1.2.009/InvoiceDetail.dtd">' + Separator + '<ROOT></ROOT>'); <ROOT></ROOT>
0 -
The problem is with the MSDOM.loadXML command.
Apparently when using an external DTD, it can't validate when parsing because it isn't a whole definition.
Adding "MSDOM.validateOnParse(FALSE);" before the "MSDOM.loadXML(..." will allow it to actually load.
So I am able to get the file to save from the MSDOM with the "<!DOCTYPE" inserted. Still getting an error on the XMLNode though.
Current Code://ChangeXMLFile(VAR MSDOM2 : Automation "'Microsoft XML, v6.0'.DOMDocument") IF ISCLEAR(MSDOM) THEN BEGIN CREATE(MSDOM); MSDOM.async := FALSE; END; Ch10 := 10; Ch13 := 13; Ch9 := 9; Separator := FORMAT(Ch13) + FORMAT(Ch10) + FORMAT(Ch9); MSDOM.validateOnParse(FALSE); Loaded := MSDOM.loadXML('<?xml version="1.0" encoding="utf-8" standalone="no" ?>' + Separator + '<!DOCTYPE cXML SYSTEM "http://xml.cXML.org/schemas/cXML/1.2.009/InvoiceDetail.dtd">' + Separator + '<ROOT></ROOT>'); //MSDOM.save('C:\Test1.xml'); //MSDOM2.save('C:\Test2.xml'); XMLNode := MSDOM.documentElement.firstChild; XMLNode2 := MSDOM2.documentElement.firstChild; //XMLNode := MSDOM.documentElement(); //XMLNode2 :=MSDOM2.documentElement(); XMLNode.appendChild(XMLNode2); MSDOM2 := MSDOM;
Once again, something small being overlooked...0 -
Thanks for your help ara3n. Got it figured out.
Basically have to copy all the child nodes into the DOM that is created with the corrected header.
You cannot move/append the <!DOCTYPE> node (it's read only :? ) which is why it was failing.
Here is the working code:VAR XMLDOMDocument : Automation :'Microsoft XML, v6.0'.DOMDocument; OutboundInvoice : Record 112; TempBlob : Record 99008535; TempOutstream : OutStream; TempInstream : InStream; OutboundInvoiceXML : XMLport 50001; MSDOM : Automation :'Microsoft XML, v6.0'.DOMDocument; XMLNode : Automation :'Microsoft XML, v6.0'.IXMLDOMNode; XMLNode2 : Automation :'Microsoft XML, v6.0'.IXMLDOMNode; Separator : Text[30]; Ch10 : Char; Ch13 : Char; Ch9 : Char; PROCEDURE AddDocType(); IF ISCLEAR(XMLDOMDocument) THEN BEGIN CREATE(XMLDOMDocument); XMLDOMDocument.async := FALSE; END; OutboundInvoice.SETCURRENTKEY("Sell-to Customer No.","External Document No."); OutboundInvoice.SETRANGE(OutboundInvoice."No.",'PI103815'); IF NOT OutboundInvoice.FIND('-') THEN ERROR('No Invoice Found'); TempBlob.Blob.CREATEOUTSTREAM(TempOutstream); OutboundInvoice.SETRANGE(OutboundInvoice."No.",'PI103815'); OutboundInvoiceXML.SETTABLEVIEW(OutboundInvoice); OutboundInvoiceXML.SETDESTINATION(TempOutstream); IF OutboundInvoiceXML.EXPORT THEN BEGIN TempBlob.Blob.CREATEINSTREAM(TempInstream); XMLDOMDocument.load(TempInstream); ChangeXMLFile(XMLDOMDocument); XMLDOMDocument.save('C:\xmlfilewithDocType.xml'); MESSAGE('Xml Export completed! C:\xmlfilewithDocType.xml Created'); END ELSE ERROR('Error exporting'); PROCEDURE ChangeXMLFile(VAR MSDOM2 : Automation :'Microsoft XML, v6.0'.DOMDocument); IF ISCLEAR(MSDOM) THEN BEGIN CREATE(MSDOM); MSDOM.async := FALSE; END; Ch10 := 10; Ch13 := 13; Ch9 := 9; Separator := FORMAT(Ch13) + FORMAT(Ch10) + FORMAT(Ch9); //Cancel validation because loadXML will try to send to parser and it will fail because of external DTD MSDOM.validateOnParse(FALSE); //Insert the header you want/need to use. It seems you must have root or something like it. //You can add a boolean in front of this command to verify that it loaded. MSDOM.loadXML('<!DOCTYPE cXML SYSTEM "http://xml.cXML.org/schemas/cXML/1.2.009/InvoiceDetail.dtd">' + Separator + '<root></root>'); //If you need to replace the <root> with your first element use this. //My first element I wanted to keep was called <cXML>. XMLNode := MSDOM.selectSingleNode('root'); XMLNode2 := MSDOM2.selectSingleNode('cXML'); MSDOM.replaceChild(XMLNode2,XMLNode); //If you wanted to keep the <root> use this instead. //XMLNode2 := MSDOM2.selectSingleNode('cXML'); //XMLNode := XMLNode2.cloneNode(TRUE); //MSDOM.documentElement.appendChild(XMLNode); //These were made for testing... //MSDOM.save('C:\Test1.xml'); //MSDOM2.save('C:\Test2.xml'); MSDOM2 := MSDOM;
April, hopefully this works for you too and you can mark this as SOLVED.
Might not be a bad thing to add into the "How-to" Section.
Now to move the file from the services tier to the client... plenty of how-to's on that though.0 -
Hi Joe
As you found out before , You end up with two files and various problems with the xml nodes within the function.
Where you able to resolve this or did you have to write something different?0 -
Hi,
I didn't notice your notification until today. I checked to see if you had posted after your call.
I did use this code and was successful making a XML file and changing the header. I only ended up with one useful file though. I run through the ChangeXMLFile procedure before saving.
What are you doing differently?0 -
It is all working fine actually and client has gone live.0
Categories
- All Categories
- 73 General
- 73 Announcements
- 66.6K Microsoft Dynamics NAV
- 18.7K NAV Three Tier
- 38.4K NAV/Navision Classic Client
- 3.6K Navision Attain
- 2.4K Navision Financials
- 116 Navision DOS
- 851 Navision e-Commerce
- 1K NAV Tips & Tricks
- 772 NAV Dutch speaking only
- 617 NAV Courses, Exams & Certification
- 2K Microsoft Dynamics-Other
- 1.5K Dynamics AX
- 320 Dynamics CRM
- 111 Dynamics GP
- 10 Dynamics SL
- 1.5K Other
- 990 SQL General
- 383 SQL Performance
- 34 SQL Tips & Tricks
- 35 Design Patterns (General & Best Practices)
- 1 Architectural Patterns
- 10 Design Patterns
- 5 Implementation Patterns
- 53 3rd Party Products, Services & Events
- 1.6K General
- 1.1K General Chat
- 1.6K Website
- 83 Testing
- 1.2K Download section
- 23 How Tos section
- 252 Feedback
- 12 NAV TechDays 2013 Sessions
- 13 NAV TechDays 2012 Sessions