XML 数据绑定向导浮点数和定点数
XML Data Binding Wizard floating point and fixed point numbers
我已经用 XML 绑定向导消耗了我准备好的 XSD。
<?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>
首先,为什么 decimal 和 double 在默认情况下被区别对待?
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;
快照
现在我将 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,其中 ,
作为 DecimalSeparator。
<?xml version="1.0" encoding="UTF-8"?>
<examples>
<example>
<doublevalue>0,08</doublevalue>
<decimalvalue>1001.015</decimalvalue>
</example>
</examples>
1.有没有办法以 simpler/proper 的方式对待 Double?
2。我能否以某种方式将 TFormatSettings 传递给 XMLDocument 或以完全不同的方式解决它?
3。你是怎么做到的?
我无法重现您的问题(使用 Delphi Tokyo 10.2.3)。
我做了一个小的 MCVE 来证明这一点,使用下面的代码和德国区域设置仍然 read/write 一个 '.'作为小数点分隔符:
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
(定义为“.”),不管你的区域设置是什么。
我已经用 XML 绑定向导消耗了我准备好的 XSD。
<?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>
首先,为什么 decimal 和 double 在默认情况下被区别对待?
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;
快照
现在我将 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,其中 ,
作为 DecimalSeparator。
<?xml version="1.0" encoding="UTF-8"?>
<examples>
<example>
<doublevalue>0,08</doublevalue>
<decimalvalue>1001.015</decimalvalue>
</example>
</examples>
1.有没有办法以 simpler/proper 的方式对待 Double?
2。我能否以某种方式将 TFormatSettings 传递给 XMLDocument 或以完全不同的方式解决它?
3。你是怎么做到的?
我无法重现您的问题(使用 Delphi Tokyo 10.2.3)。
我做了一个小的 MCVE 来证明这一点,使用下面的代码和德国区域设置仍然 read/write 一个 '.'作为小数点分隔符:
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
(定义为“.”),不管你的区域设置是什么。