Codeunit 1203 Import XML File to Data Exch., source in 29
Source29
src/Layers/W1/BaseApp/System/DataExchange/ImportXMLFiletoDataExch.Codeunit.al244 lines, Copyright (c) Microsoft Corporation. MIT
namespace System.IO;
using System;
using System.Utilities;
using System.Xml;
codeunit 1203 "Import XML File to Data Exch."
{
Permissions = TableData "Data Exch. Field" = rimd;
TableNo = "Data Exch.";
trigger OnRun()
begin
StartTime := CurrentDateTime;
UpdateProgressWindow(0);
ParseParentChildDocument(Rec);
if GuiAllowed and WindowOpen then
ProgressWindow.Close();
end;
var
#pragma warning disable AA0470
ProgressMsg: Label 'Preparing line number #1#######';
#pragma warning restore AA0470
ProgressWindow: Dialog;
WindowOpen: Boolean;
StartTime: DateTime;
local procedure ParseParentChildDocument(DataExch: Record "Data Exch.")
var
DataExchDef: Record "Data Exch. Def";
DataExchLineDef: Record "Data Exch. Line Def";
XMLDOMManagement: Codeunit "XML DOM Management";
XmlDocument: DotNet XmlDocument;
XmlNodeList: DotNet XmlNodeList;
XmlNamespaceManager: DotNet XmlNamespaceManager;
XmlStream: InStream;
CurrentLineNo: Integer;
NodeID: Text[250];
I: Integer;
NodeCount: Integer;
begin
DataExchDef.Get(DataExch."Data Exch. Def Code");
DataExchLineDef.SetRange("Data Exch. Def Code", DataExchDef.Code);
DataExchLineDef.SetRange("Parent Code", '');
if not DataExchLineDef.FindSet() then
exit;
DataExch."File Content".CreateInStream(XmlStream);
XMLDOMManagement.LoadXMLDocumentFromInStream(XmlStream, XmlDocument);
DataExchLineDef.ValidateNamespace(XmlDocument.DocumentElement);
XMLDOMManagement.AddNamespaces(XmlNamespaceManager, XmlDocument);
repeat
XMLDOMManagement.FindNodesWithNamespaceManager(
XmlDocument, EscapeMissingNamespacePrefix(DataExchLineDef."Data Line Tag"), XmlNamespaceManager, XmlNodeList);
CurrentLineNo := 1;
NodeCount := XmlNodeList.Count();
for I := 1 to NodeCount do begin
NodeID := IncreaseNodeID('', CurrentLineNo);
ParseParentChildLine(
XmlNodeList.ItemOf(I - 1), NodeID, '', CurrentLineNo, DataExchLineDef, DataExch."Entry No.", XmlNamespaceManager);
CurrentLineNo += 1;
end;
until DataExchLineDef.Next() = 0;
end;
local procedure ParseParentChildLine(CurrentXmlNode: DotNet XmlNode; NodeID: Text[250]; ParentNodeID: Text[250]; CurrentLineNo: Integer; CurrentDataExchLineDef: Record "Data Exch. Line Def"; EntryNo: Integer; XmlNamespaceManager: DotNet XmlNamespaceManager)
var
DataExchColumnDef: Record "Data Exch. Column Def";
DataExchLineDef: Record "Data Exch. Line Def";
DataExchField: Record "Data Exch. Field";
XMLDOMManagement: Codeunit "XML DOM Management";
XmlNodeList: DotNet XmlNodeList;
CurrentIndex: Integer;
CurrentNodeID: Text[250];
InnerText: Text;
LastLineNo: Integer;
I: Integer;
NodeCount: Integer;
begin
DataExchField.InsertRecXMLFieldDefinition(EntryNo, CurrentLineNo, NodeID, ParentNodeID, '', CurrentDataExchLineDef.Code);
// Insert Attributes and values
DataExchColumnDef.SetRange("Data Exch. Def Code", CurrentDataExchLineDef."Data Exch. Def Code");
DataExchColumnDef.SetRange("Data Exch. Line Def Code", CurrentDataExchLineDef.Code);
DataExchColumnDef.SetFilter(Path, '<>%1', '');
CurrentIndex := 1;
if DataExchColumnDef.FindSet() then
repeat
XMLDOMManagement.FindNodesWithNamespaceManager(
CurrentXmlNode,
GetRelativePath(DataExchColumnDef.Path, CurrentDataExchLineDef."Data Line Tag"),
XmlNamespaceManager,
XmlNodeList);
NodeCount := XmlNodeList.Count();
for I := 1 to NodeCount do begin
CurrentNodeID := IncreaseNodeID(NodeID, CurrentIndex);
CurrentIndex += 1;
InnerText := XmlNodeList.ItemOf(I - 1).InnerText;
OnParseParentChildLineOnBeforeInsertColumn(InnerText, XmlNodeList.ItemOf(I - 1).InnerXml, XmlNodeList.ItemOf(I - 1).OuterXML, DataExchColumnDef);
InsertColumn(
DataExchColumnDef."Column No.", CurrentLineNo, CurrentNodeID, ParentNodeID, XmlNodeList.ItemOf(I - 1).Name,
InnerText, CurrentDataExchLineDef, EntryNo);
end;
until DataExchColumnDef.Next() = 0;
// insert Constant values
DataExchColumnDef.SetFilter(Path, '%1', '');
DataExchColumnDef.SetFilter(Constant, '<>%1', '');
if DataExchColumnDef.FindSet() then
repeat
CurrentNodeID := IncreaseNodeID(NodeID, CurrentIndex);
CurrentIndex += 1;
DataExchField.InsertRecXMLFieldWithParentNodeID(EntryNo, CurrentLineNo, DataExchColumnDef."Column No.",
CurrentNodeID, ParentNodeID, DataExchColumnDef.Constant, CurrentDataExchLineDef.Code);
until DataExchColumnDef.Next() = 0;
// Insert Children
DataExchLineDef.SetRange("Data Exch. Def Code", CurrentDataExchLineDef."Data Exch. Def Code");
DataExchLineDef.SetRange("Parent Code", CurrentDataExchLineDef.Code);
if DataExchLineDef.FindSet() then
repeat
XMLDOMManagement.FindNodesWithNamespaceManager(
CurrentXmlNode,
GetRelativePath(DataExchLineDef."Data Line Tag", CurrentDataExchLineDef."Data Line Tag"),
XmlNamespaceManager,
XmlNodeList);
DataExchField.SetRange("Data Exch. No.", EntryNo);
DataExchField.SetRange("Data Exch. Line Def Code", DataExchLineDef.Code);
LastLineNo := 1;
if DataExchField.FindLast() then
LastLineNo := DataExchField."Line No." + 1;
NodeCount := XmlNodeList.Count();
for I := 1 to NodeCount do begin
CurrentNodeID := IncreaseNodeID(NodeID, CurrentIndex);
ParseParentChildLine(
XmlNodeList.ItemOf(I - 1), CurrentNodeID, NodeID, LastLineNo, DataExchLineDef, EntryNo, XmlNamespaceManager);
CurrentIndex += 1;
LastLineNo += 1;
end;
until DataExchLineDef.Next() = 0;
end;
local procedure InsertColumn(ColumnNo: Integer; LineNo: Integer; NodeId: Text[250]; ParentNodeId: Text[250]; Name: Text; Value: Text; var DataExchLineDef: Record "Data Exch. Line Def"; EntryNo: Integer)
var
DataExchColumnDef: Record "Data Exch. Column Def";
DataExchField: Record "Data Exch. Field";
begin
// Note: The Data Exch. variable is passed by reference only to improve performance.
if DataExchColumnDef.Get(DataExchLineDef."Data Exch. Def Code", DataExchLineDef.Code, ColumnNo) then begin
UpdateProgressWindow(LineNo);
if DataExchColumnDef."Use Node Name as Value" then
DataExchField.InsertRecXMLFieldWithParentNodeID(EntryNo, LineNo, DataExchColumnDef."Column No.", NodeId, ParentNodeId, Name,
DataExchLineDef.Code)
else
DataExchField.InsertRecXMLFieldWithParentNodeID(EntryNo, LineNo, DataExchColumnDef."Column No.", NodeId, ParentNodeId, Value,
DataExchLineDef.Code);
end;
end;
local procedure GetRelativePath(ChildPath: Text[250]; ParentPath: Text[250]): Text
var
XMLDOMManagement: Codeunit "XML DOM Management";
begin
exit(EscapeMissingNamespacePrefix(XMLDOMManagement.GetRelativePath(ChildPath, ParentPath)));
end;
local procedure IncreaseNodeID(NodeID: Text[250]; Seed: Integer): Text[250]
begin
exit(NodeID + Format(Seed, 0, '<Integer,4><Filler Char,0>'))
end;
procedure EscapeMissingNamespacePrefix(XPath: Text): Text
var
Regex: Codeunit Regex;
PositionOfFirstSlash: Integer;
FirstXPathElement: Text;
RestOfXPath: Text;
RegexPattern: Text;
begin
// we will let the user define XPaths without the required namespace prefix
// however, if he does that, we will only consider the XPath element as a local name
// for example, we will turn XPath /Invoice/cac:InvoiceLine into /*[local-name() = 'Invoice']/cac:InvoiceLine
PositionOfFirstSlash := StrPos(XPath, '/');
case PositionOfFirstSlash of
1:
exit('/' + EscapeMissingNamespacePrefix(CopyStr(XPath, 2)));
0:
begin
RegexPattern := '^[a-zA-Z0-9]*$';
OnBeforeAssignRegexPattern(RegexPattern);
if (XPath = '') or (not Regex.IsMatch(XPath, RegexPattern)) then
exit(XPath);
exit(StrSubstNo('*[local-name() = ''%1'']', XPath));
end;
else begin
FirstXPathElement := DelStr(XPath, PositionOfFirstSlash);
RestOfXPath := CopyStr(XPath, PositionOfFirstSlash);
exit(EscapeMissingNamespacePrefix(FirstXPathElement) + EscapeMissingNamespacePrefix(RestOfXPath));
end;
end;
end;
local procedure UpdateProgressWindow(LineNo: Integer)
var
PopupDelay: Integer;
begin
if not GuiAllowed then
exit;
PopupDelay := 1000;
if CurrentDateTime - StartTime < PopupDelay then
exit;
StartTime := CurrentDateTime; // only update every PopupDelay ms
if not WindowOpen then begin
ProgressWindow.Open(ProgressMsg);
WindowOpen := true;
end;
ProgressWindow.Update(1, LineNo);
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeAssignRegexPattern(var RegexPattern: Text)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnParseParentChildLineOnBeforeInsertColumn(var InnerText: Text; InnerXML: Text; OuterXML: Text; DataExchColumnDef: Record "Data Exch. Column Def")
begin
end;
}