I have the following XML file:
<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>
The idea is that I recursively loop through the <Players>
block and add the data there to a holder of class data. However, when I run the following code, which looks like it is yielding the contents of the Inner Players field, VS stops warning that it is empty:
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
In particular, the error is: System.NullReferenceException: 'Object reference not set to an instance of an object.'
. I'm not sure where the null reference is taking place. (The breakpoint places it at the Dim tpPlayer As Player Info
line.
CodePudding user response:
It may be causing you grief that your Select Case
is assigning variables to themselves:
Select Case tpPlayer.strSeatOrder
Case "A"
tpPlayerA = tpPlayer
Case "B"
tpPlayerB = tpPlayerB
Case "C"
tpPlayerC = tpPlayerC
End Select
The RHS of each should just be tpPlayer
.
You caused this bug by doing this:
Dim tpPlayerA As New PlayerInfo
Dim tpPlayerB As New PlayerInfo
Dim tpPlayerC As New PlayerInfo
You should have done this instead:
Dim tpPlayerA As PlayerInfo
Dim tpPlayerB As PlayerInfo
Dim tpPlayerC As PlayerInfo
Here's what your code would look like with XDocument
. It's much cleaner.
Dim intExpectedPlayers As Integer = CInt(xd.Root.Attribute("TeamPlayers"))
Dim lstOutputTeams As List(Of TournamentTeam) = _
( _
From team In xd.Root.Element("Teams").Elements("Team") _
Let players = team.Element("Players").Elements("Player").Select(Function(player) _
New PlayerInfo() With _
{
.strMembershipName = player.Element("MemberName").Value,
.strMembershipNo = player.Element("MemberNumber").Value,
.strPlayerFirstName = player.Element("PlayerFirstName").Value,
.strPlayerLastName = player.Element("PlayerLastName").Value,
.strSeatOrder = player.Element("SeatOrder").Value
}).ToDictionary(Function(x) x.strSeatOrder) _
Select New TournamentTeam() With _
{
.intTeamID = CInt(team.Element("TeamID")),
.strTeamName = CStr(team.Element("TeamName")),
.intStatus = CInt(team.Element("Status")),
.strMemo = CStr(team.Element("Memo")),
.intByeRounds = CInt(team.Element("ByeRounds")),
.tpPlayerA = players("A"),
.tpPlayerB = players("B"),
.tpPlayerC = players("C")
} _
).ToList()
Here's the creation of the xd
that I used for testing:
Dim xd = XDocument.Parse("<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>")
I'd also suggest changing your classes to use the standard naming conventions for .NET.
Public Class TournamentTeam
Public Name As String
Public ID As Integer
Public ByeRounds As Integer
Public Status As Integer
Public Memo As String
Public PlayerA As PlayerInfo
Public PlayerB As PlayerInfo
Public PlayerC As PlayerInfo
End Class
Public Class PlayerInfo
Public MembershipName As String
Public MembershipNo As String
Public FirstName As String
Public LastName As String
Public SeatOrder As String
End Class
Finally, I'd also look at removing the three Player
properties and put a dictionary in instead.
If it's always A
, B
, and C
then use an Enum
as the key, otherwise use a String
.
Public Class TournamentTeam
Public Name As String
Public ID As Integer
Public ByeRounds As Integer
Public Status As Integer
Public Memo As String
Public Players As Dictionary(Of PlayerSeat, PlayerInfo)
End Class
Public Enum PlayerSeat
A
B
C
End Enum
Public Class PlayerInfo
Public MembershipName As String
Public MembershipNo As String
Public FirstName As String
Public LastName As String
Public SeatOrder As String
End Class
Then your code would be:
Dim lstOutputTeams As List(Of TournamentTeam) = _
( _
From team In xd.Root.Element("Teams").Elements("Team") _
Select New TournamentTeam() With _
{
.ID = CInt(team.Element("TeamID")),
.Name = CStr(team.Element("TeamName")),
.Status = CInt(team.Element("Status")),
.Memo = CStr(team.Element("Memo")),
.ByeRounds = CInt(team.Element("ByeRounds")),
.Players = team.Element("Players").Elements("Player").Select(Function(player) _
New PlayerInfo() With _
{
.MembershipName = player.Element("MemberName").Value,
.MembershipNo = player.Element("MemberNumber").Value,
.FirstName = player.Element("PlayerFirstName").Value,
.LastName = player.Element("PlayerLastName").Value,
.SeatOrder = player.Element("SeatOrder").Value
}).ToDictionary(Function(x) CType([Enum].Parse(GetType(PlayerSeat), x.SeatOrder), PlayerSeat))
} _
).ToList()