0

I have a class Player that I use to create a List(Of Player). I need to save it when the application closes. In Windows Forms, I would just serialize, but that's no longer possible in UWP, so I had to Google for a few dozen of hours and I finally stumbled upon Microsoft.Toolkit.Uwp, then Newtonsoft.Json, but I fail miserably to use them. I need your help!

Let's say I have a small class :

Dim Name As String
Dim Score As Double
Public Class Player
    <JsonConstructor()>
    Public Sub New(Name As String, Score As Double) ' New with score
        Me.Name = Name
        Me.Score = Math.Max(1, Score)
    End Sub
End Class
Public Overrides Function ToString() As String ' ToString
    Return $"{Name} [{Score}]"
End Function

How do I successfully read and write a List(Of Player)?

' Loading MainPage.xaml
Private Sub MainPage_Loading() Handles Me.Loading
    ReadAsync()
    MainFrame.Margin = New Thickness(0)
    Window.Current.Content = MainFrame
    MainFrame.Navigate(GetType(PlayerList), Players)
End Sub

' Read
Private Async Sub ReadAsync()
    Players = JsonConvert.DeserializeObject(Of List(Of Player))(Await FileIO.ReadTextAsync((Await StorageFolder.CreateFileAsync("players.json", CreationCollisionOption.OpenIfExists))))
    If Players Is Nothing Then
        Players = New List(Of Player)
        WriteAsync()
    End If
End Sub

' Write
Public Shared Async Sub WriteAsync()
    Await FileIO.WriteTextAsync(Await StorageFolder.CreateFileAsync("players.json", CreationCollisionOption.ReplaceExisting), JsonConvert.SerializeObject(Players, Formatting.Indented))
End Sub

' Loading PlayerList.xaml
Protected Overrides Sub OnNavigatedTo(e As NavigationEventArgs)
    ListBoxPlayers.Items.Clear()
    Players = e.Parameter
    For Each Player In Players
        ListBoxPlayers.Items.Add(Player)
    Next
End Sub

' Adding a new player in the interface
Private Sub ButtonAddPlayer_Click(sender As Button, e As RoutedEventArgs) Handles ButtonAddPlayer.Click
    ' ...
    ' Commit
    MainPage.Players.Add(New Player(TextBoxName.Text))
    ListBoxPlayers.Items.Add(MainPage.Players(MainPage.Players.Count - 1))
    MainPage.WriteAsync()
End Sub

So this is all confusing. When I add a player trough the interface, it enters my ListBox like normal. However, when I close the application and I re-open it, I get a handful of empty objects.

List of different players Serialization didn't work!

I did some angry testing to know more about my problem, turns out I'm not serializing at all, but I probably am deserializing correctly.

Angry testing

Nato Boram
  • 4,083
  • 6
  • 28
  • 58
  • 1
    Have you tried using newtonsoft.json.net to serialize and deserialize your object to json and vice versa ? – Pratyay Jul 27 '17 at 09:15
  • @Pratyay Yes, I just tried to. However, I get an incorrect number of empty objects. – Nato Boram Jul 27 '17 at 21:09
  • The code you've posted is right. And I tested with you code, it works well. However, the code you've posted is not complete, if you still have problem, please share a [mcve] that can reproduce your issue. – Jay Zuo Jul 28 '17 at 09:21
  • One thing I notice is that in your `MainPage_Loading` method you are not `Await`ing the call to `ReadAsync`. So possibly what is happening is the navigation event is firing before the list of players is finished deserializing. – Brian Rogers Jul 28 '17 at 22:42
  • I just tried to `Await ReadAsync()` my new `Private Async Function ReadAsync() As Task` and still, I get the same result. But I found out something! If I comment out the `` line, my "deserialized" objects are now `Nothing, 0` instead of `Nothing, 1`. – Nato Boram Jul 29 '17 at 02:14

1 Answers1

1

I found it, turns out it's in my class Player.

What I used :

Dim Name As String
Dim Score As Double

What worked :

Public Name As String
Public Score As Double

What I should have done :

Public Property Name As String
Public Property Score As Double

I was taught to never set variables as "public" when coding in Java, and I didn't know that Property existed in Visual Basic.

Nato Boram
  • 4,083
  • 6
  • 28
  • 58
  • 1
    It's actually the same advice in Visual Basic. *Don't* make fields public unless there's a compelling reason to do so. If you need to expose data members publicly (which in this case you do), use properties: i.e. `Public Property Name As String`. – Kirill Shlenskiy Jul 29 '17 at 05:06