20. februar 2011 - 17:07
Der er
43 kommentarer og 1 løsning
Hente væerdier vha, LINQ2XML
Jeg har følgende xml struktur (kunne være i bedre kvalitet, men har desværre kun dette at arbejde med): Normalt kan jeg klare mig med '//NAVN' i almindelig XPATH (med klarhed over man skal passe på performance med '//' notationen). Dog prøver jeg at skrive noget LINQ til xml, der kan give mig alle navnene i Tabel1 strukturen. Et Linq udtryk, der kan give mig alle navnene ville også være dejligt at få. <ROOT> <FOO> <TABEL1> <TABELELEMENT> <NAVN>MitNavn1</NAVN> <VAERDI>2,530</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn2</NAVN> <VAERDI>2,5430</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn3</NAVN> <VAERDI>2,540</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn4</NAVN> <VAERDI>2,550</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn5</NAVN> <VAERDI>2,504</VAERDI> </TABELELEMENT> </TABEL1> <TABEL2> <TABELELEMENT> <NAVN>MitNavn6</NAVN> <VAERDI>2,450</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn7</NAVN> <VAERDI>2,550</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn8</NAVN> <VAERDI>2,5503</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn9</NAVN> <VAERDI>2,505</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn10</NAVN> <VAERDI>2,540</VAERDI> </TABELELEMENT> </TABEL2> </FOO> </ROOT> Er der en der er skarp på LINQ2XML?
Annonceindlæg fra Computerworld
20. februar 2011 - 17:19
#1
Demo: using System; using System.Linq; using System.Xml.Linq; namespace E { public class Program { public static void Main(string[] args) { string s = @"<ROOT> <FOO> <TABEL1> <TABELELEMENT> <NAVN>MitNavn1</NAVN> <VAERDI>2,530</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn2</NAVN> <VAERDI>2,5430</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn3</NAVN> <VAERDI>2,540</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn4</NAVN> <VAERDI>2,550</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn5</NAVN> <VAERDI>2,504</VAERDI> </TABELELEMENT> </TABEL1> <TABEL2> <TABELELEMENT> <NAVN>MitNavn6</NAVN> <VAERDI>2,450</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn7</NAVN> <VAERDI>2,550</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn8</NAVN> <VAERDI>2,5503</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn9</NAVN> <VAERDI>2,505</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn10</NAVN> <VAERDI>2,540</VAERDI> </TABELELEMENT> </TABEL2> </FOO> </ROOT>"; XDocument doc = XDocument.Parse(s); foreach(XElement elm in doc.Root.Element("FOO").Elements().Elements("TABELELEMENT").Elements("NAVN")) { Console.WriteLine(elm.Value); } Console.ReadKey(); } } }
20. februar 2011 - 17:22
#2
Hmmm ok så man kan altså ikke skrive et Linq udtryk, der giver en collection tilbage..eller antallet af noder... Prøvede faktisk at undgå at bruge en løkke...
20. februar 2011 - 17:26
#3
Ville jo prøve at lege med mulighederne for LINQ2XMl :)
20. februar 2011 - 17:30
#4
var res = from v in _xDoc.Elements("ROOT") select v.Elements("FOO").Elements("TABEL1").Elements("TABELELEMENT").Elements("NAVN"); Denne gav mig kun det første navn...hvor jeg gerne ville have alle navnene... den sidste betingelse i mit udtryk kan vel skrive om, så det bliver alle navnene eller noderne inden for TABEL1 jeg får ud...
20. februar 2011 - 17:36
#5
I virkeligheden har jeg behov for at Linq udtryk, der giver mig alle navnene noget á la //NAVN i xpath.
20. februar 2011 - 17:46
#6
Faktisk giver denne mig alle navnene: var res = from v in _xDoc.Elements("ROOT") select v.Elements("FOO").Elements("TABEL1").Elements("TABELELEMENT").Elements("NAVN"); Men jeg får "<NAVN>MineNavne</NAVN>" med ud...nu skal jeg sådan set bare have tekst værdien ud :S
20. februar 2011 - 18:04
#7
Du skal nok have fat i .Value Dvs: var res = from v in _xDoc.Elements("ROOT") select v.Elements("FOO").Elements("TABEL1").Elements("TABELELEMENT").Elements("NAVN").Select(a => a.Value);
20. februar 2011 - 18:07
#8
Når du bruger .Elements fremfor .Element får du en enumerable (og det er sådan en slags list). Og det er vel det samme som XPath med //NAVN gør. Hvis du så vil hente værdierne kan du hente .Value ligesom du i XPath kan hente text().
20. februar 2011 - 18:11
#9
Fandt value og fik det til at virke...
20. februar 2011 - 18:17
#10
Jeg deler points til begge, da I var med til at give mig en retningslinje...selvom jeg var tæt på og fandt svaret selv :P Jeg deler points...skylder vist også buzzzy fra før...
20. februar 2011 - 18:18
#11
Kan jeg ikke lave det til et array i samme udtryk? .ToArray?? Hvordan?
20. februar 2011 - 18:21
#12
Både enumerable af XElement og String kan omformes til arrays med .ToArray()
20. februar 2011 - 18:22
#13
Ja, ToArray() i enden ... så får du en string[] tilbage. Point til arne_v, jeg har ikke rigtig bedraget med noget specielt. mvh
20. februar 2011 - 18:23
#14
Jeps fik det også til at virke...det eneste jeg synes, der er lidt grimt er det her: var n = res.ElementAt(0).ToArray();
20. februar 2011 - 18:25
#15
Jeg får nemlig en collection med et element, der indeholder en collection af mine værdier...det ville være fedt at komme udenom ElementAt(0)delen.
20. februar 2011 - 19:03
#16
Hvordan beregnes res før: var n = res.ElementAt(0).ToArray(); ?
20. februar 2011 - 19:09
#17
var res = from v in _xDoc.Elements("ROOT") select v.Elements("FOO").Elements("TABEL1").Elements("TABELELEMENT").Elements("NAVN").Select(a => a.Value).ToArray(); var n = res.ElementAt(0).ToArray();
20. februar 2011 - 19:17
#18
for mange .ToArray() - prøv: var res = from v in _xDoc.Elements("ROOT") select v.Elements("FOO").Elements("TABEL1").Elements("TABELELEMENT").Elements("NAVN").Select(a => a.Value); var n = res.ToArray();
20. februar 2011 - 19:18
#19
Hvis jeg kan fange VAERDI i samme omgang fra xml'en, så jeg får begge værdier...så vil jeg også blive meget glad.
20. februar 2011 - 19:20
#20
Den havde jeg før...men jeg fik ikke mine værdier ud i min n. så ligger værdierne i n[0].
20. februar 2011 - 19:47
#21
IEnumerable<XElement> res = doc.Root.Element("FOO").Elements().Elements("TABELELEMENT"); var res2 = res.Select(elm => new { Navn=elm.Element("NAVN").Value, Vaerdi=elm.Element("VAERDI").Value}); foreach(var pair in res2) { Console.WriteLine(pair.Navn + " = " + pair.Vaerdi); }
20. februar 2011 - 19:52
#22
Mange tak...smid et bare et svar ind.
20. februar 2011 - 19:57
#23
svar
20. februar 2011 - 20:27
#24
Jeg kan se at "TABEL1" og "TABEL2" kan forvolde mig problemer...der er ikke mulighed for at springe det element over? Jeg er sådan set bare interesseret i værdierne i mellem, og vil helst være fri for at smide en parameter med over, der hedder 'TABEL1' eller 'TABEL2'. Kan det lade sig gøre...siger jeg, mens jeg accepterer svaret...og håber på et svar fra de kloge(dvs. Arne). :)
20. februar 2011 - 20:35
#25
Buzz er naturligvis også en klog mand...
20. februar 2011 - 20:44
#26
Jeg forstår ikke spørgsmålet. .Element("FOO").Elements().Elements("TABELELEMENT") det at der ikke er noget angivet i den midterste .Elements gør at den tager både TABLE1 og TABLE2.
20. februar 2011 - 20:50
#27
ahhh ok...det kan jeg lige prøve så...du har alligevel forstået mit spørgsmål :) Det er bare mig der ikke har briller på...jeg så ikke den tomme element() Skrev selv koden i mit eksempel...og det blev med TABEL1. Tak for hjælpen.
20. februar 2011 - 20:57
#28
I virkeligheden er XPath og LINQ to XML meget ens. doc.SelectNodes("/a/b/c") doc.Elements("a").Elements("b").Elements("c") etc.
20. februar 2011 - 21:13
#29
Nogen bestemt grundt til du har angivet en doc.root? Jeg får null reference exception med et tomt element().
20. februar 2011 - 21:18
#30
Jeg tror ike at .Root er nødvendig - den røg bare på i farten. .Elements() giver mening - jeg ved ikke om .Element() virker.
20. februar 2011 - 21:23
#31
Sorry det var et tomt Elements(), jeg mente...irriterer mig lidt, da det virker lige så snart jeg knalder "TABEL1" eller "TABEL2" ind...
20. februar 2011 - 21:25
#32
Hvordan ser dit udtryk ud nu?
20. februar 2011 - 21:29
#33
var res = _xDoc.Root.Element("FOO").Elements().Elements("TABELELEMENT"); var res2 = res.Select(element => new { Navn = element.Element("NAVN").Value, Vaerdi = element.Element("VAERDI").Value}).ToArray(); I det øjeblik jeg sætter "TABEL1" i det tomme Elements(), så får jeg svar tilbage..men får et null reference exception nu.
20. februar 2011 - 21:43
#34
Det lyder meget mystisk. Hvad sjer der hvis du starter et nyt projekt og kører: using System; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; namespace E { public class Program { public static void Main(string[] args) { string s = @"<ROOT> <FOO> <TABEL1> <TABELELEMENT> <NAVN>MitNavn1</NAVN> <VAERDI>2,530</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn2</NAVN> <VAERDI>2,5430</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn3</NAVN> <VAERDI>2,540</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn4</NAVN> <VAERDI>2,550</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn5</NAVN> <VAERDI>2,504</VAERDI> </TABELELEMENT> </TABEL1> <TABEL2> <TABELELEMENT> <NAVN>MitNavn6</NAVN> <VAERDI>2,450</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn7</NAVN> <VAERDI>2,550</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn8</NAVN> <VAERDI>2,5503</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn9</NAVN> <VAERDI>2,505</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>MitNavn10</NAVN> <VAERDI>2,540</VAERDI> </TABELELEMENT> </TABEL2> </FOO> </ROOT>"; XDocument doc = XDocument.Parse(s); foreach(XElement elm in doc.Root.Element("FOO").Elements().Elements("TABELELEMENT").Elements("NAVN")) { Console.WriteLine(elm.Value); } IEnumerable<XElement> res = doc.Root.Element("FOO").Elements().Elements("TABELELEMENT"); var res2 = res.Select(elm => new { Navn=elm.Element("NAVN").Value, Vaerdi=elm.Element("VAERDI").Value}); foreach(var pair in res2) { Console.WriteLine(pair.Navn + " = " + pair.Vaerdi); } Console.ReadKey(); } } }
20. februar 2011 - 21:51
#35
Så virker det fint...kan det have noget at gøre med den måde man loader xml ind på? Jeg henter det fra en fil på min disk....
20. februar 2011 - 21:58
#36
Nu parser jeg den som dig...
20. februar 2011 - 22:28
#37
Det burde ikke gøre en forskel. Er der nogen forskel i XML'en?
20. februar 2011 - 22:29
#38
Jeg har fundet ud af problemet...men ved ikke, hvordan jeg kan løse det. Min XML ser i virkeligheden således ud: Prøv at køre dit program over det...så kan du se problemet. <ROOT> <FOO> <TABEL1> <TABELELEMENT> <NAVN>500</NAVN> <VAERDI>250</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>10000</NAVN> <VAERDI>2,00</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>2000</NAVN> <VAERDI>1,7</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>50000</NAVN> <VAERDI>1,0</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>99.999</NAVN> <VAERDI/> </TABELELEMENT> </TABEL1> <TABEL2> <TABELELEMENT> <NAVN_C>50.00</NAVN_C> <VAERDI_C>2,0</VAERDI_C> </TABELELEMENT> <TABELELEMENT> <NAVN_C>100.000</NAVN_C> <VAERDI_C>150</VAERDI_C> </TABELELEMENT> <TABELELEMENT> <NAVN_C>20000</NAVN_C> <VAERDI_C>25</VAERDI_C> </TABELELEMENT> <TABELELEMENT> <NAVN_C>500.000</NAVN_C> <VAERDI_C>1,0</VAERDI_C> </TABELELEMENT> <TABELELEMENT> <NAVN_C>9999</NAVN_C> <VAERDI_C/> </TABELELEMENT> </TABEL2> <TABEL3> <TABELELEMENT> <NAVN_B>999.999</NAVN_B> <VAERDI_B>200</VAERDI_B> </TABELELEMENT> </TABEL3> <ANDETSNASK>Noget der ikke skal bruges</ANDETSNASK> <ANDETSNASK2>Noget der heller ikke skal bruges</ANDETSNASK2> <TABEL4> <TABELELEMENT> <NAVN_A>10000</NAVN_A> <VAERDI_A>2,0</VAERDI_A> </TABELELEMENT> <TABELELEMENT> <NAVN_A>99.999</NAVN_A> <VAERDI_A>100</VAERDI_A> </TABELELEMENT> </TABEL4> </FOO> </ROOT>
20. februar 2011 - 22:34
#39
Så jeg skal bare have "NAVN" og "VAERDI" ud, som befinder sig i TABEL1, men jeg kan faktisk ikke være sikker...det kan komme ud i TABEL2 i visse tilfælde...derfor vil jeg stadigvæk have alle "NAVN" og "VAERDI" ud uanset hvilken TABEL, den befinder sig i. PS! NAVN_A og VAERDI_A er jeg fx. ikke interesseret i. Kun dem uden underscore.
20. februar 2011 - 22:44
#40
Se det er jo en lidt anden XML. :-) Forslag: using System; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; namespace E { public class Program { public static void Main(string[] args) { string s = @"<ROOT> <FOO> <TABEL1> <TABELELEMENT> <NAVN>500</NAVN> <VAERDI>250</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>10000</NAVN> <VAERDI>2,00</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>2000</NAVN> <VAERDI>1,7</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>50000</NAVN> <VAERDI>1,0</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>99.999</NAVN> <VAERDI/> </TABELELEMENT> </TABEL1> <TABEL2> <TABELELEMENT> <NAVN_C>50.00</NAVN_C> <VAERDI_C>2,0</VAERDI_C> </TABELELEMENT> <TABELELEMENT> <NAVN_C>100.000</NAVN_C> <VAERDI_C>150</VAERDI_C> </TABELELEMENT> <TABELELEMENT> <NAVN_C>20000</NAVN_C> <VAERDI_C>25</VAERDI_C> </TABELELEMENT> <TABELELEMENT> <NAVN_C>500.000</NAVN_C> <VAERDI_C>1,0</VAERDI_C> </TABELELEMENT> <TABELELEMENT> <NAVN_C>9999</NAVN_C> <VAERDI_C/> </TABELELEMENT> </TABEL2> <TABEL3> <TABELELEMENT> <NAVN_B>999.999</NAVN_B> <VAERDI_B>200</VAERDI_B> </TABELELEMENT> </TABEL3> <ANDETSNASK>Noget der ikke skal bruges</ANDETSNASK> <ANDETSNASK2>Noget der heller ikke skal bruges</ANDETSNASK2> <TABEL4> <TABELELEMENT> <NAVN_A>10000</NAVN_A> <VAERDI_A>2,0</VAERDI_A> </TABELELEMENT> <TABELELEMENT> <NAVN_A>99.999</NAVN_A> <VAERDI_A>100</VAERDI_A> </TABELELEMENT> </TABEL4> </FOO> </ROOT>"; XDocument doc = XDocument.Parse(s); foreach(XElement elm in doc.Root.Element("FOO").Elements().Where(elm => elm.Name.LocalName.StartsWith("TABEL")).Elements("TABELELEMENT").Elements().Where(elm => elm.Name.LocalName.StartsWith("NAVN"))) { Console.WriteLine(elm.Value); } IEnumerable<XElement> res = doc.Root.Element("FOO").Elements().Where(elm => elm.Name.LocalName.StartsWith("TABEL")).Elements("TABELELEMENT"); var res2 = res.Select(elm => new { Navn=elm.Elements().Where(elm2 => elm2.Name.LocalName.StartsWith("NAVN")).First().Value, Vaerdi=elm.Elements().Where(elm2 => elm2.Name.LocalName.StartsWith("VAERDI")).First().Value}); foreach(var pair in res2) { Console.WriteLine(pair.Navn + " = " + pair.Vaerdi); } Console.ReadKey(); } } }
20. februar 2011 - 22:45
#41
Ah - du vill ikke have NAVN_* med ud. 2 minutter.
20. februar 2011 - 22:49
#42
using System; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; namespace E { public class Program { public static void Main(string[] args) { string s = @"<ROOT> <FOO> <TABEL1> <TABELELEMENT> <NAVN>500</NAVN> <VAERDI>250</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>10000</NAVN> <VAERDI>2,00</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>2000</NAVN> <VAERDI>1,7</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>50000</NAVN> <VAERDI>1,0</VAERDI> </TABELELEMENT> <TABELELEMENT> <NAVN>99.999</NAVN> <VAERDI/> </TABELELEMENT> </TABEL1> <TABEL2> <TABELELEMENT> <NAVN_C>50.00</NAVN_C> <VAERDI_C>2,0</VAERDI_C> </TABELELEMENT> <TABELELEMENT> <NAVN_C>100.000</NAVN_C> <VAERDI_C>150</VAERDI_C> </TABELELEMENT> <TABELELEMENT> <NAVN_C>20000</NAVN_C> <VAERDI_C>25</VAERDI_C> </TABELELEMENT> <TABELELEMENT> <NAVN_C>500.000</NAVN_C> <VAERDI_C>1,0</VAERDI_C> </TABELELEMENT> <TABELELEMENT> <NAVN_C>9999</NAVN_C> <VAERDI_C/> </TABELELEMENT> </TABEL2> <TABEL3> <TABELELEMENT> <NAVN_B>999.999</NAVN_B> <VAERDI_B>200</VAERDI_B> </TABELELEMENT> </TABEL3> <ANDETSNASK>Noget der ikke skal bruges</ANDETSNASK> <ANDETSNASK2>Noget der heller ikke skal bruges</ANDETSNASK2> <TABEL4> <TABELELEMENT> <NAVN_A>10000</NAVN_A> <VAERDI_A>2,0</VAERDI_A> </TABELELEMENT> <TABELELEMENT> <NAVN_A>99.999</NAVN_A> <VAERDI_A>100</VAERDI_A> </TABELELEMENT> </TABEL4> </FOO> </ROOT>"; XDocument doc = XDocument.Parse(s); foreach(XElement elm in doc.Root.Element("FOO").Elements().Where(elm => elm.Name.LocalName.StartsWith("TABEL")).Elements("TABELELEMENT").Elements("NAVN")) { Console.WriteLine(elm.Value); } IEnumerable<XElement> res = doc.Root.Element("FOO").Elements().Where(elm => elm.Name.LocalName.StartsWith("TABEL")).Elements("TABELELEMENT").Where(elm => elm.Element("NAVN") != null); var res2 = res.Select(elm => new { Navn=elm.Element("NAVN").Value, Vaerdi=elm.Element("VAERDI").Value}); foreach(var pair in res2) { Console.WriteLine(pair.Navn + " = " + pair.Vaerdi); } Console.ReadKey(); } } }
20. februar 2011 - 22:51
#43
Ja, meget ked af det tog længere tid...jeg vidste ikke at xml strukturen ville give anledning til nogle problemer... Jeg er sikker på dit eksempel virker...men skal nok kigge videre på det imorgen :) Tak for hjælpen
20. februar 2011 - 22:57
#44
Hov...fik det til at virke :D Takker :D
IT-kurser om Microsoft 365, sikkerhed, personlig vækst, udvikling, digital markedsføring, grafisk design, SAP og forretningsanalyse.