Мастер связывания данных XML с плавающей запятой и числами с фиксированной запятой - PullRequest
0 голосов
/ 18 октября 2018

Я использовал подготовленный XSD с помощью мастера привязки XML.

<?xml version="1.0" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
 <xs:element name="examples" type="examples"/>
 <xs:complexType name="examples">
  <xs:sequence>
   <xs:element name="example" type="example" minOccurs="1" maxOccurs="unbounded"/>
  </xs:sequence>
 </xs:complexType>
 <xs:complexType name="example">
  <xs:sequence>
   <xs:element name="doublevalue" type="xs:double"/>
   <xs:element name="decimalvalue" type="xs:decimal"/>
  </xs:sequence>
 </xs:complexType>
</xs:schema>

Прежде всего, почему по умолчанию по-разному обрабатываются десятичные и двойные числа?XML Data Binding Wizard Double enter image description here

Простой XML-тип Double переводится в собственный тип Delphi Double, а простой XML-тип Decimal по умолчанию переводится в собственный тип Delphi UnicodeString.

У меня одинаковая проблема с обоими типами данных: конфликты локалей

Я немец.Это означает, что DecimalSeparator - ,, а ThousandSeparator - . по умолчанию (Windows).

Когда я читаю мой пример XML следующим образом, двойное значение 0,08 становится 8-целым числом.

XML

<?xml version="1.0" encoding="UTF-8"?>
<examples>
  <example>
    <doublevalue>0.08</doublevalue>
    <decimalvalue>1001.015</decimalvalue>
  </example>
</examples>

Код

var
  xmldoc: IXMLDocument;
  examples: IXMLExamples;
  i: Integer;
  d: Double;
begin
  xmldoc := TXMLDocument.Create(nil) as IXMLDocument;
  try
    xmldoc.LoadFromFile('C:\temp\example.xml');
    examples := Getexamples(xmldoc); // Getexamples() is part of the unit generated by the Binding Wizard 
    for i := 0 to examples.Count - 1 do
      d := examples[i].Doublevalue;
  finally
    examples := nil;
    xmldoc := nil;
  end;
end;

Снимок Runtime Debug Snapshot

Сейчас я изменяю тип данных XML Double наСобственный тип Delphi UnicodeString и работа с методом, подобным следующему:

function XMLStringToDouble(const str: string): double;
var
  fs: TFormatSettings;
begin
  fs := FormatSettings;
  fs.DecimalSeparator := '.';
  fs.ThousandSeparator := #0;
  result := StrToFloat(str, fs);
end;

При создании XML-кода

существует еще одна проблема

var
  xmldoc: TXMLDocument;
  examples: IXMLExamples;
  example: IXMLExample;
begin
  xmldoc := TXMLDocument.Create(nil);
  try
    xmldoc.DOMVendor := MSXML_DOM;
    xmldoc.Options := [doNodeAutoCreate, doNodeAutoIndent, doAttrNull, doAutoPrefix, doNamespaceDecl];
    xmldoc.Active := true;
    xmldoc.Version := '1.0';
    xmldoc.Encoding := 'UTF-8';
    examples := xmldoc.GetDocBinding('examples', TXMLExamples, '') as IXMLExamples;
    example := examples.Add;
    example.Doublevalue := 0.08;
    example.Decimalvalue := '1001.015';
    xmldoc.SaveToFile('C:\temp\example.xml');
  finally
    xmldoc.Free
  end;
end;

Я получаюXML с , в качестве десятичного разделителя.

<?xml version="1.0" encoding="UTF-8"?>
<examples>
  <example>
    <doublevalue>0,08</doublevalue>
    <decimalvalue>1001.015</decimalvalue>
  </example>
</examples>

1.Есть ли способ относиться к Double более простым / правильным образом?

2.Могу ли я как-то передать TFormatSettings в XMLDocument или решить его совершенно другим способом?

3.Как ты это делаешь?

1 Ответ

0 голосов
/ 18 октября 2018

Я не могу воспроизвести вашу проблему (используя Delphi Tokyo 10.2.3).

Я сделал небольшой MCVE, чтобы доказать это, используя приведенный ниже код с немецкими региональными настройками, все равно будет читать / писать «.»в качестве десятичного разделителя:

program SO52863558;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  Windows,
  ActiveX,
  XmlDoc,
  XmlIntf,
  xmltest in 'xmltest.pas';

var
  Examples: IXMLExamples;
  Example: IXMLExample;
  i: Integer;
  d: Double;

begin
 try
  CoInitialize(nil);
  try
   Examples := Loadexamples('test.xml');
   for i := 0 to examples.Count - 1 do
    begin
     Writeln(examples[i].Doublevalue:0:2);
     Writeln(examples[i].Decimalvalue);
    end;
   Examples := nil;
   Examples := NewExamples;
   Example := Examples.Add;
   Example.Doublevalue := 0.012;
   Example.Decimalvalue := '12.51515';
   Examples.OwnerDocument.SaveToFile('test1.xml');
  finally
   Example := nil;
   Examples := nil;
   CoUninitialize;
  end;
 except
  on E: Exception do
   Writeln(E.Message);
 end;
 Readln;
end.

Единица, сгенерированная из вашего XSD-файла с помощью привязки:

unit xmltest;

interface

uses Xml.xmldom, Xml.XMLDoc, Xml.XMLIntf;

type

{ Forward Decls }

  IXMLExamples = interface;
  IXMLExample = interface;

{ IXMLExamples }

  IXMLExamples = interface(IXMLNodeCollection)
    ['{E86B6745-A58E-439F-A73F-B92F446B146B}']
    { Property Accessors }
    function Get_Example(Index: Integer): IXMLExample;
    { Methods & Properties }
    function Add: IXMLExample;
    function Insert(const Index: Integer): IXMLExample;
    property Example[Index: Integer]: IXMLExample read Get_Example; default;
  end;

{ IXMLExample }

  IXMLExample = interface(IXMLNode)
    ['{41DB1169-948C-4B28-BFB0-F63ACEAC14DB}']
    { Property Accessors }
    function Get_Doublevalue: Double;
    function Get_Decimalvalue: UnicodeString;
    procedure Set_Doublevalue(Value: Double);
    procedure Set_Decimalvalue(Value: UnicodeString);
    { Methods & Properties }
    property Doublevalue: Double read Get_Doublevalue write Set_Doublevalue;
    property Decimalvalue: UnicodeString read Get_Decimalvalue write Set_Decimalvalue;
  end;

{ Forward Decls }

  TXMLExamples = class;
  TXMLExample = class;

{ TXMLExamples }

  TXMLExamples = class(TXMLNodeCollection, IXMLExamples)
  protected
    { IXMLExamples }
    function Get_Example(Index: Integer): IXMLExample;
    function Add: IXMLExample;
    function Insert(const Index: Integer): IXMLExample;
  public
    procedure AfterConstruction; override;
  end;

{ TXMLExample }

  TXMLExample = class(TXMLNode, IXMLExample)
  protected
    { IXMLExample }
    function Get_Doublevalue: Double;
    function Get_Decimalvalue: UnicodeString;
    procedure Set_Doublevalue(Value: Double);
    procedure Set_Decimalvalue(Value: UnicodeString);
  end;

{ Global Functions }

function Getexamples(Doc: IXMLDocument): IXMLExamples;
function Loadexamples(const FileName: string): IXMLExamples;
function Newexamples: IXMLExamples;

const
  TargetNamespace = '';

implementation

uses Xml.xmlutil;

{ Global Functions }

function Getexamples(Doc: IXMLDocument): IXMLExamples;
begin
  Result := Doc.GetDocBinding('examples', TXMLExamples, TargetNamespace) as IXMLExamples;
end;

function Loadexamples(const FileName: string): IXMLExamples;
begin
  Result := LoadXMLDocument(FileName).GetDocBinding('examples', TXMLExamples, TargetNamespace) as IXMLExamples;
end;

function Newexamples: IXMLExamples;
begin
  Result := NewXMLDocument.GetDocBinding('examples', TXMLExamples, TargetNamespace) as IXMLExamples;
end;

{ TXMLExamples }

procedure TXMLExamples.AfterConstruction;
begin
  RegisterChildNode('example', TXMLExample);
  ItemTag := 'example';
  ItemInterface := IXMLExample;
  inherited;
end;

function TXMLExamples.Get_Example(Index: Integer): IXMLExample;
begin
  Result := List[Index] as IXMLExample;
end;

function TXMLExamples.Add: IXMLExample;
begin
  Result := AddItem(-1) as IXMLExample;
end;

function TXMLExamples.Insert(const Index: Integer): IXMLExample;
begin
  Result := AddItem(Index) as IXMLExample;
end;

{ TXMLExample }

function TXMLExample.Get_Doublevalue: Double;
begin
  Result := XmlStrToFloatExt(ChildNodes['doublevalue'].Text);
end;

procedure TXMLExample.Set_Doublevalue(Value: Double);
begin
  ChildNodes['doublevalue'].NodeValue := Value;
end;

function TXMLExample.Get_Decimalvalue: UnicodeString;
begin
  Result := ChildNodes['decimalvalue'].Text;
end;

procedure TXMLExample.Set_Decimalvalue(Value: UnicodeString);
begin
  ChildNodes['decimalvalue'].NodeValue := Value;
end;    
end.

Как видно из сгенерированного кода привязки данных, он использует XmlStrToFloatExt для преобразованияудвоить до строкиЕсли посмотреть на код этой функции:

type
  PFormatSettings = ^TFormatSettings;

var
  FormatSettings : TFormatSettings;

function GetFormatSettings: PFormatSettings;
begin
  if FormatSettings.DecimalSeparator <> XmlDecimalSeparator then
  begin
    FormatSettings := TFormatSettings.Create('');
    FormatSettings.DecimalSeparator := XmlDecimalSeparator;
  end;
  Result := @FormatSettings;
end;

function XmlFloatToStr(const Value: Extended): string;
begin
  Result := FloatToStr(Value, GetFormatSettings^);
end;

function XmlStrToFloat(const Value: string): Extended;
begin
  Result := StrToFloat(Value, GetFormatSettings^);
end;

function XmlStrToFloatExt(const Value: string): Extended;
var
  s: string;
begin
  s := Trim(Value);
  if s = '' then
    Result := 0.0
  else if SameText(Value, 'NaN') then
    Result := Nan
  else if SameText(Value, 'INF') then
    Result := Infinity
  else if SameText(Value, '-INF') then
    Result := NegInfinity
  else
    Result := XmlStrToFloat(Value);
end;

, вы увидите, что он всегда будет вызывать XmlDecimalSeparator (определяется как «.») Независимо от ваших региональных настроек.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...