Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
Wie kann ich ein bestimmtes Attribut von allen XML Knoten rekursiv auslesen?
J4m3s90
myCSharp.de - Member



Dabei seit:
Beiträge: 17

Themenstarter:

Wie kann ich ein bestimmtes Attribut von allen XML Knoten rekursiv auslesen?

beantworten | zitieren | melden

verwendetes Datenbanksystem: XML

Hallo zusammen,

folgende XML sei gegeben:

<Test>
   <REQUEST1 counter="77"  />
   <REQUEST2 counter="22"  />
   <REQUEST3 counter="33"  />
   <child1> 
		<REQUEST4 counter="44"  />   
   </child1>
</Test>

Ich schaffe es nicht alle Attribute mit dem Namen Counter auszulesen.
Die eingelesene XML Datei ändert sich bei jedem Aufruf, die Verschachtelungstiefe ist Variabel.
Wie schaffe ich es, alle Werte der Attribute auszulesen?
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15510
Herkunft: BW

beantworten | zitieren | melden

- Root finden
- Elemente auslesen
- Elemente der Elemente auslesen (also Rekursiv, am besten über Stack<T>)
- Von jedem Element die Attribute laden und anschauen

Der Einfachheithalber kannst Du die System.Xml.Linq nehmen; hier mit XElement.

XmlDocument in so einem Fall nur, wenn Du wirklich eine sehr große XML Datei hast, die Du gesamt nicht in den Speicher bekommst.

XML gehört zu den besten und umfangreichsten Dokumentationen bei MS Docs.
Zu jeder Klasse gibt es ausführliche Beispiele, die Du Dir anschauen kannst.
private Nachricht | Beiträge des Benutzers
J4m3s90
myCSharp.de - Member



Dabei seit:
Beiträge: 17

Themenstarter:

beantworten | zitieren | melden

Hallo,
vielen Dank für die schnelle Antwort.
Habe das mal probiert so umzusetzen.

   
 public static void Main(string[] args)
        {
            XDocument doc = XDocument.Load("Message.xml");
            IEnumerable<XElement> node = doc.Elements();
            SearchForAttribute(node);
            Console.ReadLine();
        }
       public static void SearchForAttribute(IEnumerable<XElement> Elements)
        {
            foreach (var Element in Elements)
            {
                if(Element.Attribute("counter") != null)
                {
                    Console.WriteLine(Element.Attribute("counter").Value);
                }
                Console.WriteLine(Element.Name);
                if (Element.HasElements)
                {
                    SearchForAttribute(Element.Elements());
                }
            }
        }

Gibt es hier evtl. noch Verbesserungen oder Möglichkeiten Ressourcen zu schonen?
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15510
Herkunft: BW

beantworten | zitieren | melden

Wie gesagt:
Zitat von Abt
also Rekursiv, am besten über Stack<T>
damit löst man das stabiler, weil keine Stackoverflow Exception damit entstehen kann, wenn die Rekursion zu tief wird.

Und ansonsten seh ich den Sinn nicht, wieso Du die Console-Commands mit in die Methode haust. Gib doch die gefundenen Elemente via yield zurück.
Das ist auch vom Code-Aufbau dann super einfach mit dem Stack<T> zusammen.

public static void Main()
{
    XDocument doc = XDocument.Load("Message.xml");

    IEnumerable<XAttribute> attributes = GetCounterAttributes(doc);
    foreach (var foundAttribute in attributes)
    {
        Console.WriteLine(foundAttribute.Value);
    }
}

public static IEnumerable<XAttribute> GetCounterAttributes(XDocument doc)
{
    XElement? root = doc.Root;
    if (root is not null)
    {
        Stack<XElement> queue = new Stack<XElement>(root.Elements());
        while (queue.TryPop(out XElement element))
        {
            foreach (var childElement in element.Elements())
            {
                queue.Push(childElement);
            }

            XAttribute attr = element.Attribute("counter");
            if (attr is not null)
            {
                yield return attr;
            }
        }
    }
}

Oder Du arbeitest mit Descendants (ist gleich im ersten XML Beispiel in der Doc), dann brauchst Du gar keine Rekursion, sondern bekommst direkt alle Elemente.

IEnumerable<XElement> allElements = doc.Root.Descendants();
IEnumerable<XAttribute> allAttributes = allElements.SelectMany(el => el.Attributes());
IEnumerable<XAttribute> counterAttributes = allAttributes.Where(at => at.Name == "counter");
foreach (var foundAttribute in counterAttributes)
{
    Console.WriteLine(foundAttribute.Value);
}
private Nachricht | Beiträge des Benutzers
J4m3s90
myCSharp.de - Member



Dabei seit:
Beiträge: 17

Themenstarter:

beantworten | zitieren | melden

Danke, ich werde mir das gleich anschauen, mit dem Stack hatte ich nicht ganz verstanden in der Doc aber werde es noch mal suchen.
Zitat von Abt
Und ansonsten seh ich den Sinn nicht, wieso Du die Console-Commands mit in die Methode haust.

Die Console-Commands gibt es nachher nicht mehr. Ich schreibe mir nur den Wert des Attributes in eine Variable und arbeite dann außerhalb damit weiter. War nur für mich die Kontrolle ob es funktioniert.
Ich durchsuche die XML Datei nach dem Attribute ( dies entspricht einem Zähler ) sobald ich den habe, kann die Funktion beendet werden. Das Attribute gibt es auch nur ein mal, ich weiß nur nicht wo.
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15510
Herkunft: BW

beantworten | zitieren | melden

Stack vermeidet die Rekursion. Keine Raketentechnik.
Aber wenn Dir nur um da seine geht, gar nicht die Struktur, dann reicht das mit Linq:


IEnumerable<XElement> allElements = doc.Root.Descendants();
IEnumerable<XAttribute> allAttributes = allElements.SelectMany(el => el.Attributes());
List<XAttribute> counterAttributes = allAttributes.Where(at => at.Name == "counter").ToList();

if(counterAttributes.Count == 0)
{
  // keines gefunden
}
else if(counterAttributes.Count == 1)
{
  // eines gefunden
  XAttribute attribute = counterAttributes.Single();
}
else
{
  // hups, doch mehrfach gefunden?
}
private Nachricht | Beiträge des Benutzers
J4m3s90
myCSharp.de - Member



Dabei seit:
Beiträge: 17

Themenstarter:

beantworten | zitieren | melden

Perfekt, danke!
Jetzt habe ich es für zukünftige Projekte verstanden.
Habe leider nichts hilfreiches gefunden. Es wurde nirgends erwähnt, wie ich alle Elemente einer XML suche und dann dort jeweils nach einem Attribute suche.

Danke dir Abt!
private Nachricht | Beiträge des Benutzers