XML Attribut Probleme

romin

Mitglied
Hi,
nur um schonmal Beschwerden vorzubeugen: Ich weiß das der Title sehr wenig aussagt, aber ich wusste nicht wie ich es formulieren sollte.
Also ich habe folgendes Problem:
Ich möchte eine XML-Datei mit VB2005 auslesen, ich benutze dafür den Verweis auf Microsoft XML v6.0 und so halbwegs funktioniert es auch wie ich will. Das Problem ist das die XML-Datei die ich auslesen will "vollkommen" durcheinander. Ich möchte nun folgendes auslesen:
Code:
<User>
	<Userkennung1>
		<count>2</count>
		<Anmelung nummer="008">
			<Kennung>123456</Kennung>
		</Anmelung>´

		<Anmelung nummer="016">
			<Kennung>034568</Kennung>
		</Anmelung>
	</Userkennung1>

	<Userkennung2>
		<count>9</count>

		<Anmelung nummer="001">
			<Kennung>401647</Kennung>
		</Anmelung>

		<Anmelung nummer="003">
			<Kennung>214865</Kennung>
		</Anmelung>

		<Anmelung nummer="004">
			<Kennung>300145</Kennung>
		</Anmelung>

		<Anmelung nummer="005">
			<Kennung>801637</Kennung>
		</Anmelung>

		<Anmelung nummer="006">
			<Kennung>934578</Kennung>
		</Anmelung>

		<Anmelung nummer"007">
			<Kennung>146527</Kennung>
		</Anmelung>

		<Anmelung nummer="009">
			<Kennung>324197</Kennung>
		</Anmelung>

		<Anmelung nummer="010">
			<Kennung>497241</Kennung>
		</Anmelung>

		<Anmelung nummer="013">
			<Kennung>670841</Kennung>
		</Anmelung>
	</Userkennung2>
</User>
Ich kann nichts dafür das diese XML Datein so aufgebaut sind, aber ich will sie halt aus lesen und zwar in der Reihenfolge der Anmeldungsnummer. Dafür gehe ich bisher <Userkennung1> und <Userkennung2> jeweils mit einer For each Schleife durch. Die erhaltenen Daten werden momentan in der Reihenfolge in einen Array geschrieben in der sie ausgelesen wurden. Um das zu ändern würde ich gerne die "Nummer" der Anmeldung auslesen. Ich habe jetzt mittlerweile alle möglichen funktionen ausprobiert die mit dem Knoten möglich sind aber die Nummer habe ich nie bekommen
Kann mir jemand helfen?
Gruß romin
 
Mal davon abgesehen, dass dein XML nicht validiert, hier das bereinigte XML als Grundlage:
Code:
<?xml version="1.0" encoding="utf-8" ?> 
<User>
	<Userkennung1>
		<count>2</count>
		<Anmelung nummer="008">
			<Kennung>123456</Kennung>
		</Anmelung>

		<Anmelung nummer="016">
			<Kennung>034568</Kennung>
		</Anmelung>
	</Userkennung1>

	<Userkennung2>
		<count>9</count>

		<Anmelung nummer="001">
			<Kennung>401647</Kennung>
		</Anmelung>

		<Anmelung nummer="003">
			<Kennung>214865</Kennung>
		</Anmelung>

		<Anmelung nummer="004">
			<Kennung>300145</Kennung>
		</Anmelung>

		<Anmelung nummer="005">
			<Kennung>801637</Kennung>
		</Anmelung>

		<Anmelung nummer="006">
			<Kennung>934578</Kennung>
		</Anmelung>

		<Anmelung nummer="007">
			<Kennung>146527</Kennung>
		</Anmelung>

		<Anmelung nummer="009">
			<Kennung>324197</Kennung>
		</Anmelung>

		<Anmelung nummer="010">
			<Kennung>497241</Kennung>
		</Anmelung>

		<Anmelung nummer="013">
			<Kennung>670841</Kennung>
		</Anmelung>
	</Userkennung2>
</User>
Gut, dann gibt es zwei Klassen. Eine Klasse Anmeldung und eine AnmeldungList. Die erste Klasse hält Informationen über Nummer und Kennung. Die zweite ist lediglich eine Liste. Zu Anmeldung selbst ist zu sagen, dass das IComparable-Interface implementiert wird (CompareTo), damit eine Sortierung stattfinden kann (das brauchst du ja für die Sortierung nach Nummer). Hier einmal die beiden Klassen:

Anmeldung
Code:
using System;
using System.Collections.Generic;
using System.Text;

namespace XmlTestConsole
{
    public class Anmeldung : IComparable<Anmeldung>
    {
        private int _nummer;
        private string _kennung;

        public string Kennung
        {
            get { return _kennung; }
            set { _kennung = value; }
        }
	
        public int Nummer
        {
            get { return _nummer; }
            set { _nummer = value; }
        }

        public override string ToString()
        {
            return String.Format("{0:000} - {1}", _nummer, _kennung);
        }

        #region IComparable<Anmeldung> Members

        public int CompareTo(Anmeldung other)
        {
            return this._nummer.CompareTo(other._nummer);
        }

        #endregion
    }
}

AnmeldungList
Code:
using System;
using System.Collections.Generic;
using System.Text;

namespace XmlTestConsole
{
    public class AnmeldungList : List<Anmeldung>
    {
    }
}
So, damit hätten wir den Grundstock. Hab noch eine kleine Demo in Form einer Consolen-Anwendung, die die Informationen aus dem XML (es nennt sich bei mir data.xml) auswertet, die entsprechenden Anmeldung-Objekte erstellt, der Liste hinzufügt, anschließend die Liste sortiert und schlußendlich über die Konsole ausgibt:

Code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

namespace XmlTestConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            AnmeldungList aList = new AnmeldungList();

            XmlDocument doc = new XmlDocument();
            doc.Load("data.xml");

            XmlNodeList xnlAnmeldungen1 = doc.SelectNodes("/User/Userkennung1/Anmelung");
            foreach (XmlNode xnAnmeldung in xnlAnmeldungen1)
            {
                Anmeldung a = new Anmeldung();
                int nr = 0;
                Int32.TryParse(xnAnmeldung.Attributes["nummer"].InnerText, out nr);
                a.Nummer = nr;
                XmlNode xnChild = xnAnmeldung.SelectSingleNode("Kennung");
                if (xnChild != null)
                    a.Kennung = xnChild.InnerText;

                aList.Add(a);
            }

            Console.WriteLine("--------------");

            XmlNodeList xnlAnmeldungen2 = doc.SelectNodes("/User/Userkennung2/Anmelung");
            foreach (XmlNode xnAnmeldung in xnlAnmeldungen2)
            {
                Anmeldung a = new Anmeldung();
                int nr = 0;
                Int32.TryParse(xnAnmeldung.Attributes["nummer"].InnerText, out nr);
                a.Nummer = nr;
                XmlNode xnChild = xnAnmeldung.SelectSingleNode("Kennung");
                if (xnChild != null)
                    a.Kennung = xnChild.InnerText;

                aList.Add(a);
            }

            aList.Sort();

            foreach (Anmeldung a in aList)
                Console.WriteLine(a.ToString());

            Console.WriteLine("--------------");

            Console.Read();
        }
    }
}
Das sollte nun so funktionieren.
 
Und natürlich hab ich überlesen, dass du das in VB.NET brauchst. Hier:

Anmeldung
Code:
Public Class Anmeldung
    Implements IComparable(Of Anmeldung)
    ' Methods
    Public Function CompareTo(ByVal other As Anmeldung) As Integer
        Return Me._nummer.CompareTo(other._nummer)
    End Function

    Public Overrides Function ToString() As String
        Return String.Format("{0:000} - {1}", Me._nummer, Me._kennung)
    End Function


    ' Properties
    Public Property Kennung As String
        Get
            Return Me._kennung
        End Get
        Set(ByVal value As String)
            Me._kennung = value
        End Set
    End Property

    Public Property Nummer As Integer
        Get
            Return Me._nummer
        End Get
        Set(ByVal value As Integer)
            Me._nummer = value
        End Set
    End Property


    ' Fields
    Private _kennung As String
    Private _nummer As Integer
End Class

AnmeldungList
Code:
Public Class AnmeldungList
    Inherits List(Of Anmeldung)
End Class

Und nun die Konsolen-Anwendung:
Code:
Friend Class Program
    ' Methods
    Private Shared Sub Main(ByVal args As String())
        Dim aList As New AnmeldungList
        Dim doc As New XmlDocument
        doc.Load("data.xml")
        Dim xnAnmeldung As XmlNode
        For Each xnAnmeldung In doc.SelectNodes("/User/Userkennung1/Anmelung")
            Dim a As New Anmeldung
            Dim nr As Integer = 0
            Integer.TryParse(xnAnmeldung.Attributes.ItemOf("nummer").InnerText, nr)
            a.Nummer = nr
            Dim xnChild As XmlNode = xnAnmeldung.SelectSingleNode("Kennung")
            If (Not xnChild Is Nothing) Then
                a.Kennung = xnChild.InnerText
            End If
            aList.Add(a)
        Next
        Console.WriteLine("--------------")
        Dim xnAnmeldung As XmlNode
        For Each xnAnmeldung In doc.SelectNodes("/User/Userkennung2/Anmelung")
            Dim a As New Anmeldung
            Dim nr As Integer = 0
            Integer.TryParse(xnAnmeldung.Attributes.ItemOf("nummer").InnerText, nr)
            a.Nummer = nr
            Dim xnChild As XmlNode = xnAnmeldung.SelectSingleNode("Kennung")
            If (Not xnChild Is Nothing) Then
                a.Kennung = xnChild.InnerText
            End If
            aList.Add(a)
        Next
        aList.Sort
        Dim a As Anmeldung
        For Each a In aList
            Console.WriteLine(a.ToString)
        Next
        Console.WriteLine("--------------")
        Console.Read
    End Sub

End Class
Die notwendigen Namespaces bitte der vorigen Post entnehmen.
 
Hi,
danke für die schnell und vorallem sehr ausführliche Antwort. Die XML-Datei die ich oben gepostet habe ist nur ein Ausschnitt und von daher natürlich nicht vollständig.
Irgendwie sieht der Programmcode den du gepostet hast sehr umfangreich aus und ich glaube er gibt wesentlich mehr als ich bräuchte. Mein Programm ist momentan so aufgebaut, dass jeder XML-Knoten ein eigenes Sub/ eine eigene Function bekommt. so habe ich ein Sub für User und eines für Userkennung1 und eine für Anmeldung und eine function die mir den Wert der Kennung ausgibt. Das sieht dann ungefähr so aus:

Code:
Sub User (ByVal pfad as string, ByVal 1DimA as Integer, ByVal 2DimA as Integer)
  Dim AktUsePar As String
  Dim a As MSXML2.IXMLDOMNode
  For Each a In XMLObj.documentElement.selectSingleNode(pfad & "/User").childNodes
    AktUsePar = a.nodeName
    AnmeldungR (pfad & "/User/" & AktUsePar, 1DimA, 2DimA)
  Next
End Sub

Sub AnmeldungR (ByVal pfad as string, ByVal 1DimA as Integer, ByVal 2DimA as Integer)
  Dim a As MSXML2.IXMLDOMNode
  Dim i As Byte
  i = 0
  For Each a In XMLObj.documentElement.selectSingleNode(pfad).childNodes
    If Not a.nodeName = "count" Then
      i = 'HIER FEHLT DIE ANMELDUNGSNUMMER
      XMLS(1DimA, 2DimA, i, 0) = Kennung(pfad & "/" & a.nodeName)
    End If
  Next
End Sub

Private Function Kennung(ByVal pfad As String) As String
  Kennung = XMLObj.documentElement.selectSingleNode(pfad & "/Kennung").text
End Function

XMLObj ist wie man vermuten kann die eingelesene XML-Datei
XMLS ist der 4Dimensionale Array in den ich die XML-Datei einlese
pfad ist der bisher verfolgte Knotenpfad und wird dann in jedem weiteren Sub vergrößert
An sich funktioniert auch alles (ich habe vieles auch mit Try-Catch-End Try ausgestattet. Und es wird auch wirklich alles ausgelesen bis auf die Anmeldung, wofür mir nur noch die 3. Array Dimension fehlt
So ich hoffe ich konnte mein Problem etwas besser darstellen, und wenn ich den obigen Programmcode vollkommen falsch verstanden habe oder mein eigener total für den Mülleimer ist sagt es mir bitte^^
Gruß romin
 
Grundsätzlich würde ich nicht mit Microsoft XML v6.0 arbeiten. Für die XML-Verarbeitung stellt das .NET Framework entsprechende Klassen zur Verfügung, die ohnehin auch darauf zugreifen.

Weiters bevorzuge ich selbst den objektorientierten Ansatz und weniger den prozeduralen. Zusätzlich würde ich meine Lösung als zukunftssicherer betrachten, wegen Erweiterbarkeit etc. Und schlußendlich verwende ich lieber Listen und Data-Objekte als Arrays.

Von dem her würde ich dir eher raten, dir meinen Ansatz genauer anzusehen.
 
hi,
ok dann werde ich mich wohl eher durch deinen Vorschlag durcharbeiten, oder es zumindest versuchen^^. Gäbe es denn ne Möglichkeit meine Idee umzusetzten?
Gruß romin
 
Sicherlich, aber warum auf externe Ressourcen zugreifen, wenn diese bereits im .NET Framework vorhanden sind? Ich sehe den Bedarf nur wenn Funktionalität vorhanden ist, die so nicht geboten wird bzw. wenn eklatante Performance-Unterschiede bestehen. In diesem Fall trifft wohl beides nicht zu.
 
Hi,
also die Performance spielt keine Große Rolle dabei. Ob ich jetzt eine halbe oder eine Sekunde zum Aufrufen der Daten braucht ist erstmal recht egal, da die Daten 1. nicht sehr groß sind und 2. nicht so häufig aufgerufen werden. Und der Vorteil an meiner "Lösung" ist ja das Sie schon fertig im Programm ist und nur dieser eine blöde Befehl nicht funktioniert^^. Ich würde mich ja noch um eine bessere Lösung des Problems kümmern aber momtan wollte ich nur das Programm fertig bekommen
Gruß romin
 
Ok. Das heißt du kommst an die Nummer nicht dran?

Da musst du dir die "Anmelung" (sollte vielleicht auf Anmeldung geändert werden?) Nodes abfragen. Bei diesen Nodes gibt es dann als Attribut den Key "nummer", denn du abfragen musst.
 
Hi,
könntest du mir vllt sagen welche Eigenschaft du genau meinst? Ich habe die For each- Schleife jetzt leicht verändert
Code:
For each a in XMLObj.documentElement.selectSingleNode(pfad).attributes
welche Eigenschaft von a gibt mir jetzt den Key an?
Gruß romin
 

Neue Beiträge

Zurück