Codeunit 1283 Export Generic XML, source in 29

Source29

src/Layers/W1/BaseApp/ExportGenericXML.Codeunit.al380 lines, Copyright (c) Microsoft Corporation. MIT

// ------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// ------------------------------------------------------------------------------------------------
namespace System.IO;

codeunit 1283 "Export Generic XML"
{
    Permissions = TableData "Data Exch. Field" = rimd;
    TableNo = "Data Exch.";

    trigger OnRun()
    var
        DataExch: Record "Data Exch.";
        DataExchLineDef: Record "Data Exch. Line Def";
        DataExchMapping: Record "Data Exch. Mapping";
        DataExchTableFilter: Record "Data Exch. Table Filter";
        DefaultTableID: Integer;
        xmlDoc: XmlDocument;
        OutStr: OutStream;
        IsHandled: Boolean;
    begin
        DataExchMapping.SetRange("Data Exch. Def Code", Rec."Data Exch. Def Code");
        DataExchMapping.SetRange("Data Exch. Line Def Code", Rec."Data Exch. Line Def Code");
        if DataExchMapping.FindFirst() then
            DefaultTableID := DataExchMapping."Table ID";

        DataExchLineDef.SetRange("Data Exch. Def Code", Rec."Data Exch. Def Code");
        DataExchLineDef.SetFilter(Code, '<>%1', Rec."Data Exch. Line Def Code");
        if DataExchLineDef.FindSet() then
            if DataExchLineDef."Data Line Tag" <> '' then
                Error(RootHeaderElementNotFoundErr)
            else
                repeat
                    DataExchMapping.SetRange("Data Exch. Def Code", DataExchLineDef."Data Exch. Def Code");
                    DataExchMapping.SetRange("Data Exch. Line Def Code", DataExchLineDef.Code);
                    DataExchMapping.FindFirst();

                    DataExch.Init();
                    DataExch."Data Exch. Def Code" := DataExchLineDef."Data Exch. Def Code";
                    DataExch."Data Exch. Line Def Code" := DataExchLineDef.Code;
                    if DataExchTableFilter.Get(Rec."Entry No.", DataExchMapping."Table ID") then begin
                        DataExchTableFilter.CalcFields("Table Filters");
                        DataExch."Table Filters" := DataExchTableFilter."Table Filters";
                    end else
                        if (DefaultTableID <> 0) and (DataExchMapping."Table ID" = DefaultTableID) then
                            DataExch."Table Filters" := Rec."Table Filters";
                    DataExch.Insert(true);

                    OnBeforeProcessDataExc(DataExch, IsHandled);
                    if not IsHandled then begin
                        if DataExchMapping."Pre-Mapping Codeunit" > 0 then
                            Codeunit.Run(DataExchMapping."Pre-Mapping Codeunit", DataExch);

                        Codeunit.Run(DataExchMapping."Mapping Codeunit", DataExch);

                        if DataExchMapping."Post-Mapping Codeunit" > 0 then
                            Codeunit.Run(DataExchMapping."Post-Mapping Codeunit", DataExch);

                        if DataExchLineDef."Line Type" = DataExchLineDef."Line Type"::Detail then
                            ProcessDetails(DataExch, xmlDoc)
                        else
                            ProcessHeader(DataExch, xmlDoc);
                    end;
                    DataExch.Delete(true);
                until DataExchLineDef.Next() = 0;

        ProcessDetails(Rec, xmlDoc);

        Rec."File Content".CreateOutStream(OutStr);
        xmlDoc.WriteTo(OutStr);
    end;

    local procedure ProcessDetails(var DataExch: Record "Data Exch."; var xmlDoc: XmlDocument);
    var
        DataExchDef: Record "Data Exch. Def";
        DataExchField: Record "Data Exch. Field";
        DataExchColumnDef: Record "Data Exch. Column Def";
        DataExchLineDef: Record "Data Exch. Line Def";
        xmlDocPerLine: XmlDocument;
        xmlElem, xmlElemRoot, xmlElemSubRoot : XmlElement;
        xmlNode: XmlNode;
        i, LineCount : Integer;
        isSubRoot, IsHandled : Boolean;
        xmlNodeFound: Boolean;
    begin
        IsHandled := false;
        OnBeforeExportDetails(DataExch, xmlDoc, IsHandled);
        if IsHandled then
            exit;

        DataExchDef.Get(DataExch."Data Exch. Def Code");
        DataExchLineDef.Get(DataExch."Data Exch. Def Code", DataExch."Data Exch. Line Def Code");
        if DataExchLineDef."Data Line Tag" <> '' then begin
            if StrPos(DataExchLineDef."Data Line Tag", ':') = 0 then
                xmlNodeFound := xmlDoc.SelectSingleNode(DataExchLineDef."Data Line Tag".Replace('/', '/' + DefNameSpacePrefixLbl + ':').Replace('[', '[' + DefNameSpacePrefixLbl + ':'), xmlNamespaceManager, xmlNode)
            else
                xmlNodeFound := xmlDoc.SelectSingleNode(DataExchLineDef."Data Line Tag", xmlNamespaceManager, xmlNode);
            // currently xPath with only one [node = value] criteria is supported
            if not xmlNodeFound then
                exit;
            xmlElemRoot := xmlNode.AsXmlElement();
        end else begin
            xmlDoc := xmlDocument.Create();
            xmlElemRoot := xmlElement.Create('root');
            xmlDoc.Add(xmlElemRoot);
        end;

        DataExchField.SetRange("Data Exch. No.", DataExch."Entry No.");
        if DataExchField.FindLast() then
            LineCount := DataExchField."Line No."
        else
            LineCount := 1;

        for i := 1 to LineCount do begin
            DataExchColumnDef.SetRange("Data Exch. Def Code", DataExch."Data Exch. Def Code");
            DataExchColumnDef.SetRange("Data Exch. Line Def Code", DataExch."Data Exch. Line Def Code");
            if DataExchColumnDef.FindSet() then begin
                xmlDocPerLine := xmlDocument.Create();
                xmlElem := xmlElement.Create('root');
                xmlElemSubRoot := XmlElement.Create(uniqueNodeLbl);
                xmlDocPerLine.Add(xmlElem);
                isSubRoot := true;
                repeat
                    ProcessDataExchColumnDef(DataExchColumnDef, GetDataExchFieldValue(DataExch."Entry No.", i, DataExchColumnDef."Column No."), xmlElem, xmlElemSubRoot, isSubRoot);
                until DataExchColumnDef.Next() = 0;

                if xmlElemSubRoot.Name <> uniqueNodeLbl then
                    xmlElemRoot.Add(xmlElemSubRoot);
                Clear(xmlDocPerLine);
            end;
        end;
    end;

    local procedure ProcessHeader(var DataExch: Record "Data Exch."; var xmlDoc: XmlDocument)
    var
        DataExchDef: Record "Data Exch. Def";
        DataExchColumnDef: Record "Data Exch. Column Def";
        DataExchLineDef: Record "Data Exch. Line Def";
        xmlElem, xmlElemRoot, xmlElemSubRoot : XmlElement;
        xmlSubDoc: XmlDocument;
        xmlNode: XmlNode;
        xmlDec: XmlDeclaration;
        xmlPath, nName, nVal : Text;
        isSubRoot, IsHandled : Boolean;
        xmlNodeFound: Boolean;
    begin
        IsHandled := false;
        OnBeforeExportHeader(DataExch, xmlDoc, IsHandled);
        if IsHandled then
            exit;

        DataExchDef.Get(DataExch."Data Exch. Def Code");
        DataExchColumnDef.SetRange("Data Exch. Def Code", DataExch."Data Exch. Def Code");
        DataExchColumnDef.SetRange("Data Exch. Line Def Code", DataExch."Data Exch. Line Def Code");
        if DataExchColumnDef.FindSet() then
            if DataExchLineDef.Get(DataExch."Data Exch. Def Code", DataExch."Data Exch. Line Def Code") and (DataExchLineDef."Data Line Tag" <> '') then begin
                if StrPos(DataExchLineDef."Data Line Tag", ':') = 0 then
                    xmlNodeFound := xmlDoc.SelectSingleNode(DataExchLineDef."Data Line Tag".Replace('/', '/' + DefNameSpacePrefixLbl + ':'), xmlNamespaceManager, xmlNode)
                else
                    xmlNodeFound := xmlDoc.SelectSingleNode(DataExchLineDef."Data Line Tag", xmlNamespaceManager, xmlNode);

                if not xmlNodeFound then
                    exit;

                xmlElemRoot := xmlNode.AsXmlElement();

                xmlSubDoc := xmlDocument.Create();
                xmlElem := xmlElement.Create('root');
                xmlElemSubRoot := XmlElement.Create(uniqueNodeLbl);
                xmlSubDoc.Add(xmlElem);
                isSubRoot := true;
                repeat
                    ProcessDataExchColumnDef(DataExchColumnDef, GetDataExchFieldValue(DataExch."Entry No.", 1, DataExchColumnDef."Column No."), xmlElem, xmlElemSubRoot, isSubRoot);
                until DataExchColumnDef.Next() = 0;

                if xmlElemSubRoot.Name <> uniqueNodeLbl then
                    xmlElemRoot.Add(xmlElemSubRoot);
            end else begin
                xmlDoc := xmlDocument.Create();

                IsHandled := false;
                OnBeforeCreateXMLDeclaration(DataExchDef, xmlDec, IsHandled);
                if not IsHandled then
                    xmlDec := xmlDeclaration.Create('1.0', 'utf-8', 'yes');

                xmlDoc.SetDeclaration(xmlDec);

                xmlPath := DataExchColumnDef.Path;
                if not xmlPath.Split('/').Get(2, nName) then
                    nName := 'root';

                nVal := GetDataExchFieldValue(DataExch."Entry No.", 1, DataExchColumnDef."Column No.");

                DefaultNameSpace := DataExchLineDef.Namespace;
                xmlNamespaceManager.NameTable(xmlDoc.NameTable());
                xmlNamespaceManager.AddNamespace(DefNameSpacePrefixLbl, DefaultNameSpace);

                IsHandled := false;
                OnBeforeCreateRootElement(DataExchDef, xmlElem, nName, nVal, DefaultNameSpace, xmlNamespaceManager, IsHandled);
                if not IsHandled then
                    xmlElem := xmlElement.Create(nName, DefaultNameSpace, nVal);

                xmlDoc.Add(xmlElem);
                if DataExchColumnDef.Next() = 0 then
                    exit;

                repeat
                    ProcessDataExchColumnDef(DataExchColumnDef, GetDataExchFieldValue(DataExch."Entry No.", 1, DataExchColumnDef."Column No."), xmlElem, xmlElemSubRoot, isSubRoot);
                    xmlDoc.GetRoot(xmlElem);
                until DataExchColumnDef.Next() = 0;
            end;
    end;

    local procedure GetDataExchFieldValue(DataExchEntryNo: Integer; LineNo: Integer; ColumnNo: Integer) nVal: Text
    var
        DataExchField: Record "Data Exch. Field";
    begin
        if DataExchField.Get(DataExchEntryNo, LineNo, ColumnNo) then
            nVal := DataExchField.GetValue()
        else
            nVal := '';
    end;

    local procedure ProcessDataExchColumnDef(var DataExchColumnDef: Record "Data Exch. Column Def"; eValue: Text; var xmlElem: XmlElement; var xmlElemSubRoot: XmlElement; var isSubRoot: Boolean)
    var
        xmlElemNew: XmlElement;
        xmlNode: XmlNode;
        xmlNodeList, xmlNodeList2 : XmlNodeList;
        xmlAttributesText: List of [Text];
        xmlAttributeText: Text;
        xPathPart, attrName, attrValue, eName : Text;
        xmlNodesText: List of [Text];
        i: Integer;
        IsHandled: Boolean;
        NameSpacePrefix, NameSpace : Text;
    begin
        NameSpace := DefaultNameSpace;
        NameSpacePrefix := DefNameSpacePrefixLbl;

        Clear(xmlNodesText);
        xPathPart := DataExchColumnDef.Path;
        xmlNodesText := xPathPart.Split('/');
        for i := 1 to xmlNodesText.Count() do begin
            xPathPart := xmlNodesText.Get(i);
            if xPathPart <> '' then begin
                if StrPos(xPathPart, ':') <> 0 then begin
                    NameSpacePrefix := CopyStr(xPathPart, 1, StrPos(xPathPart, ':') - 1);
                    xmlNamespaceManager.LookupNamespace(NameSpacePrefix, NameSpace);
                    xPathPart := CopyStr(xPathPart, StrPos(xPathPart, ':') + 1);
                end;
                xmlElem.SelectNodes(StrSubstNo(xPathLbl, NameSpacePrefix, xPathPart), xmlNamespaceManager, xmlNodeList);
                if (xmlNodeList.Count = 0) or (i = xmlNodesText.Count) then
                    case true of
                        StrPos(xPathPart, '@') = 0:
                            begin
                                eName := xPathPart;
                                IsHandled := false;
                                OnBeforeCreateXMLNodeWithoutAttributes(eName, eValue, DataExchColumnDef, NameSpace, IsHandled);
                                if not IsHandled then begin
                                    if DataExchColumnDef."Export If Not Blank" and (eValue = '') then
                                        exit;
                                    if i = xmlNodesText.Count() then
                                        xmlElemNew := xmlElement.Create(eName, NameSpace, eValue)
                                    else
                                        xmlElemNew := xmlElement.Create(eName, NameSpace, '');
                                    xmlElem.Add(xmlElemNew);
                                    xmlElem := xmlElemNew;
                                    if isSubRoot then begin
                                        xmlElemSubRoot := xmlElem;
                                        isSubRoot := false;
                                    end;
                                end;
                            end;
                        (StrPos(xPathPart, '@') <> 0) and (StrPos(xPathPart, '=') = 0):
                            begin
                                eName := xPathPart.Split('[').Get(1);
                                if xmlElem.SelectNodes(StrSubstNo(xPathLbl, NameSpacePrefix, eName), xmlNamespaceManager, xmlNodeList2) then
                                    if xmlNodeList2.Get(xmlNodeList2.Count(), xmlNode) then begin
                                        xmlElem := xmlNode.AsXmlElement();
                                        attrName := xPathPart.Split('[@').Get(2).TrimEnd(']');

                                        IsHandled := false;
                                        OnBeforeCreateXMLAttribute(attrName, eValue, DataExchColumnDef, NameSpace, IsHandled);
                                        if not IsHandled then begin
                                            if DataExchColumnDef."Export If Not Blank" and (eValue = '') then
                                                exit;
                                            xmlElem.SetAttribute(attrName, eValue);
                                        end;
                                    end;
                            end;
                        (StrPos(xPathPart, '@') <> 0) and (StrPos(xPathPart, '=') <> 0):
                            begin
                                eName := xPathPart.Split('[').Get(1);
                                IsHandled := false;
                                OnBeforeCreateXMLNodeWithAttributes(eName, eValue, DataExchColumnDef, NameSpace, IsHandled);
                                if not IsHandled then begin
                                    if DataExchColumnDef."Export If Not Blank" and (eValue = '') then
                                        exit;
                                    if i = xmlNodesText.Count() then
                                        xmlElemNew := xmlElement.Create(eName, NameSpace, eValue)
                                    else
                                        xmlElemNew := xmlElement.Create(eName, NameSpace, '');
                                    xmlElem.Add(xmlElemNew);
                                    xmlElem := xmlElemNew;

                                    attrValue := xPathPart.Split('[').Get(2).TrimEnd(']');
                                    xmlAttributesText := attrValue.Split(' and ');
                                    foreach xmlAttributeText in xmlAttributesText do begin
                                        attrName := xmlAttributeText.Split('=').Get(1).TrimStart('@');
                                        attrValue := xmlAttributeText.Split('=').Get(2).TrimStart('"').TrimEnd('"');
                                        xmlElem.SetAttribute(attrName, attrValue)
                                    end;
                                    if isSubRoot then begin
                                        xmlElemSubRoot := xmlElem;
                                        isSubRoot := false;
                                    end;
                                end;
                            end;
                        else
                            Error(XPathNotSupportedErr);
                    end
                else begin
                    xmlNodeList.Get(xmlNodeList.Count, xmlNode);
                    xmlElem := xmlNode.AsXmlElement();
                end;
            end;
        end;
    end;

    var
        DefaultNameSpace: Text;
        xmlNamespaceManager: XmlNamespaceManager;
        RootHeaderElementNotFoundErr: Label 'Root header element is not found.';
        XPathNotSupportedErr: Label 'xPath is not supported.';
        DefNameSpacePrefixLbl: Label 'def', Locked = true;
        xPathLbl: Label '//%1:%2', Locked = true;
        uniqueNodeLbl: Label 'eb25fce3-1cc0-4ed5-b87a-ff9c31a895f5', Locked = true;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCreateXMLNodeWithoutAttributes(var xmlNodeName: Text; var xmlNodeValue: Text; var DataExchColumnDef: Record "Data Exch. Column Def"; var DefaultNameSpace: Text; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCreateXMLAttribute(var xmlAttributeName: Text; var xmlAttributeValue: Text; var DataExchColumnDef: Record "Data Exch. Column Def"; var DefaultNameSpace: Text; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCreateXMLNodeWithAttributes(var xmlNodeName: Text; var xmlNodeValue: Text; var DataExchColumnDef: Record "Data Exch. Column Def"; var DefaultNameSpace: Text; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeExportHeader(var DataExch: Record "Data Exch."; var xmlDoc: XmlDocument; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeExportDetails(var DataExch: Record "Data Exch."; var xmlDoc: XmlDocument; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeProcessDataExc(var DataExch: Record "Data Exch."; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCreateXMLDeclaration(DataExchDef: Record "Data Exch. Def"; var xmlDec: XmlDeclaration; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCreateRootElement(DataExchDef: Record "Data Exch. Def"; var xmlElem: XmlElement; var nName: Text; var nVal: Text; DefaultNameSpace: Text; var xmlNamespaceManager: XmlNamespaceManager; var IsHandled: Boolean)
    begin
    end;
}