Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   XML (https://www.delphipraxis.net/46-xml/)
-   -   Delphi Probleme beim Arbeiten mit XML und Double-Werten (https://www.delphipraxis.net/207565-probleme-beim-arbeiten-mit-xml-und-double-werten.html)

HeZa 8. Apr 2021 17:48

Probleme beim Arbeiten mit XML und Double-Werten
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo,

der folgende Code führt unter Delphi-Seattle und Delphi-Rio (Update 3) zu unterschiedlichen Ausgaben:
Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
var
  node: IXMLNode;
  doubleValueInput: Double;
  doubleValueOutput: Double;
begin
  node := NewXMLDocument.GetDocBinding('Value', TXMLNode, '') as IXMLNode;
  doubleValueInput := 3.14;
  node.NodeValue := doubleValueInput;
  doubleValueOutput := node.NodeValue;
  Caption := Format('DoubleValueInput: %f NodeValue: %s, DoubleValueOutput: %f', [doubleValueInput, VarToStr(node.NodeValue), doubleValueOutput]);
end;
Ausgabe des Programms mit Delphi-Seattle kompiliert:
Code:
DoubleValueInput: 3,14, NodeValue: 3,14, DoubleValueOutput: 3,14
Ausgabe des Programms mit Delphi-Rio kompiliert:
Code:
DoubleValueInput: 3,14, NodeValue: 3.14, DoubleValueOutput: 314
Ist Jemand diese Situation bekannt?
Was kann man dagegen tun?
Wie sieht das Ergebnis unter Delphi-Sydney kompiliert aus?

Vielen Dank im Voraus.
Ciao Heza

PS. Den Code des Test-Programms habe ich angehängt.

himitsu 8. Apr 2021 19:09

AW: Probleme beim Arbeiten mit XML und Double-Werten
 
Tja, im Prinzip ist das aktuelle Verhalten "eigentlich" richtig. :angle:

* in der XML wird platformunabhängig gespeichert (Englisch mit Dezimalpunkt)
* beim Auslesen wird aus dem Text ein Variant/OleVariant
* und Variant/OleVariant nimmt beim Umwandeln die aktuellen Systemeinstellungen (nja, eigentlich falsch ... der sollte besser die Programmeinstellungen nehmen aber so lange du sie nicht änderst, passt es)
* und da das bei dir ein Dezimalkomma ist (Deutsch), wird der Punkt ignoriert

Double-from-XML: TXMLNode.GetNodeValue -> GetText -> _VarToReal -> _VarToDouble -> varOleStr:VarToDoubleAsString - VarR8FromStr(VAR_LOCALE_USER_DEFAULT)

Double-to-XML: _VarFromReal -> NodeValueToText -> varDouble:XmlFloatToStrExt -> _VarToDouble -> XmlFloatToStr -> FloatToStr(XmlFormatSettings) -> SetText

Leider ist es nicht möglich, einem Variant/OleVariant eine eigene Locale (FormatSettings) mitzugeben, so das es im Programm überall gleich behandelt wird.
* XMLString-to-Variant und Variant-to-Double macht hier der Variant (sprachabhängig)
* Double-to-Variant macht auch der Variant (sprachabhängig), aber dann Variant-to-XMLString macht die XML-Komponente (sprachunabhängig)

XML-Komponente: ist zwar richtig, aber leider falsch, da es beim Auslesen nicht auch so gemacht wird, aber geht auch nicht anders.
Die XML-Komponente kann nicht einfach den "." im String in ein "," tauschen, damit Variant-to-Double "richtig" arbeiten kann, denn was wäre, wenn der Punkt kein Dezimalpunkt, sondern einfach nur ein Punkt wäre. (z.B. eine Versionsnummer und keine Dezimalzahl)



Delphi-Quellcode:
uses Xml.XMLIntf, Xml.XMLDoc;

procedure TForm15.FormCreate(Sender: TObject);
var
  node: IXMLNode;
  doubleValueInput: Double;
  doubleValueOutput: Double;
begin
  node := NewXMLDocument.GetDocBinding('Value', TXMLNode, '') as IXMLNode;
  doubleValueInput := 3.14;
  node.NodeValue := doubleValueInput;
  doubleValueOutput := node.NodeValue;
  Memo1.Text := Format('DoubleValueInput: %f NodeText: %s, NodeValue: %s, DoubleValueOutput: %f',
    [doubleValueInput, node.Text, VarToStr(node.NodeValue), doubleValueOutput])
    + sLineBreak + Node.XML;
end;
Code:
DoubleValueInput: 3,14 NodeText: 3.14, NodeValue: 3.14, DoubleValueOutput: 314,00
<Value>3.14</Value>
Was kommt denn im Seattle raus?

Nja, Hier wirst du wohl oder übel selber den Zahl-String in einen Double konvertieren müssen.
Mit fällt auch keine "gute" Lösung ein, das beim NodeValue reparieren zu können. (außer du stellst dein Windows auf Englisch um oder siehe Tipp)
Delphi-Quellcode:
D := XmlStrToFloat(node.Text);
// oder
D := StrToFloat(node.Text, TFormatSettings.Create('en-US'));
Tipp: Bau dir für IXMLNode einen Record-Helper, welcher ein .AsFloat-Property hat und wo intern in beiden Richtungen gleich gearbeitet wird.
Delphi-Quellcode:
// unit Xml.xmlutil;
function XmlStrToFloat(const Value: string): Extended;
function XmlFloatToStr(const Value: Extended): string;
function XmlStrToFloatExt(const Value: string): Extended;
function XmlFloatToStrExt(const Value: Extended): string;
Die ...Ext beachten auch NaN, Infinity und NegInfinity.

HeZa 9. Apr 2021 06:33

AW: Probleme beim Arbeiten mit XML und Double-Werten
 
Hallo Himitsu,

Danke für die ausführliche Antwort.

Das war zwar nicht das was ich hören bzw. lesen wollte, ist aber für mich nachvollziehbar.

Es sollte gerade ein umfangreiches Projekt von Seattle nach Rio migriert werden. Da müssen jetzt erst mal alle XML verarbeitende Stellen untersucht werden, ob sie von dieser Änderung der XML-Verarbeitung zwischen Seattle und Rio betroffen sind. :roll:

@Alle
Machen es da eigentlich andere XML-Bibliotheken besser?

Ciao HeZa

himitsu 9. Apr 2021 09:02

AW: Probleme beim Arbeiten mit XML und Double-Werten
 
Das "Problem" hier ist, dass es einige Teile im Delphi gibt (wie z.B. die Konvertierung von Variant in den XMLString, aber das Andersrum fehlt) und dahnter eine Fremdkomponente von Microsoft sich versteckt, bzw. "Eine" ist es garnicht, da dort unterschiedliche Libraries und Versionen gekapselt sein können (z.B. MS-XML, OmniXML usw.), welche teilweise sich auch im Verhalten unterscheiden.

https://www.delphipraxis.net/20455-a...zum-msxml.html


Und ja, es gibt auch andere XMLLibs. (SuFu)
Die z.B. komplett in Pascal geschrieben sind und dann auf beiden Seiten gleich arbeiten können.
Achtung, wenn sie ebenfalls Variant einsetzen, dann gibt es da die gleichen Probleme. Aber mit Funktionen/Erweiterungen ala AsFloat geht es.
Es gibt auch Libs, welche die Sprache nicht beachten, also im deutschen Windows 3,14 speichern, anstatt 3.14 , was "jetzt" erstmal super funktioniert, welches dann aber knallt, wenn diese XML in einem anderssprachigem Windows geladen/gespeichert wird.

Ohne zusätzlich, z.B. als Attribut, in der XML zu speichern, dass dieses ein "Float" ist, dann ist eine automatische Behandlung/Konvertierung (Variant und Co.) garnicht möglich,
außer man gibt eben programmseitig das Verhalten vor (AsFloat).
Eine Heuristik, zum erkennen der "." und ",", also ob es nun ein Dezimalpunkt oder ein Tausenderpunkt ist, ist auch nicht zielführend, denn ist 3.142 nun Pi oder doch eine 3000?


Es gibt auch XML-Object-Mapping-Komponenten, welche die Typen richtig beachten.

Incocnito 17. Mär 2022 13:01

AW: Probleme beim Arbeiten mit XML und Double-Werten
 
Zitat:

Zitat von himitsu (Beitrag 1486735)
...
Tipp: Bau dir für IXMLNode einen Record-Helper, welcher ein .AsFloat-Property hat und wo intern in beiden Richtungen gleich gearbeitet wird....

Da wir gerade InHouse genau dieses Problem auch haben, hatte ich überlegt diesen Record-Helper zu bauen.

Aber ein Record-Helper geht scheinbar für Interfaces nicht. TXmlNode macht auch keinen Sinn.

Hat da jemand noch einen Trick. Ich komme gerade nicht drauf.

Nur mal so als Grundidee:
Delphi-Quellcode:
unit XmlHelper;

interface

uses
  System.SysUtils,
  Xml.XMLDoc;

type
  XmlNodeHelper = record helper for IXmlNode
    Function AsFloat() : Extended;
  end;

var
  XmlFormatSetting : TFormatSettings;

implementation

{ XmlNodeHelper }

Function XmlNodeHelper.AsFloat() : Extended;
var
  sValue : String;
begin
  sValue := Self.Text; // <- Einfach nur zu Debugging-Zwecken in extra Variable
  Result := StrToFloatDef(sValue, 0, XmlFormatSetting);
end;

initialization
  XmlFormatSetting := TFormatSettings.Create('en-US');

end.
Liebe Grüße
Incocnito


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:35 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz