Встала задача импортировать данные из YML в БД (подробнее о формате здесь).
Решил попробовать написать небольшой класс, который поможет мне в чтении данных из YML. Но в процессе решения задачи начали одолевать сомнения в правильности подхода.
Является ли данный подход идеологически верным?
Исходники класса и пробного проекта прилагаю.
Пример прайс-листа можно взять, например, здесь: http://www.alas-nb.ru/yml.xml
- Код: Выделить всё
unit wYMLparser;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
DOM, xmlread
;
type
TCurrencyID = (criEUR, criUSD, criRUB, criKZT, criNONE);
// Currency
TCurrency = record
id: TCurrencyID;
rate: Double;
end;
// Category
TCategory = record
id: integer;
parentId: integer;
name: string;
end;
// Offer
TOffer = record
id: integer;
url: string;
price: double;
currencyId: TCurrencyID;
categoryId: integer;
name: string;
vendorCode: string;
model: string;
barcode: string;
end;
ArrayOfCurrencies = array of TCurrency;
ArrayOfCategories = array of TCategory;
ArrayOfOffers = array of TOffer;
{ TYML_catalog }
// Catalog
TYML_catalog = class
private
fDecimalSeparator: Char;
fYMLFile: string;
fDate: TDateTime;
fCompany: string;
fPhone: string;
fCurrencies: ArrayOfCurrencies;
fCategories: ArrayOfCategories;
fOffers: ArrayOfOffers;
Document: TXMLDocument;
function GetCurrencies():ArrayOfCurrencies;
function GetCategories():ArrayOfCategories;
function GetCurrencyID(aCurrencyString: string): TCurrencyID;
function GetOffers():ArrayOfOffers;
public
constructor Create(aYMLFile: string);
destructor Destroy;
procedure Log(aText: string);
function Open(): boolean;
property YMLFile: string read fYMLFile write fYMLFile;
property Date: TDateTime read fDate write fDate;
property Company: string read fCompany write fCompany;
property Phone: string read fPhone write fPhone;
property Currencies: ArrayOfCurrencies read fCurrencies write fCurrencies;
property Categories: ArrayOfCategories read fCategories write fCategories;
property Offers: ArrayOfOffers read fOffers write fOffers;
end;
implementation
{ TYML_catalog }
constructor TYML_catalog.Create(aYMLFile: string);
begin
fYMLFile:= aYMLFile;
fDecimalSeparator:= DefaultFormatSettings.DecimalSeparator;
end;
destructor TYML_catalog.Destroy;
begin
Document.Free;
Currencies:= nil;
Categories:= nil;
Offers:= nil;
end;
procedure TYML_catalog.Log(aText: string);
begin
//Here you can write your own output procedure
//Form1.Log(aText);
end;
function TYML_catalog.Open: boolean;
begin
try
result:= true;
Log('Open ' + YMLFile);
if Assigned(Document) then
begin
Destroy;
end;
ReadXMLFile(Document, YMLFile);
Log('Open...[OK]');
Currencies:= GetCurrencies; // Get Currencies
Categories:= GetCategories; // Get Categories
Offers:= GetOffers; // Get Offers
except
on E: Exception do
begin
result:= false;
Log(E.Message);
end;
end;
end;
function TYML_catalog.GetCurrencyID(aCurrencyString: string):TCurrencyID;
begin
case aCurrencyString of
'EUR': Result:= criEUR;
'USD': Result:= criUSD;
'RUB': Result:= criRUB;
'KZT': Result:= criKZT
else
Result:= criNONE;
end;
end;
function TYML_catalog.GetCurrencies: ArrayOfCurrencies;
var
Node: TDOMNode;
i: Integer;
begin
try
try
Result := nil;
Node := nil;
i := 0;
Node := Document.DocumentElement.FirstChild;
Node := Node.FindNode('currencies');
Node := Node.FindNode('currency'); //currency
while Assigned(Node) do
begin
// Used ChildNodes
Inc(i);
SetLength(Result, i);
with Node.ChildNodes do
begin
try
if Assigned(Node.Attributes) then
Result[i - 1].id:= GetCurrencyID(Node.Attributes[0].NodeValue);
TryStrToFloat(StringReplace(Node.Attributes[1].NodeValue,'.',fDecimalSeparator,[rfReplaceAll]), Result[i - 1].rate);
finally
Free;
end;
Node := Node.NextSibling;
end;
end; //while Assigned(Node)
finally
if Assigned(Node) then
Node.Free;
end;
except
on E: Exception do
begin
Result:= nil;
Log(E.Message);
end;
end;
end;
function TYML_catalog.GetCategories: ArrayOfCategories;
var
Node: TDOMNode;
i: Integer;
begin
try
try
Result := nil;
Node := nil;
i := 0;
Node := Document.DocumentElement.FirstChild;
Node := Node.FindNode('categories');
Node := Node.FindNode('category'); //category
while Assigned(Node) do
begin
// Used ChildNodes
Inc(i);
SetLength(Result, i);
with Node.ChildNodes do
begin
try
if Assigned(Node.Attributes) then
if Node.Attributes.Length = 2 then
begin
TryStrToInt(Node.Attributes[0].NodeValue, Result[i - 1].id);
TryStrToInt(Node.Attributes[1].NodeValue, Result[i - 1].parentId);
end
else
begin
TryStrToInt(Node.Attributes[0].NodeValue, Result[i - 1].id);
Result[i - 1].parentId:= 0;
end;
Result[i - 1].name := StringReplace(Node.TextContent, '"', '"', [rfReplaceAll]);
finally
Free;
end;
Node := Node.NextSibling;
end;
end; //while Assigned(Node)
finally
if Assigned(Node) then
Node.Free;
end;
except
on E: Exception do
begin
Result:= nil;
Log(E.Message);
end;
end;
end;
function TYML_catalog.GetOffers: ArrayOfOffers;
var
Node,ChildNode: TDOMNode;
i: Integer;
begin
try
try
Result := nil;
Node := nil;
ChildNode:= nil;
i:= 0;
Node := Document.DocumentElement.FirstChild;
Node := Node.FindNode('offers');
Node := Node.FindNode('offer'); //offer
while Assigned(Node) do
begin
// Used ChildNodes
Inc(i);
SetLength(Result,i);
with Node.ChildNodes do
begin
try
// id
if Assigned(Node.Attributes) then
TryStrToInt(Node.Attributes[0].NodeValue, Result[i-1].id);
//url
ChildNode:=Node.FindNode('url');
if Assigned(ChildNode) then
Result[i-1].url:= ChildNode.TextContent else Result[i-1].url:='';
//price
ChildNode:=Node.FindNode('price');
if Assigned(ChildNode) then
TryStrToFloat(StringReplace(ChildNode.TextContent,'.',fDecimalSeparator,[rfReplaceAll]),Result[i-1].price) else Result[i-1].price:=0;
//currencyId
ChildNode:=Node.FindNode('currencyId');
if Assigned(ChildNode) then
begin
Result[i - 1].currencyId:= GetCurrencyID(Node.TextContent);
end else
Result[i-1].currencyId:=criNONE;
// categoryId
ChildNode:=Node.FindNode('categoryId');
if Assigned(ChildNode) then
TryStrToInt(ChildNode.TextContent,Result[i-1].categoryId) else Result[i-1].categoryId:=0;
//name
ChildNode:=Node.FindNode('name');
if Assigned(ChildNode) then
Result[i-1].name:= StringReplace(ChildNode.TextContent, '"', '"', [rfReplaceAll]) else Result[i-1].name:='';
//vendorCode
ChildNode:=Node.FindNode('vendorCode');
if Assigned(ChildNode) then
Result[i-1].vendorCode:= StringReplace(ChildNode.TextContent, '"', '"', [rfReplaceAll]) else Result[i-1].vendorCode:='';
//model
ChildNode:=Node.FindNode('model');
if Assigned(ChildNode) then
Result[i-1].model:= StringReplace(ChildNode.TextContent, '"', '"', [rfReplaceAll]) else Result[i-1].model:='';
//barcode
ChildNode:=Node.FindNode('barcode');
if Assigned(ChildNode) then
Result[i-1].barcode:= ChildNode.TextContent else Result[i-1].barcode:='';
finally
Free;
end;
Node := Node.NextSibling;
end;
end; //while asigned
finally
if Assigned(Node) then
Node.Free;
end;
except
on E: Exception do
begin
Result:= nil;
Log(E.Message);
end;
end;
end;
end.
========
Исходники со всеми правками доступны здесь: https://github.com/wofs/wYMLparser.git