ImportXML - XML als Struktur-Variable laden und lesen

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7031
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

ImportXML - XML als Struktur-Variable laden und lesen

Beitrag von STARGÅTE »

Vorwort:
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
Nachteil (Warnung):
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: Beispiel mit Zwischenspeichern der Adresse eines Knotens

Update 2: Es kan nun auch ein XML-String importiert werden (durch angabe des Import-Typs)
Zuletzt geändert von STARGÅTE am 30.04.2012 00:01, insgesamt 4-mal geändert.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: ImportXML - XML als Struktur-Variable laden und nutzen

Beitrag von ts-soft »

:allright:
Wenn ich die XML nur Lese ist das bestimmt eine große Erleichterung :D

Wenn ich die Werte ändern und speichern muss, dann bringt mir dies leider
nichts :(

Gruß
Thomas
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
RSBasic
Admin
Beiträge: 8047
Registriert: 05.10.2006 18:55
Wohnort: Gernsbach
Kontaktdaten:

Re: ImportXML - XML als Struktur-Variable laden und lesen

Beitrag von RSBasic »

Danke fürs Teilen. :allright:
Aus privaten Gründen habe ich leider nicht mehr so viel Zeit wie früher. Bitte habt Verständnis dafür.
Bild
Bild
Antworten