Exception parsing XML File

0

I'm developing a Delphi Firemonkey routine (version 10.4.2) to parse an incoming XML file as a way to export data from my application in multiple file formats. Here is the relevant code I'm using:

procedure ParseFieldList(FieldsNode: IXMLNode);
var
  i: integer;
  FieldNode: IXMLNode;
  FieldRec: TDataDescRecClass;
  RecFieldID,
  FieldName,
  DataType,
  MaxLength,
  Precision: string;
begin
  for i := 0 to FieldsNode.ChildNodes.Count-1 do begin
    FieldNode := FieldsNode.ChildNodes.Get(i);
    if FieldNode.NodeType = ntElement then begin
      RecFieldID := FieldNode.Attributes['db_id'];
      FieldName := FieldNode.ChildValues['field_name'];
      DataType := FieldNode.ChildValues['datatype'];
      MaxLength := FieldNode.ChildValues['maxlength'];
      Precision := FieldNode.ChildValues['precision'];
      FieldRec := RecDesc.TDataDescRecClass.Create(RecFieldID,
            FieldName, DataType, MaxLength, Precision);
      DataDescList.AddDescRec(FieldRec);
    end;
  end;
end;

procedure ParseDataList(DataRecsNode: IXMLNode);
var
  i, j: integer;
  idx: integer;
  DataNode: IXMLNode;
  DataFieldNode: IXMLNode;
  FieldRec: TDataDescRecClass;
begin
  for i := 0 to DataRecsNode.ChildNodes.Count-1 do begin
    DataNode := DataRecsNode.ChildNodes.Get(i);
    if DataNode.NodeType = ntElement then begin
      if DataNode.HasChildNodes then begin
        for j := 0 to DataNode.ChildNodes.Count-1 do begin
          DataFieldNode := DataNode.ChildNodes.Get(j);
          if (DataFieldNode.NodeType = ntElement) or (DataFieldNode.IsTextElement) then begin
            idx := StrToInt(DataFieldNode.Attributes['db_id']);
            FieldRec := DataDescList.DataDescRec[idx-1];
            if (FieldRec.DataType = 'C') and (DataFieldNode.Text > '') then
              FieldRec.Value := Format('%m', [StrToFloat(DataFieldNode.Text)])
            else
              FieldRec.Value := DataFieldNode.Text;
            FieldRec.ValueSet := True;
            FieldRec.DetailField := DetailData;
          end;
        end;
      end;
      DataDescList.ClearDataValues;
    end;
  end;
end;

function FileExport(FieldStream: TMemoryStream; var ErrorStr: PChar): integer;
var
  FileName: string;
  ErrStr: string;
  TitlesIncluded: boolean;
  i: Integer;
  fXML: TXMLDocument;
  BaseNode: IXMLNode;
  GroupNode: IXMLNode;
begin
  Result := 0;
  ErrStr := '';

  { Initialize the XML parsing parameters }
  DataDescList := TDataDescListClass.Create;

  { Parse the memory stream to get the fields and data to be exported }
  FieldStream.Position := 0;
  fXML := TXMLDocument.Create(nil);
  try
    try
      { Initialize the XML parsing parameters }
      fXML.DOMVendor := GetDOMVendor(sAdom4XmlVendor);
      DefaultDOMVendor := fXML.DOMVendor.Description;
      fXML.Active := True;
      fXML.Options := fXML.Options - [doNodeAutoCreate];
      fXML.Version := '1.0';
      fXML.LoadFromStream(FieldStream);

      { Parse the memory stream to get the fields and data to be exported }
      BaseNode := fXML.Node.ChildNodes.Get(1);
      if BaseNode <> nil then begin
        for i  := 0 to BaseNode.ChildNodes.Count-1 do begin
          GroupNode := BaseNode.ChildNodes.Get(i);
          if GroupNode.IsTextElement then begin
            if GroupNode.NodeName = FileNameTag then begin
              FileName := GroupNode.Text;
              try
                AssignFile(OutputFile, FileName);
                ReWrite(OutputFile);
              except
                on E: EInOutError do begin
                  raise EExportImportError.Create(Format(CannotOpenExportFile,
                        [Filename, EInOutError(E).Message]));
                end;
              end;
            end
          end
          else if GroupNode.NodeType = ntElement then begin
            if GroupNode.NodeName = FieldRecsTag then begin
              TitlesIncluded := GroupNode.Attributes[TitlesAttr] = 'Y';
              ParseFieldList(GroupNode);
            end
            else if GroupNode.NodeName = DataRecsTag then
              ParseDataList(GroupNode);
          end;
        end;
        CloseFile(OutputFile);
      end;
    except
      on E: EExportImportError do begin
        Result := cCannotOpenExportFile;
//        MessageDlgCtr('Data Export error', E.Message, mtWarning, [mbOK], 0);
      end;
    end;
  finally
    ErrorStr := PChar(ErrStr);
    FreeAndNil(DataDescList);
    FreeAndNil(fXML);
  end;
end;

The XML file is generated by the app and passed as a Memory Stream to the routine, where it is parsed and then re-written in a specific file format. The issue is that if the a node in the XML file data contains a hyphen, it causes the following node to fail with an "access violation at 0x00000000: read of address 0x00000000." The exception comes from ParseDataList - DataFieldNode := DataNode.ChildNodes.Get(j);. I've traced it to the CreateChildList function - NodeList.List.Add(CreateChildNode(DOMNode.childNodes[I])) step in the XML.XMLDoc unit (and the call to _IntfClear in the System unit). Here is a sample XML file I've been testing with:

<?xml version="1.0" encoding="UTF-8"?>
<transfer transfertype="Export">
  <filename>E:\Temp\Test.csv</filename>
  <field_records delimiter="" quotechar="" titles_included="N">
    <field_record db_id="1">
      <field_name>Name of Item</field_name>
      <datatype>S</datatype>
      <maxlength>0</maxlength>
      <precision>0</precision>
    </field_record>
    <field_record db_id="2">
      <field_name>Item Description</field_name>
      <datatype>S</datatype>
      <maxlength>0</maxlength>
      <precision>0</precision>
    </field_record>
    <field_record db_id="3">
      <field_name>Serial Number</field_name>
      <datatype>S</datatype>
      <maxlength>0</maxlength>
      <precision>0</precision>
    </field_record>
  </field_records>
  <data_records>
    <data_record>
      <field db_id="1">Chest of Drawers</field>
      <field db_id="2">Chest, 3 Drawer, Copper</field>
      <field db_id="3">CD23</field>
    </data_record>
    <data_record>
      <field db_id="1">Chest of Drawers</field>
      <field db_id="2">Chest, 4 Drawer, Copper</field>
      <field db_id="3">CD25-1</field>
    </data_record>
    <data_record>
      <field db_id="1">Comforter</field>
      <field db_id="2">FQ Blanket</field>
      <field db_id="3">HB23</field>
    </data_record>
  </data_records>
</transfer>

I'm not clear why a hyphen in the data would cause this problem and how to work around it. (There is also another failure when trying to free the fXML object, but I'd like to get past the parsing issue before I get to that.) Any assistance would be greatly appreciated.

xml
delphi
firemonkey
asked on Stack Overflow May 3, 2021 by Gordon Turner

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0