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?
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.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
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?
Wie gesagt:
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);
}
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Danke, ich werde mir das gleich anschauen, mit dem Stack hatte ich nicht ganz verstanden in der Doc aber werde es noch mal suchen.
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.
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?
}
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
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!