B4J Question Why Can't I Write this KeyValueStore

cklester

Well-Known Member
Licensed User
I'm sure there's a bit of data that isn't compatible with KVS, but all I'm storing are Strings.

Can anybody figure out why this crashes? Maybe it's something simple I have overlooked. :confused:
 

Attachments

  • test_kvs.zip
    1.8 KB · Views: 113

cklester

Well-Known Member
Licensed User
B4X:
Waiting for debugger to connect...
Program started.
Returning from mockNotes with 10 notes.
Error occurred on line: 18 (KeyValueStore)
java.lang.RuntimeException: Cannot serialize object: [note=[data=(MyMap) {Text=This is note #1}, main=null], main=null]
    at anywheresoftware.b4a.randomaccessfile.B4XSerializator.writeType(B4XSerializator.java:275)
    at anywheresoftware.b4a.randomaccessfile.B4XSerializator.writeObject(B4XSerializator.java:241)
    at anywheresoftware.b4a.randomaccessfile.B4XSerializator.writeList(B4XSerializator.java:269)
    at anywheresoftware.b4a.randomaccessfile.B4XSerializator.writeObject(B4XSerializator.java:221)
    at anywheresoftware.b4a.randomaccessfile.B4XSerializator.WriteObject(B4XSerializator.java:121)
    at anywheresoftware.b4a.randomaccessfile.B4XSerializator.ConvertObjectToBytes(B4XSerializator.java:79)
    at b4j.example.keyvaluestore._put(keyvaluestore.java:68)
    at b4j.example.main._appstart(main.java:73)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:632)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:234)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:167)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:91)
    at anywheresoftware.b4a.shell.ShellBA.raiseEvent2(ShellBA.java:98)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:78)
    at b4j.example.main.main(main.java:29)
Program terminated (StartMessageLoop was not called).
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
You cannot serialize class instances (with B4XSerializator).
You can serialize custom types or you can serialize the inner map.

I usually use custom types for this.

However you can do something like:
B4X:
'add to cGenericItem
Public Sub ToBytes As Byte()
    Dim ser As B4XSerializator
    Return ser.ConvertObjectToBytes(Data)
End Sub

Public Sub FromBytes(b() As Byte)
    Dim ser As B4XSerializator
    Data = ser.ConvertBytesToObject(b)
End Sub

B4X:
'add to cNote
Public Sub ToBytes As Byte()
    Return Note.ToBytes
End Sub

Public Sub FromBytes(b() As Byte)
    Note.FromBytes(b)
End Sub

In the Main module:
B4X:
Private Sub SaveListOfNotes (key As String, notes As List)
    Dim slist As List
    slist.Initialize
    For Each note As cNote In notes
        slist.Add(note.ToBytes)
    Next
    data.Put(key, slist)
End Sub

Private Sub LoadListOfNotes(key As String) As List
    Dim slist As List = data.Get(key)
    Dim notes As List
    notes.Initialize
    For Each b() As Byte In slist
        Dim note As cNote
        note.Initialize
        note.FromBytes(b)
        notes.Add(note)
    Next
    Return notes
End Sub
 
Upvote 0

cklester

Well-Known Member
Licensed User
I usually use custom types for this.

Do you have an example you could show me of using custom types like this? I'm trying to think if I'd be giving anything up by putting these "forms" (records?) in a type instead of in a class.

If I'm using custom types, do I no longer need the cGenericItem?
 
Upvote 0

cklester

Well-Known Member
Licensed User
I will have a lot of these types of data forms (at present, 13 item types), and there might even be a provision to allow the user to create his/her own. It seems like I need to stick with classes I can build on the fly, as I don't think types will let me do that. Maybe you have a trick around that.

With the method you suggest above, I would have to have a separate SaveListOf<T> for each class type I implement. That somewhat defeats the purpose of my setting it all up this way, unless there's a way to create a sub or class that lets me SaveListOfItems(key as string, items as List, itemType as <T>):

B4X:
Private Sub SaveListOfItems (key As String, items As List, <T> )
    Dim slist As List
    slist.Initialize
    For Each item As <T> In items
        slist.Add(item.ToBytes)
    Next
    data.Put(key, slist)
End Sub

but how do I pass to this Save() function the class/type I'm saving?
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
A combination of duck typing and callsub. The class has a save method and then you can call the save method via callsub.
 
Upvote 0

cklester

Well-Known Member
Licensed User
Ah, yes! Duck typing. I actually was reading about that a little bit this past week, due to this same project. I figured I could use duck typing, but I forgot about the callsub mechanism. Thanks for the heads up and reminder! ?

I will look more closely at this sometime this weekend.
 
Upvote 0

cklester

Well-Known Member
Licensed User
So, Erel's code above has one issue for my attempted usecase:

B4X:
Private Sub LoadListOfItems(key As String) As List
    Dim slist As List = App.userData.Get(key)
    Dim items As List
    items.Initialize
    For Each b() As Byte In slist

        Dim item As cNote ' <-- I don't know what class this will be. Could be one of many.

        item.Initialize
        item.FromBytes(b)
        items.Add(item)
    Next
    Return items
End Sub

Right now, cNote is just one class of 13 possible classes. How do I make this function able to handle a variety of classes? I want to avoid creating a special function to load each and every class type (because, of course, it defeats the purpose of creating this system to be dynamic).
 
Upvote 0
Top