Da es mich schon oft genervt hat, mit den PB-Befehlen durch den XML-Baum zu hüpfen
und damit eine XML-Datei auszulesen, hab ich mir jetzt mal eine Prozedur geschrieben,
die mit einem rutsch aus dem XML-Baum eine "Baum-Struktur-Variable" erzeugt.
Also eine Variable mit Listen und Maps zum direkten ansprechen von Knoten,
Attributen und geraden Inhalte in allen Typen.
Vorteile:
- Knoten über Map-Keys ansprechen
- Knoten mit Listenbefehlen (ForEach) durchgehen
- Attribute über Map-Keys ansprechen
- Attribute mit Listenbefehlen (ForEach) durchgehen
- Attribute liegen in Integer, Float und String vor
Aufgrund der Schachtelung von Maps und Listen und dem mehrfachen Einlesen von Daten
benötig die Variable, in die der Baum importiert wird, relativ viel Speicher.
Es ist keines Falls eine effiziente Lösung, aber eine einfache in der Nutzung.
Die Variable sollte nur als "Lesehilfe" genutzt werden, und nicht zum endgültigen Speichern der Daten.
Nutzung:
Einfach eine Variable mit der Struktur XML definieren und eine XML-Datei importieren.
Danach kann durch die Struktur aus Maps und Listen der Baum gelesen werden.
Quellcode mit Beispiel:
Code: Alles auswählen
;- Code
; Importiertypen
Enumeration
#ImportXML_File
#ImportXML_String
EndEnumeration
; Attribut-Name und Wert als Integer, Float und String
Structure XMLAttribute
Name.s
Integer.i
Float.f
StructureUnion
Value.s
String.s
EndStructureUnion
EndStructure
; Knoten-Name, -Text und -Typ, sowie alle Unterknoten und Attribute
Structure XMLNode
Name.s
Text.s
Type.i
List *Attributes.XMLAttribute()
Map Attribute.XMLAttribute(8)
List *Nodes.XMLNode()
Map Node.XMLNode(16)
EndStructure
; XML-Struktur mit Hauptknoten und Pfadzugriff
Structure XML
Main.XMLNode
Map *Path.XMLNode()
EndStructure
; Intern: Importieren eines XML-Knotens
Procedure ImportXML_Node(*XML.XML, *XMLNode.XMLNode, *Node)
Protected *Child
With *XMLNode
\Type = XMLNodeType(*Node)
\Name = GetXMLNodeName(*Node)
\Text = GetXMLNodeText(*Node)
AddMapElement(*XML\Path(), XMLNodePath(*Node))
*XML\Path() = *XMLNode
If ExamineXMLAttributes(*Node)
While NextXMLAttribute(*Node)
AddMapElement(\Attribute(), XMLAttributeName(*Node))
AddElement(\Attributes())
\Attributes() = @\Attribute()
\Attribute()\Name = XMLAttributeName(*Node)
\Attribute()\String = XMLAttributeValue(*Node)
\Attribute()\Integer = Val(XMLAttributeValue(*Node))
\Attribute()\Float = ValF(XMLAttributeValue(*Node))
Wend
EndIf
*Child = ChildXMLNode(*Node)
While *Child
If XMLNodeType(*Child) = #PB_XML_Normal
AddMapElement(\Node(), GetXMLNodeName(*Child), #PB_Map_NoElementCheck)
AddElement(\Nodes())
\Nodes() = @\Node()
ImportXML_Node(*XML, \Node(), *Child)
EndIf
*Child = NextXMLNode(*Child)
Wend
EndWith
EndProcedure
; Prozedur zum importieren einer XML-Quelle (Dateiname oder Zeichenkette) als Variable mit Struktur
Procedure ImportXML(*XML.XML, Source.s, Type.i=#ImportXML_File)
Protected XML.i, Result.i
Select Type
Case #ImportXML_File
XML = LoadXML(#PB_Any, Source)
Case #ImportXML_String
XML = CatchXML(#PB_Any, @Source, StringByteLength(Source))
EndSelect
If XML
If XMLStatus(XML) = #PB_XML_Success
ImportXML_Node(*XML, *XML\Main, MainXMLNode(XML))
Result = #True
EndIf
FreeXML(XML)
EndIf
ProcedureReturn Result
EndProcedure
;- Beispiel
Enumeration
#File
EndEnumeration
InitNetwork()
ReceiveHTTPFile("http://data.unionbytes.de/Example.xml", GetTemporaryDirectory()+"Example.xml")
If ReadFile(#File, GetTemporaryDirectory()+"Example.xml")
While Not Eof(#File)
Debug ReadString(#File, #PB_UTF8)
Wend
CloseFile(#File)
Debug ""
EndIf
Define Example.XML
If ImportXML(@Example, GetTemporaryDirectory()+"Example.xml")
Debug ":: Attribut eines Knoten auslesen"
Debug Example\Main\Node("Head")\Attribute("Name")\Value
Debug ":: Attributnamen auflisten"
ForEach Example\Main\Node("Head")\Attributes()
Debug Example\Main\Node("Head")\Attributes()\Name
Next
Debug ":: Knoten auflisten und Inhalt lesen"
ForEach Example\Main\Node("Data")\Nodes()
Debug Example\Main\Node("Data")\Nodes()\Node("Name")\Text
Next
Debug ":: Direkter Pfadzugriff und Attributwert als Integer und Float"
Debug Example\Path("/Order/Data/Item[2]")\Attribute("Number")\Integer
Debug Example\Path("/Order/Data/Item[2]")\Attribute("Price")\Float
EndIf
Update 2: Es kan nun auch ein XML-String importiert werden (durch angabe des Import-Typs)