Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Probleme mit XML Encoding (https://www.delphipraxis.net/195002-probleme-mit-xml-encoding.html)

tofse 25. Jan 2018 14:04

Probleme mit XML Encoding
 
Hallo,
ein Fremdprogramm X exportiert XML Dateien. Diese soll mein Programm verarbeiten. Ich habe keine Möglichkeit, auf den Export Einfluss zu nehmen.
Aufgebaut sind die Dateien immer gleich

Code:
<HEDX>
<BODY>
<ExportType>Export</ExportType>
<SWVersion>
<Name>Viewing Module</Name>
<Version>6.5.7.0</Version>
</SWVersion>
<Patient>
<PatientUIDList>
<NumUIDs>1</NumUIDs>
<PatientUID>
<Source>LOC1456211344</Source>
<UID>LOC1456211344.418d33f0-10c5-4445-8a20-f3bd5f62190b</UID>
</PatientUID>
</PatientUIDList>
<ID>3005</ID>
<PID>200115203</PID>
<LastName>Müller</LastName>
<FirstNames>Franz</FirstNames>
usw.

Mein Code
Delphi-Quellcode:
procedure Te2e.starteVerarbeitung;
var Xml : IXMLDocument;

begin
  Xml:=newXMLDocument;
  try
        Xml.Encoding:='UTF-8';
        Xml.LoadFromFile(FXml);

        Xml.Active:=True;
Es gibt nun ein Problem mit den Umlauten, als Exception erscheint immer

Erste Gelegenheit für Exception bei $7576C54F. Exception-Klasse EDOMParseError mit Meldung
'Im Textinhalt wurde ein ungültiges Zeichen gefunden.

Zeile: 18
<LastName>M'.


Ich hatte es auch schon mit LoadFromStream versucht, da kann man das Encoding ja auch mit angeben, aber das gleiche Problem
Hat jemand eine Idee?

Gruß

Fukiszo 25. Jan 2018 14:07

AW: Probleme mit XML Encoding
 
das umlaut löst den fehler aus, das sollte wahrscheinlich berücksichtigt werden.

Grüße

tofse 25. Jan 2018 14:08

AW: Probleme mit XML Encoding
 
Das hatte ich ja geschrieben
"Es gibt nun ein Problem mit den Umlauten"
Nur wie gehe ich damit um?

Uwe Raabe 25. Jan 2018 14:14

AW: Probleme mit XML Encoding
 
Versuch mal

Delphi-Quellcode:
  Xml.Encoding:='windows-1252';

tofse 25. Jan 2018 14:15

AW: Probleme mit XML Encoding
 
Leider der gleiche Fehler

Fukiszo 25. Jan 2018 14:24

AW: Probleme mit XML Encoding
 
damit wollt ich andeuten, du kennst ja dein problem und wollt auf soetwas in etwa hinaus, einen xml parser

Zitat:

Title: XML File Viewer

Question: How to create a simple XML File viewer, without worrying about the XML itself. The easiest solution is using the Microsoft XML Document Object Model, installed on every machine that run MS Internet Explorer 4 or higher.

Answer:
In this article I am showing you a simple XML file viewer, that may be extended to an XML editor with some work. You may enhance the editor by importing icons for the different node types, etc. That's up to you.

My main idea is to show you how to import type libraries from readily available components installed on nearly every machine in the modern windows world.

Note: The MS XML DOM is available for free download and redistribution at msdn.microsoft.com.

IMPORTING THE MS XML TYPE LIBRARY
=================================

Start Delphi and create a new application, I you haven't done so already. In the Delphi menu go to Project|Import Type Library... A dialog will appear on your screen, with a list of all installed and registered COM libraries available for import. Take a moment and scroll through it, you might be surprised.

Somewhere down the list you find Microsoft XML, version 2.0 (Version 2.0). This is the type library we are going to import. Additionally, you may see Microsoft XML, v3.0 (Version 3.0). This is a newer and faster version from MS, we are going to use the older version however, since it is more common.

After selecting the MS XML, version 2.0 component object, select a Unit Directory, and press the Create Unit button. The Install button will install the component in your Component Pallete, additionally.

PREPARING YOUR APPLICATION FORM
===============================

Drop a MainMenu (Standard) component on your form, and insert a Open Menu item (name: Open1).
Drop a TreeView (Win32) component on your form, set Align=alLeft and ReadOnly=True (name: trvStructure).
Drop an OpenDialog (Dialogs) component on your form (name: OpenDialog1).
Drop a Panel (Standard) component on your form, set Align=alClient and clear the Caption (name: Panel1).
Drop a StringGrid (Additional) component on the Panel1 set Align=alTop, RowCount=2, ColCount=2, FixedCols=0, FixedRows=1 (name: grdAttributes).
Drop a Memo (Standard) on the Panel1 set Align=alClient, ReadOnly=True (name mmoNodeContent).

Note: The names I have used will appear in the source code again!

A PSEUDO CLASS FOR THE XML INTERFACES
=====================================

Because mapping of interface via pointers introduces some problems I chose to create a simple class that contains only on variable holding the reference to the XML Node interface.

type
TXMLNodeWrapper = class
private
FNode: IXMLDOMNode;
protected
public
constructor Create(aNode: IXMLDOMNode);
property Node: IXMLDOMNode read FNode;
end;

The constructor will save the reference in the FNode variable.

CREATING THE XML DOM OBJECT
===========================

Creating an instance of the object is rather simple. Having a variable FDocument of the type IXMLDOMDocument, defined in the imported MSXML_TLB.

FDocument := CoDOMDocument.Create;

Next you need to set up the component to your needs.

FDocument.async := False;
FDocument.validateOnParse := True;
FDocument.preserveWhiteSpace := True;

The first I want to do is inserting an base element into the document. Every XML document needs at least this base element. I have named it xmlstart.

Note: Be careful, XML is case-sensitive.

FDocument.appendChild(FDocument.createNode(NODE_EL EMENT, 'xmlstart', ''));

PARSING THE XML DOCUMENT
========================

There are quite many ways of parsing XML. I want to show you two recursive ways, that are very similar, but have quite different results.

(1) NodeList := Node.childNodes;

Returns all children, inlcude some special node types, such as #text or #comment. These node types require special care.

(2) NodeList := Node.selectNodes('*');

Returns all standard node types, that can be accessed via XSL (XML Structured Language). These node types are easy in use.

ACCESSING THE NODE LIST
=======================

Accessing any item in a Node List is very easy. The length returns the count of items in the list (equal to Delphis Count property). The Item array gives access to every Item of the node list.

for I := 0 to Pred(XMLNodeList.length) do
ShowMessage(XMLNodeList.item[I].nodeName);

MORE INFORMATION ABOUT THE MS XML DOM
=====================================

The most important web addresses for the MS XML DOM are:

http://msdn.microsoft.com/xml (all about XML)
http://131.107.228.20/xquerydemo/demo.aspx (XML Query Language Demo)
http://msdn.microsoft.com/downloads/.../072/topic.xml (Downloads)

THE SOURCE CODE FOR THE XML VIEWER
==================================

unit uMainForm;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
MSXML_TLB, ComCtrls, Menus, Grids, ExtCtrls, StdCtrls;

type
TXMLNodeWrapper = class
private
FNode: IXMLDOMNode;
protected
public
constructor Create(aNode: IXMLDOMNode);
property Node: IXMLDOMNode read FNode;
end;

TfrmMain = class(TForm)
MainMenu1: TMainMenu;
File1: TMenuItem;
Open1: TMenuItem;
trvStructure: TTreeView;
OpenDialog1: TOpenDialog;
Panel1: TPanel;
grdAttributes: TStringGrid;
mmoNodeContent: TMemo;
procedure FormCreate(Sender: TObject);
procedure Open1Click(Sender: TObject);
procedure trvStructureChange(Sender: TObject; Node: TTreeNode);
private
FDocument: IXMLDOMDocument;
FFileName: String;
procedure LoadXML;
public
end;

var
frmMain: TfrmMain;

implementation

{$R *.DFM}

{ TXMLNodeWrapper }

constructor TXMLNodeWrapper.Create(aNode: IXMLDOMNode);
begin
inherited Create;
FNode := aNode;
end;

{ TFrmMain }

procedure TfrmMain.FormCreate(Sender: TObject);
begin
FDocument := CoDOMDocument.Create;
FDocument.async := False;
FDocument.validateOnParse := True;
FDocument.preserveWhiteSpace := True;
FDocument.appendChild(FDocument.createNode(NODE_EL EMENT, 'xmlstart', ''));

grdAttributes.Cells[0, 0] := 'Attribute name';
grdAttributes.Cells[1, 0] := 'Attribute value';
end;

procedure TfrmMain.LoadXML;
procedure EnterNode(const XMLNode: IXMLDOMNode; TreeNode: TTreeNode);
var
I: Integer;
XMLNodeList: IXMLDOMNodeList;
NewTreeNode: TTreeNode;
begin
NewTreeNode := trvStructure.Items.AddChild(TreeNode, XMLNode.nodeName);
NewTreeNode.Data := TXMLNodeWrapper.Create(XMLNode);
// use XMLNode.childNodes to get all nodes (incl. special types)
XMLNodeList := XMLNode.selectNodes('*');
for I := 0 to Pred(XMLNodeList.length) do
EnterNode(XMLNodeList.item[I], NewTreeNode);
end;
begin
for I := 0 to trvStructure.Items.Count - 1 do
TXMLNodeWrapper(trvStructure.Items.Item[I].Data).Destroy;
trvStructure.Items.BeginUpdate;
try
trvStructure.Items.Clear;
EnterNode(FDocument.documentElement, nil);
finally
trvStructure.Items.EndUpdate;
end;
end;

procedure TfrmMain.Open1Click(Sender: TObject);
begin
if OpenDialog1.Execute then
begin
FDocument.load(OpenDialog1.FileName);
FFileName := OpenDialog1.FileName;
LoadXML;
end;
end;

procedure TfrmMain.trvStructureChange(Sender: TObject; Node: TTreeNode);
var
I: Integer;
CurrentNode: IXMLDOMNode;
begin
CurrentNode := TXMLNodeWrapper(Node.Data).Node;
Caption := CurrentNode.nodeName;
if CurrentNode.selectNodes('*').length = 0 then
mmoNodeContent.Text := CurrentNode.text
else
mmoNodeContent.Text := '';
if CurrentNode.attributes.length 0 then
begin
grdAttributes.RowCount := Succ(CurrentNode.attributes.length);
grdAttributes.FixedRows := 1;
for I := 0 to Pred(CurrentNode.attributes.length) do
begin
grdAttributes.Cells[0, Succ(I)] := CurrentNode.attributes.item[I].nodeName;
grdAttributes.Cells[1, Succ(I)] := CurrentNode.attributes.item[I].text;
end;
end else begin
grdAttributes.RowCount := 2;
grdAttributes.Cells[0, 1] := '';
grdAttributes.Cells[1, 1] := '';
end;
end;

end.
man könnte die Namensfelder per Parser "korrigieren", falls das helfen könnte.

grüße

tofse 25. Jan 2018 14:25

AW: Probleme mit XML Encoding
 
Habe es nun anders gelöst.
Lese erst die XML Datei in einer Variable FXMLContent und setze an den Anfang die Encoding Informationen.
Danach verwende ich
Delphi-Quellcode:
Xml.LoadFromXML(FXMLContent);

himitsu 25. Jan 2018 14:50

AW: Probleme mit XML Encoding
 
Zitat:

Zitat von tofse (Beitrag 1391987)
Leider der gleiche Fehler

Das ist auch richtig so.

Delphi-Quellcode:
Xml.Encoding
hat garnichts mit dem Einlesen zu tun.
Da steht das Encoding drin, was beim Auslesen der XML genutzt wurde, bzw. welches beim Speichern verwendet wird.

Erstmal ist diese XML nicht valide aka "ungültig", womit der Fehler völlig korrekt ist.
Es gilt das Encoding via BOM (für den ersten XML-Node), wenn kein BOM dann UTF-8. Und gibt es eine Processing Instruction (
Delphi-Quellcode:
<?xml ...?>
), dann gilt nach ihr das darin definierte Encoding.
Als Ausnahme gelten UTF-16 LE und BE, welche auch ohne BOM am ersten
Delphi-Quellcode:
<
erkannt werden können. (#0'<' oder '<'#0)
Ohne Angabe eines Encoding sind XML immer UTF-8, also liegt der Fehler beim schreibenden Programm, wenn es ein falsches Encoding verwendet, als es (nicht) in der XML definiert hat.

Kann man beim LoadFromFile ein TEncoding-Parameter übergeben werden, um das Default-Encoding zu ändern?
Wenn nicht, dann mußt du die Datei selber einlesen, dabei das gewünschte TEncoding verwenden (bei normalen Textdateien ist es ANSI, unter Windows) und dann als String via LoadXML (oder wie das hieß) laden.
> Lesen mit TStringList oder Delphi-Referenz durchsuchenTFile.ReadAllText

Bernhard Geyer 25. Jan 2018 15:02

AW: Probleme mit XML Encoding
 
Zitat:

ein Fremdprogramm X exportiert XML Dateien.
Wenn die Datei so wie si angegeben ist aussieht, ist es kein gültiges XML.
Wenn du an diesem Fehler nix machen kannst (habe ich z.B. auch bei Adobe-SW die - sagen wir mal Adobe XML schreibt -), so solltest du evtl ein FixDefectXMLFromXYZ-Methode schreiben welche die Codierung korrekt setzt und den XML-Header ebenfalls ergänzt. Dann kannst du mit dem XML-Parser deiner Wahl nutzen.

Redeemer 25. Jan 2018 22:59

AW: Probleme mit XML Encoding
 
Was Bernhard meint, ist das hier:
Delphi-Quellcode:
var
  Stream1: TFileStream;
  Stream2: TMemoryStream;
const
  Head: AnsiString = '<?xml version="1.0" encoding="UTF-8"?>'; // oder Encoding Windows-1252
begin
  Stream1 := TFileStream.Create(DateiDatei, fmOpenRead or fmShareDenyNone);
  Stream2 := TMemoryStream.Create;
  try
    Stream2.Write(Head[1], Length(Head));
    Stream2.Copyfrom(Stream1, 0);
    Stream2.Position := 0;
    // bla
    Xml.LoadFromStream(Stream2);
    // bla
  finally
    Stream1.Free;
    Stream2.Free;
  end;
end;


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:52 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