【问题标题】:XPath not returning Node Value ASP.NET 4.5XPath 不返回节点值 ASP.NET 4.5
【发布时间】:2016-02-24 13:38:22
【问题描述】:

这里有一个小问题,我正在为客户将传统的经典 ASP 应用程序转换为 ASP.NET 4.5,并且需要从 XML 文件中提取数据,但我无法获得单个节点的值,我的代码什么也不返回。

XML 文件

<?xml version="1.0" encoding="UTF-8"?>
<Devices-Detail-Response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.embeddeddatasystems.com/schema/owserver">
<PollCount>150722</PollCount>
<DevicesConnected>4</DevicesConnected>
<LoopTime>3.974</LoopTime>
<DataErrors>2</DataErrors>
<DeviceName>OW_SERVER-Enet</DeviceName>
<HostName>EDSOWSERVER</HostName
<MACAddress>00:50:C2:91:B3:9C</MACAddress>
<owd_DS18B20 Description="Programmable resolution thermometer">
<Name>DS18B20</Name>
<Family>28</Family>
<ROMId>2D0000023C519A28</ROMId>
<Health>7</Health>
<RawData>0E014B467FFF021000000</RawData>
<PrimaryValue>16.8750 Deg C</PrimaryValue>
<Temperature Units="Centigrade">16.8750</Temperature>
<UserByte1 Writable="True">75</UserByte1>
<UserByte2 Writable="True">70</UserByte2>
<Resolution>12</Resolution>
<PowerSource>0</PowerSource>
</owd_DS18B20>
<owd_DS18B20 Description="Programmable resolution thermometer">
<Name>DS18B20</Name>
<Family>28</Family>
<ROMId>DA000002E0E5A928</ROMId>
<Health>7</Health>
<RawData>32014B467FFF0E101E000000</RawData>
<PrimaryValue>19.1250 Deg C</PrimaryValue>
<Temperature Units="Centigrade">19.1250</Temperature>
<UserByte1 Writable="True">75</UserByte1>
<UserByte2 Writable="True">70</UserByte2>
<Resolution>12</Resolution>
<PowerSource>0</PowerSource>
</owd_DS18B20>
<owd_DS18B20 Description="Programmable resolution thermometer">
<Name>DS18B20</Name>
<Family>28</Family>
<ROMId>C3000002E0EE1B28</ROMId>
<Health>7</Health>
<RawData>1D014B467FFF0310C5000000</RawData>
<PrimaryValue>17.8125 Deg C</PrimaryValue>
<Temperature Units="Centigrade">17.8125</Temperature>
<UserByte1 Writable="True">75</UserByte1>
<UserByte2 Writable="True">70</UserByte2>
<Resolution>12</Resolution>
<PowerSource>0</PowerSource>
</owd_DS18B20>
<owd_DS18B20 Description="Programmable resolution thermometer">
<Name>DS18B20</Name>
<Family>28</Family>
<ROMId>070000023DBB7F28</ROMId>
<Health>7</Health>
<RawData>52014B467FFF0E10FFFF0000000000000000</RawData>
<PrimaryValue>21.1250 Deg C</PrimaryValue>
<Temperature Units="Centigrade">21.1250</Temperature>
<UserByte1 Writable="True">75</UserByte1>
<UserByte2 Writable="True">70</UserByte2>
<Resolution>12</Resolution>
<PowerSource>255</PowerSource>
</owd_DS18B20>
</Devices-Detail-Response>

我需要能够根据 ROMId 获取温度值,因为任何时候最多可以连接 22 个传感器。

我尝试了以下方法,但总是没有任何结果:

Imports System.Xml
Dim m_xmld As XmlDocument
Dim m_nodelist As XmlNodeList
Dim m_node As XmlNode
Dim Temperature As String = String.Empty

m_xmld = New XmlDocument()
m_xmld.Load("http://" & server_ip & "/details.xml")
Dim m_xmlns As New XmlNamespaceManager(m_xmld.NameTable)
m_xmlns.AddNamespace("opsm_ns", "http://tempuri.org/zitem.xsd")

For Each node As XmlNode In
m_xmld.SelectNodes("//opsm_ns:owd_DS18B20[ROMId='" & deviceId & "']",m_xmlns)
Console.WriteLine(node.Attributes("Temperature").Value)
Next

文档加载,但我根本无法取出数据,因为没有返回任何内容,我在这里缺少什么??

提前致谢

奥兹

更新 2:已在 Visual Studio 中获取完整的 XML 输出并在上面更新

更新 3:已将下面 @CrnaStena 中的示例与上述 XML 一起使用,但仍然没有返回任何值

【问题讨论】:

  • 您似乎选择了没有温度属性的 ROMid 节点。您必须先获取节点父节点,然后查找名称为 temperature 的子节点。
  • 我已尝试选择根节点“/Devices-Detail-Response”,但这不会返回任何内容。这就是我以古老的经典 asp 方式做到的:Dim strXPath : strXPath = "/Devices-Detail-Response/owd_DS18B20[ROMId='" &amp; strName &amp; "']" Dim xmlSensor For Each xmlSensor In objXML.documentElement.selectNodes(strXPath) Dim romID : romID = xmlSensor.selectSingleNode("ROMId").text Dim temperature : temperature = xmlSensor.selectSingleNode("Temperature").text Response.Write Server.HTMLEncode(temperature) &amp; "&lt;br /&gt;&lt;br /&gt;" Next
  • 如果我在 Visual Studio 中单步执行代码,我可以看到文档正在成功加载,但是当我们通过它时,数据/节点不可用
  • 我终于到了电脑前,看看我的回答,如果您需要进一步的帮助,请告诉我。
  • 为什么你的 XML 没有使用命名空间前缀?示例 XML 中的所有节点都在默认命名空间中。

标签: asp.net xml vb.net xpath


【解决方案1】:

这是我用 C# 编写的 LinqPad 示例,抱歉,我已经 20 年没有使用过 VB 了。我已扩展您的 XML 以添加具有不同 ROMid 和温度的附加节点。

void Main()
{
        // XML to load
    var xml = @"<?xml version=""1.0"" encoding=""UTF-8""?>
<Devices-Detail-Response>
    <PollCount>36593</PollCount>
    <DevicesConnected>2</DevicesConnected>
    <LoopTime>1.031</LoopTime>
    <DataErrors>0</DataErrors>
    <DeviceName>OW_SERVER-Enet</DeviceName>
    <HostName>EDSOWSERVER</HostName>
    <MACAddress>00:50:C2:91:B3:9C</MACAddress>
    <owd_DS18B20 Description=""Programmable resolution thermometer"">
        <Name>DS18B20</Name>
        <Family>28</Family>
        <ROMId>2D0000023C519A28</ROMId>
        <Health>7</Health>
        <RawData>61014B467FFF0F1002FF</RawData>
        <PrimaryValue>22.0625 Deg C</PrimaryValue>
        <Temperature Units=""Centigrade"">22.0625</Temperature>
        <UserByte1 Writable=""True"">75</UserByte1>
        <UserByte2 Writable=""True"">70</UserByte2>
        <Resolution>12</Resolution>
        <PowerSource>255</PowerSource>
    </owd_DS18B20>
    <owd_DS18B20 Description=""Programmable resolution thermometer"">
        <Name>DS18B20</Name>
        <Family>28</Family>
        <ROMId>2D0000023C519A29</ROMId>
        <Health>7</Health>
        <RawData>61014B467FFF0F1002FF</RawData>
        <PrimaryValue>22.0625 Deg C</PrimaryValue>
        <Temperature Units=""Centigrade"">24.0625</Temperature>
        <UserByte1 Writable=""True"">75</UserByte1>
        <UserByte2 Writable=""True"">70</UserByte2>
        <Resolution>12</Resolution>
        <PowerSource>255</PowerSource>
    </owd_DS18B20>  
</Devices-Detail-Response>";

    var xmlDoc = new XmlDocument();
    xmlDoc.LoadXml(xml);

    var nodes = xmlDoc.SelectNodes("//owd_DS18B20/ROMId[text()='2D0000023C519A28']/../Temperature");
    foreach(XmlNode node in nodes)
    {
        node.InnerText.Dump();
    }


}

结果显示22.0625

更新 1

我复习了 VB,这是具有相同结果的 LinqPad VB 代码:

Sub Main
    Dim xml As String
    Dim m_xmld As XmlDocument

    xml = <![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<Devices-Detail-Response>
    <PollCount>36593</PollCount>
    <DevicesConnected>2</DevicesConnected>
    <LoopTime>1.031</LoopTime>
    <DataErrors>0</DataErrors>
    <DeviceName>OW_SERVER-Enet</DeviceName>
    <HostName>EDSOWSERVER</HostName>
    <MACAddress>00:50:C2:91:B3:9C</MACAddress>
    <owd_DS18B20 Description="Programmable resolution thermometer">
        <Name>DS18B20</Name>
        <Family>28</Family>
        <ROMId>2D0000023C519A28</ROMId>
        <Health>7</Health>
        <RawData>61014B467FFF0F1002FF</RawData>
        <PrimaryValue>22.0625 Deg C</PrimaryValue>
        <Temperature Units="Centigrade">22.0625</Temperature>
        <UserByte1 Writable="True">75</UserByte1>
        <UserByte2 Writable="True">70</UserByte2>
        <Resolution>12</Resolution>
        <PowerSource>255</PowerSource>
    </owd_DS18B20>
    <owd_DS18B20 Description="Programmable resolution thermometer">
        <Name>DS18B20</Name>
        <Family>28</Family>
        <ROMId>2D0000023C519A29</ROMId>
        <Health>7</Health>
        <RawData>61014B467FFF0F1002FF</RawData>
        <PrimaryValue>22.0625 Deg C</PrimaryValue>
        <Temperature Units="Centigrade">24.0625</Temperature>
        <UserByte1 Writable="True">75</UserByte1>
        <UserByte2 Writable="True">70</UserByte2>
        <Resolution>12</Resolution>
        <PowerSource>255</PowerSource>
    </owd_DS18B20>  
</Devices-Detail-Response>]]>.Value

    m_xmld = New XmlDocument()
    m_xmld.LoadXml(xml)
    'Dim m_xmlns As New XmlNamespaceManager(m_xmld.NameTable)
    'm_xmlns.AddNamespace("opsm_ns", "http://tempuri.org/zitem.xsd")
    Dim deviceId As String = "2D0000023C519A28"

    For Each node As XmlNode In m_xmld.SelectNodes("//owd_DS18B20/ROMId[text()='" & deviceId & "']/../Temperature")
        node.InnerText.Dump()
    Next

End Sub

更新 2 - 这行得通。您的 XML 缺少 &lt;/HostName&gt; 的结束标记。您还必须为沿 XPath 的每个标记指定命名空间。

 //opsm_ns:owd_DS18B20/opsm_ns:ROMId[text()='" & deviceId & "']/../opsm_ns:Temperature

这是整个小程序。

Sub Main
    Dim xml As String
    Dim m_xmld As XmlDocument

    xml = <![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<Devices-Detail-Response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.embeddeddatasystems.com/schema/owserver">
<PollCount>150722</PollCount>
<DevicesConnected>4</DevicesConnected>
<LoopTime>3.974</LoopTime>
<DataErrors>2</DataErrors>
<DeviceName>OW_SERVER-Enet</DeviceName>
<HostName>EDSOWSERVER</HostName>
<MACAddress>00:50:C2:91:B3:9C</MACAddress>
<owd_DS18B20 Description="Programmable resolution thermometer">
<Name>DS18B20</Name>
<Family>28</Family>
<ROMId>2D0000023C519A28</ROMId>
<Health>7</Health>
<RawData>0E014B467FFF021000000</RawData>
<PrimaryValue>16.8750 Deg C</PrimaryValue>
<Temperature Units="Centigrade">16.8750</Temperature>
<UserByte1 Writable="True">75</UserByte1>
<UserByte2 Writable="True">70</UserByte2>
<Resolution>12</Resolution>
<PowerSource>0</PowerSource>
</owd_DS18B20>
<owd_DS18B20 Description="Programmable resolution thermometer">
<Name>DS18B20</Name>
<Family>28</Family>
<ROMId>DA000002E0E5A928</ROMId>
<Health>7</Health>
<RawData>32014B467FFF0E101E000000</RawData>
<PrimaryValue>19.1250 Deg C</PrimaryValue>
<Temperature Units="Centigrade">19.1250</Temperature>
<UserByte1 Writable="True">75</UserByte1>
<UserByte2 Writable="True">70</UserByte2>
<Resolution>12</Resolution>
<PowerSource>0</PowerSource>
</owd_DS18B20>
<owd_DS18B20 Description="Programmable resolution thermometer">
<Name>DS18B20</Name>
<Family>28</Family>
<ROMId>C3000002E0EE1B28</ROMId>
<Health>7</Health>
<RawData>1D014B467FFF0310C5000000</RawData>
<PrimaryValue>17.8125 Deg C</PrimaryValue>
<Temperature Units="Centigrade">17.8125</Temperature>
<UserByte1 Writable="True">75</UserByte1>
<UserByte2 Writable="True">70</UserByte2>
<Resolution>12</Resolution>
<PowerSource>0</PowerSource>
</owd_DS18B20>
<owd_DS18B20 Description="Programmable resolution thermometer">
<Name>DS18B20</Name>
<Family>28</Family>
<ROMId>070000023DBB7F28</ROMId>
<Health>7</Health>
<RawData>52014B467FFF0E10FFFF0000000000000000</RawData>
<PrimaryValue>21.1250 Deg C</PrimaryValue>
<Temperature Units="Centigrade">21.1250</Temperature>
<UserByte1 Writable="True">75</UserByte1>
<UserByte2 Writable="True">70</UserByte2>
<Resolution>12</Resolution>
<PowerSource>255</PowerSource>
</owd_DS18B20>
</Devices-Detail-Response>]]>.Value

    m_xmld = New XmlDocument()
    m_xmld.LoadXml(xml)
    Dim m_xmlns As New XmlNamespaceManager(m_xmld.NameTable)
    m_xmlns.AddNamespace("opsm_ns", "http://www.embeddeddatasystems.com/schema/owserver")
    Dim deviceId As String = "2D0000023C519A28"

    For Each node As XmlNode In m_xmld.SelectNodes("//opsm_ns:owd_DS18B20/opsm_ns:ROMId[text()='" & deviceId & "']/../opsm_ns:Temperature", m_xmlns)
        node.InnerText.Dump()
    Next

End Sub

【讨论】:

  • 我已经尝试了您的建议,但不幸的是,作为 dan field 的建议,我没有得到任何结果。稍后我将发布来自 innerXML 的完整 XML 输出,以防我们遗漏了什么。
  • 好的,所以使用了更新 1 中的电子邮件以及我在上面更新的完整 XML,是的,没有返回值。我更改了 Select Nodes 行以包含从根目录开始的完整路径,但仍然没有:-(
  • 对不起,我的意思是上面评论中的示例,而不是电子邮件。
  • 好的,我添加了适用于命名空间的示例。请参阅更新 2
  • 工作,但必须删除 .Dump() 因为它不受支持,XML 命名空间的新手,所以帮助很大,再次感谢 :-)
【解决方案2】:

您的 XML 位于默认命名空间中,但您正在尝试读取位于指定命名空间中的节点。改变这个:

m_xmld = New XmlDocument()
m_xmld.Load("http://" & server_ip & "/details.xml")
Dim m_xmlns As New XmlNamespaceManager(m_xmld.NameTable)
m_xmlns.AddNamespace("opsm_ns", "http://tempuri.org/zitem.xsd")

For Each node As XmlNode In
m_xmld.SelectNodes("//opsm_ns:owd_DS18B20[ROMId='" & deviceId & "']",m_xmlns)
Console.WriteLine(node.Attributes("Temperature").Value)
Next

到这里:

m_xmld = New XmlDocument()
m_xmld.Load("http://" & server_ip & "/details.xml")
/* Dim m_xmlns As New XmlNamespaceManager(m_xmld.NameTable)
m_xmlns.AddNamespace("opsm_ns", "http://tempuri.org/zitem.xsd") */

For Each node As XmlNode In
m_xmld.SelectNodes("//owd_DS18B20[ROMId='" & deviceId & "']") /*,m_xmlns)*/
Console.WriteLine(node.Attributes("Temperature").Value)
Next

或者,使用位于命名空间中的新更新(但与您实际使用的不同,您可以这样做):

m_xmld = New XmlDocument()
m_xmld.Load("http://" & server_ip & "/details.xml")
Dim m_xmlns As New XmlNamespaceManager(m_xmld.NameTable)
m_xmlns.AddNamespace("opsm_ns", "http://www.embeddeddatasystems.com/schema/owserver")

For Each node As XmlNode In
m_xmld.SelectNodes("//opsm_ns:owd_DS18B20[ROMId='" & deviceId & "']",m_xmlns)
Console.WriteLine(node.Attributes("Temperature").Value)
Next

如果事实证明您的 XML 在某些实例中确实包含命名空间,但在其他实例中不包含命名空间,但您知道您总是希望该节点按名称命名,您可以像这样使用 local-name() 函数:

m_xmld.SelectNodes("//*[local-name()='owd_DS18B20'][ROMId='" & deviceId & "']")

【讨论】:

  • 哈,在我的示例中我也禁用了命名空间,但我不知道本地名称。每天学习新东西。谢谢。
  • 我错过了你的 - 不太确定你改变了什么
  • @DanField 我已经尝试过你的建议,一旦我们点击 for each 循环,它就会退出,因为 xmlnode 为空?我确实尝试了您的其他建议,但这没有任何区别。我真的不明白,连接xml源没有问题。
  • 更新了我的答案。您的实际 XML 实例使用新的命名空间。
  • @DanField,更改有效,但我必须根据 CrnaStena 的更新更改选择节点路径并将 node.attributes("").values 更改为 node.innerText 以返回值。感谢您的帮助,之前从未使用过 XML 命名空间,所以在这里学到了一些新东西 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-03
  • 1970-01-01
  • 2016-02-23
  • 2018-07-28
相关资源
最近更新 更多