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.
User contributions licensed under CC BY-SA 3.0