遍历XML时触发System.NullReferenceException问题求助
XML遍历空引用异常的修复方案
问题场景
给定如下XML结构:
<Tournament TeamPlayers="1"> <Teams> <Team> <TeamID>0</TeamID> <TeamName>Sample</TeamName> <Status>10</Status> <Memo>Sample Team</Memo> <ByeRounds>0</ByeRounds> <Players> <Player> <MemberNumber>1</MemberNumber> <MemberName>Dummy</MemberName> <PlayerFirstName>Test</PlayerFirstName> <PlayerLastName>User</PlayerLastName> <SeatOrder>A</SeatOrder> </Player> <Player> <MemberNumber></MemberNumber> <MemberName></MemberName> <PlayerFirstName></PlayerFirstName> <PlayerLastName></PlayerLastName> <SeatOrder>B</SeatOrder> </Player> <Player> <MemberNumber></MemberNumber> <MemberName></MemberName> <PlayerFirstName></PlayerFirstName> <PlayerLastName></PlayerLastName> <SeatOrder>C</SeatOrder> </Player> </Players> </Team> </Teams> </Tournament>
遍历<Players>节点并映射到自定义类集合时,运行以下代码触发System.NullReferenceException,断点指向Dim tpPlayer As PlayerInfo行:
Dim teamNodes As XmlNodeList = xmlDoc.DocumentElement.SelectNodes("/Tournament/Teams/Team") Dim lstOutputTeams As New List(Of TournamentTeam) ' Check the atrribute of "TeamPlayers" in the root. 'Dim intExpectedPlayers As Integer = xmlDoc.Attributes("/").Value Dim intExpectedPlayers As Integer = xmlDoc.SelectSingleNode("/Tournament").Attributes("TeamPlayers").Value For Each node As XmlNode In teamNodes Dim tpPlayerA As New PlayerInfo Dim tpPlayerB As New PlayerInfo Dim tpPlayerC As New PlayerInfo Dim playerNodes As XmlNodeList = node.SelectNodes("Players") For Each innerNode As XmlNode In playerNodes Dim tpPlayer As PlayerInfo = New PlayerInfo With { .strMembershipName = innerNode.SelectSingleNode("MemberName").InnerText, .strMembershipNo = innerNode.SelectSingleNode("MemberNumber").InnerText, .strPlayerFirstName = innerNode.SelectSingleNode("PlayerFirstName").InnerText, .strPlayerLastName = innerNode.SelectSingleNode("PlayerLastName").InnerText, .strSeatOrder = innerNode.SelectSingleNode("SeatOrder").InnerText } Select Case tpPlayer.strSeatOrder Case "A" tpPlayerA = tpPlayer Case "B" tpPlayerB = tpPlayerB Case "C" tpPlayerC = tpPlayerC End Select Next lstOutputTeams.Add(New TournamentTeam() With { .strTeamName = node.SelectSingleNode("TeamName").InnerText, .intTeamID = node.SelectSingleNode("TeamID").InnerText, .intByeRounds = node.SelectSingleNode("ByeRounds").InnerText, .intStatus = node.SelectSingleNode("Status").InnerText, .strMemo = node.SelectSingleNode("Memo").InnerText, .tpPlayerA = tpPlayerA, .tpPlayerB = tpPlayerB, .tpPlayerC = tpPlayerC} ) Next
问题原因
- XPath节点选择错误:
node.SelectNodes("Players")获取的是<Players>父节点,而非其下的<Player>子节点。尝试在<Players>节点上查找MemberName会返回null,访问InnerText时触发空引用。 - 赋值逻辑错误:SelectCase中Case B和C的赋值语句是
tpPlayerB = tpPlayerB,未将当前遍历的Player对象赋值给对应的变量。 - 类型转换隐患:XML节点的
InnerText是字符串类型,直接赋值给Integer类型的属性(如intTeamID)可能引发类型转换异常。
修复后的代码
Dim teamNodes As XmlNodeList = xmlDoc.DocumentElement.SelectNodes("/Tournament/Teams/Team") Dim lstOutputTeams As New List(Of TournamentTeam) ' 正确获取根节点属性并转换为Integer Dim intExpectedPlayers As Integer = Convert.ToInt32(xmlDoc.SelectSingleNode("/Tournament").Attributes("TeamPlayers").Value) For Each node As XmlNode In teamNodes Dim tpPlayerA As New PlayerInfo Dim tpPlayerB As New PlayerInfo Dim tpPlayerC As New PlayerInfo ' 修改XPath,获取Players下的所有Player子节点 Dim playerNodes As XmlNodeList = node.SelectNodes("Players/Player") For Each innerNode As XmlNode In playerNodes ' 安全获取节点文本,避免空引用 Dim getNodeText = Function(nodeName As String) Dim childNode = innerNode.SelectSingleNode(nodeName) Return If(childNode IsNot Nothing, childNode.InnerText, String.Empty) End Function Dim tpPlayer As PlayerInfo = New PlayerInfo With { .strMembershipName = getNodeText("MemberName"), .strMembershipNo = getNodeText("MemberNumber"), .strPlayerFirstName = getNodeText("PlayerFirstName"), .strPlayerLastName = getNodeText("PlayerLastName"), .strSeatOrder = getNodeText("SeatOrder") } Select Case tpPlayer.strSeatOrder Case "A" tpPlayerA = tpPlayer Case "B" tpPlayerB = tpPlayer ' 修正赋值逻辑 Case "C" tpPlayerC = tpPlayer ' 修正赋值逻辑 End Select Next ' 显式转换字符串为Integer类型 lstOutputTeams.Add(New TournamentTeam() With { .strTeamName = node.SelectSingleNode("TeamName").InnerText, .intTeamID = Convert.ToInt32(node.SelectSingleNode("TeamID").InnerText), .intByeRounds = Convert.ToInt32(node.SelectSingleNode("ByeRounds").InnerText), .intStatus = Convert.ToInt32(node.SelectSingleNode("Status").InnerText), .strMemo = node.SelectSingleNode("Memo").InnerText, .tpPlayerA = tpPlayerA, .tpPlayerB = tpPlayerB, .tpPlayerC = tpPlayerC} ) Next
关键修改点
- 将
node.SelectNodes("Players")改为node.SelectNodes("Players/Player"),直接遍历每个<Player>节点。 - 新增
getNodeText函数,安全获取节点文本,避免因节点不存在导致的空引用。 - 修正SelectCase中的赋值逻辑,将
tpPlayerB = tpPlayerB改为tpPlayerB = tpPlayer。 - 对Integer类型的属性使用
Convert.ToInt32进行显式类型转换,避免隐式转换错误。
内容的提问来源于stack exchange,提问作者Paul Williams
相关产品推荐
相关产品推荐

