1

I am having a problem the "SelectSingleNode" function in Visual Basic.

I have the following XML file:

<sistema versao="1.02" xmlns="http://www.portalfiscal.inf.br/nfe">
<det>
<prod>
    <cProd>000085</cProd>
    <xProd>MARTELO</xProd>
    <NCM>73170090</NCM>
    <uCom>UN</uCom>
    <vUnCom>7.0000</vUnCom>
</prod>
<imposto>
    <ICMS>
        <orig>0</orig>
        <CST>500</CST>
    </ICMS>
</imposto>
</det>
</sistema>

I need the value that is inside the node "cProd". In that case, "000085".

I tried the following code:

    Public Sub importacao()
    Dim arquivos = CheckedListBox1.CheckedItems
    Dim total = arquivos.Count

    Dim nome_produto As String

    For i As Integer = 0 To total - 1
        Dim xml As New XmlDocument

        xml.Load(arquivos.Item(i))

        nome_produto = xml.ChildNodes(0).ChildNodes(0).ChildNodes(0).SelectSingleNode("cProd").InnerText

    Next

End Sub

In "arquivos" is a CheckBoxList with the paths of the XML files.

That returns the error "Object reference not set to an instance of an object." Because "SelectSingleNode("cProd")" is returning a null value.

I think I'm placing the XPath incorrectly. Could someone help me?

Thank you and sorry for my english.

EDIT:

It worked, I used a function to remove the namespaces (source: http://rprateek.blogspot.com.br/2011/01/how-to-remove-xmlns-namespace-from-xml.html). And then I could use the following: xml.ChildNodes(0).SelectSingleNode("//cProd").InnerText

Just one more thing. What would be the most practical way of dealing with errors that may be caused if the tag "cProd" (or another one), does not exist in the file? For example, in some files, will not have the tag "NCM". Maybe I can verify if the tag exists with "GetElementsByTagName()"?

Thank you all.

Junior Zancan
  • 25
  • 1
  • 13
  • Your example is actually an invalid XML. If `` is a parent node for `` you're missing the `` at the end; if it is an empty node it should be ``. BTW, `SelectSingleNode` lets you specify the whole XPath so you don't need to do it the way yo did. – Josh Part Mar 27 '15 at 23:07
  • Sorry, there was a problem when I put the XML here. Now it's ok. About the XPath, could you please show me an example of how to do in this case? Thank you. – Junior Zancan Mar 27 '15 at 23:16
  • Why are you jumping through hoops? `xml.ChildNodes(0).SelectSingleNode("//cProd")` or `SelectSingleNode("/det/prod/cProd")` should both work for you. – Ken White Mar 27 '15 at 23:26

1 Answers1

1

As stated in the comments, you don't need to be hoping throught nodes to reach the node you want; you can set the whole XPath to look for the node you want.

Still, there's a little problem with your XML: it has a namespace without a prefix:

<sistema versao="1.02" xmlns="http://www.portalfiscal.inf.br/nfe">

This screws a little the XPath queries as all nodes have the default namespace prefix wich "cannot be seen", but it's still there.

You have three options:

1.- Remove the namespace from the XML...

<sistema versao="1.02">
<det>
<prod>
    <cProd>000085</cProd>
    <xProd>MARTELO</xProd>
    <NCM>73170090</NCM>
    <uCom>UN</uCom>
    <vUnCom>7.0000</vUnCom>
</prod>
<imposto>
    <ICMS>
        <orig>0</orig>
        <CST>500</CST>
    </ICMS>
</imposto>
</det>
</sistema>

... and use either

xml.ChildNodes(0).SelectSingleNode("//cProd").InnerText

or

xml.ChildNodes(0).SelectSingleNode("/sistema/det/prod/cProd").InnerText

2.- Specify a prefix (ns is an example, you can name it whatever you want) ...

<ns:sistema versao="1.02" xmlns:ns="http://www.portalfiscal.inf.br/nfe">
<ns:det>
<ns:prod>
    <ns:cProd>000085</ns:cProd>
    <ns:xProd>MARTELO</ns:xProd>
    <ns:NCM>73170090</ns:NCM>
    <ns:uCom>UN</ns:uCom>
    <ns:vUnCom>7.0000</ns:vUnCom>
</ns:prod>
<ns:imposto>
    <ns:ICMS>
        <ns:orig>0</ns:orig>
        <ns:CST>500</ns:CST>
    </ns:ICMS>
</ns:imposto>
</ns:det>
</ns:sistema>

... use a XmlNameSpaceManager...

Dim manager as New XmlNameSpaceManager(xml.NameTable)

... and use either

xml.ChildNodes(0).SelectSingleNode("//ns:cProd", manager).InnerText

or

xml.ChildNodes(0).SelectSingleNode("/ns:sistema/ns:det/ns:prod/ns:cProd", manager).InnerText

3.- Leave the XML as it is now, and use either

xml.ChildNodes(0).SelectSingleNode("//*[name()='cProd']").InnerText

or this little monstruosity

xml.ChildNodes(0).SelectSingleNode("/*[name()='sistema']/*[name()='det']/*[name()='prod']/*[name()='cProd']").InnerText

/* lets you ignore the namespace.

This question or this one might help you understand better the problem.

Community
  • 1
  • 1
Josh Part
  • 2,154
  • 12
  • 15
  • 1
    Thank you so much. I had to use the third option. I can not change the XML because it is generated by a third party software, and it would be impractical to open the file in text mode, edit the first line, close and continue. And I have to select by name, because the nodes will vary the position inside the tag "prod", depending on the file. So, thank you again. – Junior Zancan Mar 28 '15 at 00:21