![]() |
XML zum Verzweifeln
Ich nehme aber an, dass der Fehler bestimmt vor dem Monitor sitzt...
Ich lese eine XML Datei, recht gross, viele Unterstrukturen und Listen. Jetzt habe ich eine Zubehörliste pro Produkt und würde die Felder gern generisch auslesen. Der Code, der mich da verwirrt sieht so aus :
Code:
Das ist nur ein Code-Snippet, also Öffnen und die oberen Ebenen durchzugehen ist da nicht dabei..
var
lAccessories: IXMLNodeList; lAcc, lChild: IXMLNode; begin lAccessories := lItem.ChildNodes.FindNode('accessory_list').ChildNodes; // findet er lAcc := lAccessories.First; while Assigned(lAcc) do begin CodeSite.Send(csmLevel1, '', lAcc.ChildNodes.Count ); // Codesite schreibt 36 lChild := lAcc.ChildNodes.First; while Assigned(lChild) do begin CodeSite.Send(csmLevel1, lChild.NodeName, VarToStr(lChild.NodeValue)); lChild := lChild.NextSibling; end; lAcc := lAcc.NextSibling; end; end; Beim Durchlaufen kommt "natürlich" ein Fehler (sonst wäre es ja auch langweilig) : --------------------------- Debugger Exception Notification --------------------------- Project Import.exe raised exception class EXMLDocError with message 'Element "alternative_info" does not contain a single text node'. --------------------------- Break Continue Copy Help --------------------------- Hier mal ein Stück des XML
Code:
Nun die Frage:
<accessory_item>
<item_id>-2761267905524739048</item_id> <identification>1[1]</identification> <item_number/> <item_subnumber>1</item_subnumber> <item_number_architect/> <item_number_internal/> <item_number_online/> <item_piece_number_list/> <sorting_indicator>t</sorting_indicator> <harmonized_system_code_number/> <article_for_sale_number>NKM02</article_for_sale_number> <article_for_sale_number_internal>NKM02</article_for_sale_number_internal> <item_piece>1</item_piece> <short_description>NKM02 - Listwa transportowa</short_description> <internal>true</internal> <price_representation>price_charged_in_jumbo</price_representation> <print_image>false</print_image> <material_groups_key>Transport</material_groups_key> <commission_groups_key/> <revenue_accounts_description>STD</revenue_accounts_description> <F_A_cost_centres_key/> <ce_text_list/> <alternative_info> <--- hier crasht er <alternative>false</alternative> </alternative_info> <amendment_info> <amendment>false</amendment> </amendment_info> <needs_info> <as_needed>false</as_needed> </needs_info> <related_info> <taken_from_previous>false</taken_from_previous> </related_info> Woran erkenne ich solche Elemente, die dann "Unterelemente" haben ? Nodetype ist es nicht, ist immer die gleiche Ord-Zahl von 1 |
AW: XML zum Verzweifeln
So, habe jetzt rausgefunden, dass ich ein Element wie das "<ce_text_list/>" über IsTextElement=False + ChildNodes.Count = 0 rausfiltern kann (haben ja keinen Wert).
Die Unterlisten gehen dann über IsTextElement=False + ChildNodes.Count > 0 |
AW: XML zum Verzweifeln
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:
|
AW: XML zum Verzweifeln
Ich habe eine Pro-Version von Delphi (vom Arbeitgeber gestellt), da gibt es diesen Punkt nicht... :(
|
AW: XML zum Verzweifeln
Zitat:
|
AW: XML zum Verzweifeln
Danke, ich schau mal daheim in der Installation, weil im Büro sehe ich es nicht und GetIt funktioniert hier nicht (wohl irgendwas im Proxy falsch konfiguriert).
|
AW: XML zum Verzweifeln
Ich hab Delphi 10.4 Pro und habe ebenfalls diesen Wizard.
Nur ist er gegenüber früher, in der Kategorie Web und nicht in XML zu finden. |
AW: XML zum Verzweifeln
Ich habe jetzt im Endeffekt eine lauffähige rekursive Funktion, die den Namen des Unterlementes an den Parent-Namen (mit Punkt getrennt) anhängt. Das ganze wird
Code:
Im Codesite wird das dann als Baum ausgegeben, leere Elemente werden unterdrückt. Dazu gibt es noch einen Aufruf von DoIterate, der Falsxe zurückliefert, wenn etwas nicht durchgegangen werden soll (ich muss einige Sachen gesondert behandeln).
procedure IterateNode(ARoot: IXMLNode; ACurrentNodeName: String = '');
var lChild: IXMLNode; lNodeName: String; begin lNodeName := IfThen(ACurrentNodeName <> '', ACurrentNodeName + '.', ''); lChild := ARoot.ChildNodes.First; while Assigned(lChild) do begin if Not lChild.IsTextElement then begin if lChild.ChildNodes.Count > 0 then begin if DoIterate(lChild.NodeName) then begin CodeSite.EnterMethod(lChild.NodeName); IterateNode(lChild, lNodeName + lChild.NodeName); CodeSite.ExitMethod(lChild.NodeName); end; end; end else begin CodeSite.Send(csmLevel1, lNodeName + lChild.NodeName, VarToStr(lChild.NodeValue)); end; lChild := lChild.NextSibling; end; end; Jedenfalls ist es eine lauffähige Lösung, damit der Thread auch einen Abschluss hat :) |
AW: XML zum Verzweifeln
|
AW: XML zum Verzweifeln
Zitat:
|
AW: XML zum Verzweifeln
Eine aufgepette Version des Databinding-Assistenten findet man auch hier:
![]() Diese unterstützt ein bisschen mehr als der Assistent in Delphi. |
AW: XML zum Verzweifeln
Ich habe das XML Databinding gefunden, das Problem war im Endeffekt, dass man dazu ein offenes Projekt haben muss.
Nun hat mir der Wizard 3 Funktionen in den erstellten Sourcecode mit reingegeben : function GetORDER(Doc: IXMLDocument): IXMLORDERType; function LoadORDER(const FileName: string): IXMLORDERType; function NewORDER: IXMLORDERType; (ORDER ist wohl der 1. Knoten...) Nun die Frage... Sollte ich das Daten ändern wollen und das ganze wieder als XML speichern wollen, gibt es da auch eine Möglichkeit oder muss ich alles selbst machen und durch-iterieren ? Edit: Kann man vllt den Start-Knoten "einfach" der Rootnode eines TXMLDocument zuweisen und los geht's ? |
AW: XML zum Verzweifeln
var
LOrder: IXMLOrderType; LOrder.OwnerDocument.XML.Text sollte Dir den kompletten Inhalt des Baums als String liefern und den kannst Du dann in eine Datei schreiben. Dabei bitte das korrekte Encoding (normalerweise UTF-8) beachten. |
AW: XML zum Verzweifeln
Zitat:
Danke, das geht, nutze allerdings LOrder.OwnerDocument.SaveToFile... |
AW: XML zum Verzweifeln
So, ich will den Thread damit schliessen, dass ich kurz über die Fallstricke, und wie ich sie gelöst habe, schreibe, damit andere auch eine zufriedenstellende Lösung finden können.
Ich habe, wie gesagt wurde, den XML Data Binding Wizard genutzt, um die XML Datei einzulesen. Diese nutzte Namespaces, die natürlich nicht eingelesen wurden. Nodes wie dieser
Code:
wurden zwar eingefügt, aber ohne "bmecat" Namespace.
<bmecat:NAME2>Nebenstelle 2</bmecat:NAME2>
Ich habe dazu die erzeugten Klassen erweitert :
Delphi-Quellcode:
Dazu musste der Namespace aber entsprechend registriert werden, dies habe ich dann in der erstellen Funktion NewORDER gemacht (der Name kann bei anderen Klassen abweichen, ORDER war in meinem Fall der Rootnode.
function TXMLADDRESSType.Get_NAME2: UnicodeString;
begin Result := ChildNodes['bmecat:NAME2'].Text; end; procedure TXMLADDRESSType.Set_NAME2(const Value: UnicodeString); begin if Value <> EmptyStr then ChildNodes['bmecat:NAME2'].NodeValue := Value; end;
Delphi-Quellcode:
Jetzt gab es noch einen weiteren Fallstrick und zwar die Attribute in Verbindung mit Namespaces, wie hier :
function NewORDER: IXMLORDERType;
begin Result := NewXMLDocument.GetDocBinding('ORDER', TXMLORDERType, TargetNamespace) as IXMLORDERType; Result.Version := '2.1'; Result.DeclareNamespace('xsi', 'http://www.w3.org/2001/XMLSchema-instance'); // von mir hinzugefügt Result.DeclareNamespace('xsd', 'http://www.w3.org/2001/XMLSchema'); // von mir hinzugefügt Result.DeclareNamespace('bmecat', 'http://www.bmecat.org/bmecat/2005'); // von mir hinzugefügt end;
Code:
Dazu musste zum einen im erzeugten Delphi Code, wo der Node erzeugt wird, eine Änderung gemacht werden :
<bmecat:PARTY_ID type="supplier_specific">0000055905</bmecat:PARTY_ID>
Delphi-Quellcode:
Dort wurde zum einen vor PARTY_ID jeweils "bmecat:" hinzugefügt, zum anderen aber auch in RegisterChildNode noch der 3. Parameter mit der bmecat-URL angegefügt.
procedure TXMLPARTYType.AfterConstruction;
begin RegisterChildNode('bmecat:PARTY_ID', TXMLPARTY_IDType, 'http://www.bmecat.org/bmecat/2005'); inherited; end; function TXMLPARTYType.Get_PARTY_ID: IXMLPARTY_IDType; begin Result := ChildNodes['bmecat:PARTY_ID'] as IXMLPARTY_IDType; end; Beim Beschreiben des Nodes wurde dann aber anstelle der einfachen Zuweisung wie
Delphi-Quellcode:
dann der Node eher händisch erzeugt
var
lParty: IXMLPARTYType; begin lParty := FXMLOrder.ORDER_HEADER.ORDER_INFO.PARTIES.Add; lParty.ADDRESS.NAME2 := AName2; end;
Delphi-Quellcode:
Beim Einlesen eines XML in die ereugten Objecte gibt es keine Probleme, aber falls man ein komplett neues XML-Dokument in diesem Standard erzeugen will muss man diese Umwege scheinbar gehen. Ist nicht so schlimm, muss man nur wissen ;)
var
lParty: IXMLPARTYType; lNode: IXMLNode; begin lParty := FXMLOrder.ORDER_HEADER.ORDER_INFO.PARTIES.Add; lNode := lParty.PARTY_ID; lNode.Attributes['type'] := ATTR_SUPPLIER_SPECIFIC; lNode.Text := ASupplierId; end; Ich hoffe es hilft, zu dem Thema auch eine abschliessende Lösung zu haben... |
AW: XML zum Verzweifeln
Zitat:
![]() Dieses kleine Tool unterstützt Namespaces im vollen Umfang, beim Erstellen und auch beim Lesen von XML Dateien. |
AW: XML zum Verzweifeln
Ja, hatte ich, brachte bei mir aber einen Fehler und hatte es zur Seite gelegt. Dann hab ich später eine andere XML Datei gehabt, da ging der Standard Delphi Wizard, hab aber das von Dir gezeigt Tool nicht mehr probiert. Asche auf mein Haupt. Ich teste es nochmal. Danke für den Hinweis
|
AW: XML zum Verzweifeln
Ich bin auch gerade schwer am Kämpfen eine ZugFerd Rechnung (CrossIndustryInvoice) nach einer komplexen XSD Datei (enthält auch @import] mit Delphi zu erzeugen.
Zuerst sah der XML data binding wizard ganz gut aus. Bis ich dann in der erzeugten XLS Datei (bzw. ein Validator) entdeckte, das die Namespaces überhaupt nicht erzeugt wurden. Ich habe viel ausprobiert und auch versucht die XML binding *.pas Datei zu patchen. Aber das war aussichtslos. Okay, dann bin hier über diesen Thread gestolpert und mache die ersten Versuche mit dem x2xmldatabinding. Bei zwei Dingen brauche ich Hilfe: 1. Wie initialisiert man diese Klasse? Bei der Embarcadero Klasse geht das mittels
Delphi-Quellcode:
Aber wie geht das in x2xmldatabinding?
Zug:=NewCrossIndustryInvoice;
2. manche Elemente müsse im Embarcadero data bindung per .add hinzugefügt werden, bevor dem Knoten Werte zugewiesen werden können:
Delphi-Quellcode:
Diese Add-Funktion fehlt im x2xmldatabinding. Wie läuft das hier? über Addchild (mit welchen Parametern)?
Zug
.SupplyChainTradeTransaction .ApplicableHeaderTradeSettlement .ApplicableTradeTax .add; Gibt es ein Demo-Programm, wo man die Benutzung des x2xmldatabinding studieren kann? |
AW: XML zum Verzweifeln
Ich habe gerade versucht in x2xmldatabinding das camt.053 Format über die XSD einzulesen, aber die generierten Objekte waren leider nicht brauchbar. Es gab Verweise auf Interfaces, welche es nicht gab.
Da kann ich dir also nicht mit Tipps helfen. Dazu habe ich über das Delphi XML Databindung die XSD importiert, da gab es ein "interface not supported" aber ich konnte es lösen, da stand ein falscher Rootnodenname des XML in der Load-Methode (1. Param in TXMLDocument.GetDocBinding). |
Alle Zeitangaben in WEZ +1. Es ist jetzt 06:52 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz