Null returned when selecting a node in XML documen

2019-05-30 06:51发布

问题:

New to XML. I have a 3rd party webservice that supplies an XML document that I have to update the element values and pass back. The core issue issue is I get an NullReferenceException error when calling the node.RemoveAll() method in the code below. I'm calling the RemoveAll() method because each element has the xsi:nil attribute when it is supplied to me, and if I don't remove it before updating the element value, the XML won't validate by the webservice.

The XML document supplied by the 3rd party webservice is as follows:

<?xml version="1.0" encoding="utf-16"?>
<TaskData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schema.sample.com/application/1/520800B">
  <Global>
    <RequestInfo xmlns="http://schema.sample.com/application/1/Types">
      <Requestor xsi:nil="true" />
      <Date_init xsi:nil="true" />
      <Shipto xsi:nil="true" />
      <Customer xsi:nil="true" />
      <Contact xsi:nil="true" />
      <Requestor_Email xsi:nil="true" />      
    </RequestInfo>    
   </Global>
  </TaskData>

Other solutions I've seen have used the XmlNamespaceManager, but I haven't been able to make it work. This xml document has a namespace specified for the TaskData element, and a different namespace for the RequestInfo element. I tried specifying the XmlNamespaceManager variable for each namespace, but got the same results....hovering over the nsmgr variable while in break mode reveals that the "children could not be evaluated" and that the DefaultNamespace property is an empty string.

Public Sub testxml()

    Dim doc As New XmlDocument
    doc.Load("c:\temp\sample.xml")

    Dim nsmgr As XmlNamespaceManager = New XmlNamespaceManager(doc.NameTable)
    nsmgr.AddNamespace("s", "http://schema.sample.com/application/1/520800B")

    Dim node As XmlNode = doc.SelectSingleNode("s:Requestor", nsmgr)
    node.RemoveAll()
    node.InnerText = "Your Name Goes Here"

    doc.Save("c:\temp\sample.xml")

End Sub

回答1:

The problem is with this statement:

doc.SelectSingleNode("s:Requestor", nsmgr)

what you need to do is

doc.SelectSingleNode("//s:Requestor",nsmgr)

"s:Requestor" means give me the node underneath the current node name s:Requestor

"//s:Requestor" means give me all nodes in the document named s:Requestor

if you want to ignore the namespace you could do

doc.SelectSingleNode("//*[local-name()='Requestor']")


回答2:

I'm not sure whether I have understood your task correctly. But if you have to just remove the xsi:nil="true" part, why don't you load it as a string and invoke a

string.replace("xsi:nil=\"true\"", "")

Of course that's not the cleanest solution, but I'm not yet comfortable with the XML handling API of C#, so I'd have to consult the MSDN to get accustomed. Maybe this solves already your issue.



回答3:

There are two errors in your code. One is the XPath used for selecting the node (as Greg pointed out). The second one is the name space. I may be wrong, but as I interpret the XML document the TaskData element has the following namespace declaration:

xmlns="http://schema.sample.com/application/1/520800B"

...which sets up the namespace for elements without prefixes.

But then the RequestInfo tag has this namespace declaration:

xmlns="http://schema.sample.com/application/1/Types"

So within the RequestInfo tag, there is another namespace for tags without prefixes. In your code you use the first of these two namespaces to match a tag that resides in the second, which will not work out. There are two ways to solve it. One is to simply change the namespace in your code:

XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("s", "http://schema.sample.com/application/1/Types");
XmlNode node = doc.SelectSingleNode("//s:Requestor", nsmgr);

The second one is to define both namespaces, and use an XPath expression that points out the full path to the tag:

XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("r", "http://schema.sample.com/application/1/520800B");
nsmgr.AddNamespace("s", "http://schema.sample.com/application/1/Types");
XmlNode node = doc.SelectSingleNode(@"/r:TaskData/r:Global/s:RequestInfo/s:Requestor", nsmgr);

Both these cases lead to the same result; node is not null.



回答4:

You should be able to opt out of any namespace calls in your selectelement call, i prefer to loop through my document anyway.. but that's just me.