B4J Question How To Retrieve String Array Value from Map in KeyValueStore

cklester

Well-Known Member
Licensed User
I'm gleaning from the docs and forum posts that a KVS can hold a Map without issues and no special handling.

So, how do I retrieve the string array value from a map stored in a KVS?

The attached project builds a KVS with two maps inside of it, each with String keys and String() values.

On the first run, it creates a new KVS with two maps. On subsequent runs, it opens the KVS.

I cannot figure out how to retrieve back the String() values from the keys, even on the first run.

Any help is very much appreciated!
 

Attachments

  • kvs_map_test_001.zip
    8.5 KB · Views: 195

LucaMs

Expert
Licensed User
Longtime User
B4X:
'This event will be called once, before the page becomes visible.
Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")

    xui.SetDataFolder("kvs_test")
    kvs.Initialize(xui.DefaultFolder,"kvs.dat")
    
    activeMap.Initialize
    archiveMap.Initialize

    'did we load an existing KVS?
    If kvs.ListKeys.Size = 0 Then
        'no
        Log("Building kvs")
        activeMap.Put("A B 3",Array As String("one","two","three","four","five"))
        activeMap.Put("C D 3",Array As String("one","two","three","four","five"))
        activeMap.Put("E F 3",Array As String("one","two","three","four","five"))
        activeMap.Put("G H 3",Array As String("one","two","three","four","five"))
        archiveMap.Put("Z Y 5",Array As String("one","two","three","four","five"))
        kvs.Put("active",activeMap)
        kvs.Put("archive",archiveMap)
    End If

End Sub

Private Sub B4XPage_Appear
    activeMap = kvs.Get("active")
    archiveMap = kvs.Get("archive")
    LogMap("Active", activeMap)
    LogMap("Archive", archiveMap)
End Sub

Private Sub LogMap(Title As String, M As Map)
    Log(Title &  "...")
    Dim sb As StringBuilder
    sb.Initialize
    For Each key As String In m.Keys
        Log(key)
        Dim strArr() As String = activeMap.Get(key)
        If Null = strArr Then Continue
        For Each str As String In strArr
            sb.Append(TAB)
            sb.Append(str)
        Next
    Next
    Log(sb.ToString)
    Log("-------")
End Sub
 
Last edited:
Upvote 0

Brian Dean

Well-Known Member
Licensed User
Longtime User
I have not managed to get @LucaMs suggestion to work, but I did find this elsewhere in the forum (my italics) ...

The supported types of objects are:

Lists, Maps, Strings, primitives (numbers), user defined types and arrays (only arrays of bytes and arrays of objects are supported).
 
Upvote 0

cklester

Well-Known Member
Licensed User
B4X:
Private Sub B4XPage_Created (Root1 As B4XView)
    '...
End Sub

Private Sub B4XPage_Appear
    '...
End Sub

Private Sub LogMap(Title As String, M As Map)
    '...
End Sub

I'm still getting a crash.

B4X:
Waiting for debugger to connect...
Program started.
Building kvs
Active...
A B 3
Error occurred on line: 59 (B4XMainPage)
java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class [Ljava.lang.String; ([Ljava.lang.Object; and [Ljava.lang.String; are in module java.base of loader 'bootstrap')
    at b4j.example.b4xmainpage._logmap(b4xmainpage.java:99)
    at b4j.example.b4xmainpage._b4xpage_appear(b4xmainpage.java:57)
    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.keywords.Common.CallSub4(Common.java:487)
    at anywheresoftware.b4a.keywords.Common.access$0(Common.java:467)
    at anywheresoftware.b4a.keywords.Common$CallSubDelayedHelper.run(Common.java:541)
    at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
    at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
    at java.base/java.lang.Thread.run(Thread.java:834)

I updated the project file in the OP to include @LucaMs modifications.
 
Upvote 0

cklester

Well-Known Member
Licensed User
I have not managed to get @LucaMs suggestion to work, but I did find this elsewhere in the forum (my italics) ...

From that page, it says that Maps and Strings and Arrays and combinations thereof are supported, which gives me hope. However, the stipulation for Arrays only being Bytes or Objects seems to be the problem here. My Maps values cannot save String Arrays, even in a KVS?!

If so, that means I will manually have to handle string arrays... Or... I can put those strings in an object or custom type? What if my custom type has a String Array? Is that always going to disrupt my flow?! :D
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
This works as expected. You can save the strings directly in an object array, and then read the array as object and extract the values as string.

If you store the array as a string array, it can still be read as an object array.

Also note changes to line 39 & 40 of the code below.

B4X:
Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")

    xui.SetDataFolder("kvs_test")
    kvs.Initialize(xui.DefaultFolder,"kvs.dat")
 
    activeMap.Initialize
    archiveMap.Initialize

    'did we load an existing KVS?
    If kvs.ListKeys.Size = 0 Then
        'no
        Log("Building kvs")
        activeMap.Put("A B 3",Array("one","two","three","four","five"))     'activeMap.Put("A B 3",Array As String("one","two","three","four","five")) will still work
        activeMap.Put("C D 3",Array("one","two","three","four","five"))
        activeMap.Put("E F 3",Array("one","two","three","four","five"))
        activeMap.Put("G H 3",Array("one","two","three","four","five"))
        archiveMap.Put("Z Y 5",Array("one","two","three","four","five"))
        kvs.Put("active",activeMap)
        kvs.Put("archive",archiveMap)
    End If

End Sub

Private Sub B4XPage_Appear
    activeMap = kvs.Get("active")
    archiveMap = kvs.Get("archive")
    LogMap("Active", activeMap)
    LogMap("Archive", archiveMap)
End Sub

Private Sub LogMap(Title As String, M As Map)
    Log(Title &  "...")
    Dim sb As StringBuilder
    sb.Initialize
    For Each key As String In m.Keys
        Log(key)
        Dim strArr() As Object = m.Get(key)
        If strArr = Null Then Continue
        For Each str As String In strArr
            sb.Append(TAB)
            sb.Append(str)
        Next
    Next
    Log(sb.ToString)
    Log("-------")
End Sub
 
Last edited:
Upvote 0

cklester

Well-Known Member
Licensed User
Thanks, @stevel05 for the detective work!

This works as expected. You can save the strings directly in an object array, and then read the array as object and extract the values as string.

B4X:
Private Sub B4XPage_Appear
    activeMap = kvs.Get("active")
    archiveMap = kvs.Get("archive")
    LogMap("Active", activeMap)
    LogMap("Archive", archiveMap)
End Sub

Why does kvs.Get() not work in B4XPage_Created but does work in B4XPage_Appear? It doesn't seem like that should matter in this case.
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
It does work in page created, try this:

B4X:
    'did we load an existing KVS?
    If kvs.ListKeys.Size = 0 Then
        'no
        Log("Building kvs")
        activeMap.Put("A B 3",Array As String("one","two","three","four","five"))
        activeMap.Put("C D 3",Array As String("one","two","three","four","five"))
        activeMap.Put("E F 3",Array As String("one","two","three","four","five"))
        activeMap.Put("G H 3",Array As String("one","two","three","four","five"))
        archiveMap.Put("Z Y 5",Array As String("one","two","three","four","five"))
        kvs.Put("active",activeMap)
        kvs.Put("archive",archiveMap)
    Else
        Log("Reading kvs")
        activeMap = kvs.Get("active")
        archiveMap = kvs.Get("archive")
    End If
 
Last edited:
Upvote 0
Top