[C#/XML] Node selecteren met XPath

Pagina: 1
Acties:

  • whoami
  • Registratie: December 2000
  • Laatst online: 10:11
Een vraagje aan de XML en XPath goeroe's onder de P&W'ers:

Ik heb een programma geschreven die een XML bestand afhaalt. Nu wil ik dit XML bestand gaan inlezen en bepaalde nodes gaan selecteren.

M'n XML bestand ziet er zo uit:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<stock_quotes>
<stock_quote>
  <symbol>AUTN</symbol> 
  <when>
   <date>4/18/2002</date> 
   <time>10:59am</time> 
  </when>
  <price type="ask" value="5" /> 
  <price type="open" value="4.92" /> 
  <price type="dayhigh" value="5" /> 
  <price type="daylow" value="4.92" /> 
  <change>0</change> 
  <volume>500</volume> 
</stock_quote>
...
</stock_quotes>

In m'n C# programma heb ik code geschreven zodat ik een bepaalde node kan selecteren mbhv XPath:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
XPathNavigator nav;
XPathNodeIterator iter;
XPathDocument xdoc = new XPathDocument(".\\quotes.xml");
        
nav = xdoc.CreateNavigator();
        
for(int i = 0; i < StockView.Items.Count; i++)
{
    iter = nav.Select("/stock_quotes/stock_quote[symbol='AUTN']"); 
    while(iter.MoveNext())
    { 
        MessageBox.Show(iter.Current.Value);
    }
}

Dit lukt; de node met symbol='AUTN' wordt geselecteerd. Maar dit lukt maar gedeeltelijk, want in m'n MessageBox krijg ik volgende string:
AUTN4/12/200210:59am0500
Het lijkt er dus op dat de tags die attributes hebben, niet mee geselecteerd worden. Het is natuurlijk de bedoeling dat ik die tags ook meekrijg.

Iemand die mij op weg kan helpen?

https://fgheysels.github.io/


  • Scare360
  • Registratie: Juli 2001
  • Laatst online: 07:26
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
    '******************************************************
    ' <summary>
    ' This method parses a System.Messaging.Message object to a good
    ' old ADODB.Recordset. Notice that the ADODB is an interop! We need this to
    ' to keep compatible with the "old" BatchRegD component (our client)
    ' </summary>
    Public Function ParseMessageToRecordSet(ByVal msgMessage As System.Messaging.Message) As ADODB.Recordset
      Dim rsMessage = New ADODB.Recordset()  ' Recordset to collect the xml data in
      Dim xmlDoc As XmlDocument          ' Xml-document representation of the recieved message
      Dim nrXml As XmlNodeReader            ' Xml-node reader for traversing all the nodes in the xml-doc

      xmlDoc = New XmlDocument()
      xmlDoc.LoadXml(msgMessage.Body)
      nrXml = New XmlNodeReader(xmlDoc.SelectSingleNode("ProductionEvent"))

      Try
        rsMessage.Fields.Append("Name", ADODB.DataTypeEnum.adVarChar, 50, ADODB.FieldAttributeEnum.adFldUpdatable)
        rsMessage.Fields.Append("Value", ADODB.DataTypeEnum.adVarChar, 50, ADODB.FieldAttributeEnum.adFldUpdatable)
        rsMessage.Fields.Append("Header", ADODB.DataTypeEnum.adBoolean, , ADODB.FieldAttributeEnum.adFldUpdatable)
        rsMessage.LockType = ADODB.LockTypeEnum.adLockBatchOptimistic
        rsMessage.Open()
        Dim blnInnerLoop = False  ' Dont change

        ' We filled our xmlDoc, lets walk along the nodes and fill our recordset
        ' Notice the inner/outer loop mechanisme. I need to construct the algol this
        ' way because of the elements only typo xml-message we recieve, PG
        While (nrXml.Read())    ' Loop through al the nodes in our xml-doc
            Select Case nrXml.NodeType  ' To prevent overhead filter only the element node-type
              Case XmlNodeType.Element
                If (nrXml.Name <> "ProductionEvent") Then   ' We dont want the "ProductionEvent node in our recordset so skip this one
                    If (nrXml.Name = "Values") Then     ' Check if we reached the inner-xml where our values are listed
                      blnInnerLoop = True   ' Switch to inner loop
                      nrXml.Read()    ' We dont need the "Values" node so skip this on and move to "Name"
                    End If

                    If (blnInnerLoop = True) Then
                      ' Add a recordset, walk the nodes in our xml-doc and fill our recordset with the
                      ' the retrieved values from the xml-doc
                      ' NOTE: this is NOT our header so set the header bool = false
                      rsMessage.AddNew()
                      nrXml.Read()
                      rsMessage.Fields("Name").Value = nrXml.Value
                      nrXml.Read()
                      nrXml.Read()
                      nrXml.Read()
                      rsMessage.Fields("Value").Value = nrXml.Value
                      rsMessage.Fields("Header").Value = False
                    Else
                      ' Add a recordset, walk the nodes in our xml-doc and fill our recordset with the
                      ' the retrieved values from the xml-doc
                      ' NOTE: this is our header so set the header bool = true
                      rsMessage.AddNew()
                      rsMessage.Fields("Name").Value = nrXml.Name
                      nrXml.Read()
                      rsMessage.Fields("Value").Value = nrXml.Value
                      rsMessage.Fields("Header").Value = True
                    End If
                End If
            End Select
        End While

        Return rsMessage
      Catch err As Exception
        MsgBox(err.Message)
      End Try
    End Function

In mijn XML bericht zitten alleen maar elements... je moet het attribuut element afvangen in een XmlNodeType.Attribuut achtigtige case...

ff de code naar C# denken maar das niet zo moeilijk, een while lus dus...de nodes afvangen met XmlNodeType.[type node] middels een switch. De body van de switch netjes invullen naargelang je functionaliteit nrXml.Value kom je vast een heel eind


niet concreets maar wel de handle gegeven ;), succes

Verwijderd

'Value' property van een XPathNavigator object geeft bij een 'element' Node Type de InnerText terug (de concatenatie van alle texts in alle child nodes), wat volgens mij gebeurt.

Kun je in de debugger kijken wat de structuur is van het XPathNavigator object waar iter.Current naar wijst?

  • whoami
  • Registratie: December 2000
  • Laatst online: 10:11
Jammer genoeg gebruik ik geen VS.NET, maar een gratis editor. Zonder debugger...

Dat wil dus zeggen, dat die tags met attributs niet in de inner-text opgenomen worden?

https://fgheysels.github.io/


  • whoami
  • Registratie: December 2000
  • Laatst online: 10:11
De reden waarom de <price> tags niet mee geselecteerd worden heb ik gevonden: Ze hebben geen waarde. Er is wel een attribuut die de waarde weergeeft, maar de tag zelf heeft geen waarde.

https://fgheysels.github.io/


Verwijderd

Op vrijdag 19 april 2002 12:16 schreef whoami het volgende:
Jammer genoeg gebruik ik geen VS.NET, maar een gratis editor. Zonder debugger...

Dat wil dus zeggen, dat die tags met attributs niet in de inner-text opgenomen worden?
Je current node is dit:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
<stock_quote>
  <symbol>AUTN</symbol> 
  <when>
   <date>4/18/2002</date> 
   <time>10:59am</time> 
  </when>
  <price type="ask" value="5" /> 
  <price type="open" value="4.92" /> 
  <price type="dayhigh" value="5" /> 
  <price type="daylow" value="4.92" /> 
  <change>0</change> 
  <volume>500</volume> 
</stock_quote>

Daar vraag je de inner tekst van op. Die is dus de text van alle childs aan elkaar, dus:
AUTN4/18/200210:59am0500

Je moet dus 1 niveau dieper gaan met uitlezen

  • whoami
  • Registratie: December 2000
  • Laatst online: 10:11
Mijn tag is wel degelijk zoals ik hem in m'n eerste post gepost heb:
code:
1
2
3
4
5
6
7
8
<stock_quote>
  <symbol>AUTN</symbol>
  <when>
     <date></date>
     <time></time>
  </when>
  <price type="ask" value="5"/>
 ...

De volume - tags en difference haalt hij allemaal op, behalve de tags 'price' die een attribute hebben, maar verder geen value.

Ttz, hij zal ze wel ophalen, maar aangezien ze geen waarde hebben, toont hij ze niet.
Nu is m'n vraag, hoe geraak ik aan die attributes?

https://fgheysels.github.io/


  • whoami
  • Registratie: December 2000
  • Laatst online: 10:11
Op vrijdag 19 april 2002 14:15 schreef Otis het volgende:

[..]

Je current node is dit:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
<stock_quote>
  <symbol>AUTN</symbol> 
  <when>
   <date>4/18/2002</date> 
   <time>10:59am</time> 
  </when>
  <price type="ask" value="5" /> 
  <price type="open" value="4.92" /> 
  <price type="dayhigh" value="5" /> 
  <price type="daylow" value="4.92" /> 
  <change>0</change> 
  <volume>500</volume> 
</stock_quote>

Daar vraag je de inner tekst van op. Die is dus de text van alle childs aan elkaar, dus:
AUTN4/18/200210:59am0500

Je moet dus 1 niveau dieper gaan met uitlezen
Als je bedoelt 1 niveau dieper gaan, bedoel je dan die when node? Die haalt hij op.

https://fgheysels.github.io/


Verwijderd

Op vrijdag 19 april 2002 14:18 schreef whoami het volgende:
Mijn tag is wel degelijk zoals ik hem in m'n eerste post gepost heb:
code:
1
2
3
4
5
6
7
8
<stock_quote>
  <symbol>AUTN</symbol>
  <when>
     <date></date>
     <time></time>
  </when>
  <price type="ask" value="5"/>
 ...

De volume - tags en difference haalt hij allemaal op, behalve de tags 'price' die een attribute hebben, maar verder geen value.

Ttz, hij zal ze wel ophalen, maar aangezien ze geen waarde hebben, toont hij ze niet.
Nu is m'n vraag, hoe geraak ik aan die attributes?
Je huidige code selecteert een stock_quote node. Je kunt nu daarbinnen nodes selecteren en daarvan de attributes uitlezen, bv door de attributes collection uit te lezen van de XNode object.

  • whoami
  • Registratie: December 2000
  • Laatst online: 10:11
Ok, 'k heb uiteindelijk de oplossing gevonden.
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
iter = nav.Select("/stock_quotes//stock_quote[symbol='" +
            "StockView.Items[i].Text + "']");
            
while(iter.MoveNext())
{ 
  iter.Current.MoveToFirstChild();
  while(iter.Current.MoveToNext())
  {
    if (iter.Current.Name == "price")
    {
    pricetype = iter.Current.GetAttribute("type", "");
    if (pricetype == "ask")
    {
      askprice = iter.Current.GetAttribute("value", "");
    }
    }           
  }
}

Maar echt 'mooie' code is dit eigenlijk niet. Dus als er mensen zijn die de kennis hebben om dit 'op te schonen' mogen ze het altijd doen. :)

thx.

https://fgheysels.github.io/


Verwijderd

Op vrijdag 19 april 2002 14:50 schreef whoami het volgende:
Ok, 'k heb uiteindelijk de oplossing gevonden.
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
iter = nav.Select("/stock_quotes//stock_quote[symbol='" +
            "StockView.Items[i].Text + "']");
            
while(iter.MoveNext())
{ 
  iter.Current.MoveToFirstChild();
  while(iter.Current.MoveToNext())
  {
    if (iter.Current.Name == "price")
    {
    pricetype = iter.Current.GetAttribute("type", "");
    if (pricetype == "ask")
    {
      askprice = iter.Current.GetAttribute("value", "");
    }
    }           
  }
}

Maar echt 'mooie' code is dit eigenlijk niet. Dus als er mensen zijn die de kennis hebben om dit 'op te schonen' mogen ze het altijd doen. :)
Je kunt met XPath ook tags op basis van attributevalue selecteren. (bv price[@type="ask"] ). Je hoeft dan niet te if-then-elsen maar je kunt de tags selecteren en meteen de value uitlezen mocht de selectie values opleveren.
Pagina: 1