B4J Question ReadList, WriteList compatibility

Didier9

Well-Known Member
Licensed User
Longtime User
I seem to be having problems reading a list that I just saved using custom type.
B4X:
Type ItemType( Ndx As Int, Name As String, Addr As Int, lstBits As List, lstTlms As List, ... )
   
Dim item as ItemType
item.Initialize
Dim lst as List
lst.Initialize
' store strings, lists and numbers in Item
item.Ndx = 0 ' <- a number
item.Name = "abcd" ' Note: Name is read from an MSDOS formatted file and may include the CRLF
item.Addr = 2 ' <- a number
...
' add Item to the list
lst.Add( Item )
' save to disk
File.WriteList( dir, file, lst )

' sometime later
Dim lst as list
lst = File.ReadList( dir, file )
Log( item.Addr ) <= crashes on that statement: "Error evaluating expression."
This is what the list looks like in a "Unix-aware" text editor (TextPad):
[IsInitialized=true, Ndx=0, Name=PS0
, Addr=1, lstBits=(ArrayList) [[IsInitialized=true, Row=3, Col=17
, Description=, MsgTrue= Run, MsgFalse=Ready
, colorTrue=false, colorFalse=false, ByteNum=1
, OrMask=1, AndMask=1, Fault=false
], [IsInitialized=true, Row=3, Col=25
, Description=, MsgTrue= Run, MsgFalse=Ready
, colorTrue=false, colorFalse=false, ByteNum=3
, OrMask=4, AndMask=4, Fault=false
], [IsInitialized=true, Row=3, Col=54
, Description=, MsgTrue=Flt, MsgFalse= Ok
, colorTrue=true, colorFalse=false, ByteNum=2
, OrMask=32, AndMask=32, Fault=false
], [IsInitialized=true, Row=3, Col=58
, Description=, MsgTrue=Flt, MsgFalse= Ok
, colorTrue=true, colorFalse=false, ByteNum=3
, OrMask=16, AndMask=16, Fault=false
], [IsInitialized=true, Row=3, Col=62
, Description=, MsgTrue=Flt, MsgFalse= Ok
, colorTrue=true, colorFalse=false, ByteNum=2
, OrMask=16, AndMask=16, Fault=false
], [IsInitialized=true, Row=3, Col=66
, Description=, MsgTrue=Flt, MsgFalse= Ok
, colorTrue=true, colorFalse=false, ByteNum=1
, OrMask=2, AndMask=2, Fault=false
], [IsInitialized=true, Row=3, Col=71
, Description=, MsgTrue=Mnt, MsgFalse=Usr
, colorTrue=true, colorFalse=false, ByteNum=2
, OrMask=8, AndMask=8, Fault=false
]], lstTlms=(ArrayList) [[IsInitialized=true, Row=3, Col=9
, Description=, Format=#####, Cmd=I
, Logging=false], [IsInitialized=true, Row=3, Col=32
, Description=, Format=#####, Cmd=6
, Logging=false], [IsInitialized=true, Row=3, Col=37
, Description=, Format=#####, Cmd=7
, Logging=false], [IsInitialized=true, Row=3, Col=47
, Description=, Format=#####, Cmd=>
, Logging=false]]
, tmoOnStatus=false, DeviceID= Device 0]
It looks like there are a number of line breaks that "break" the ReadList function.
The data I feed it should not include any carriage return even though it is possible that the Name parameter has one. For sure, the Col parameter does not include one since it is declared as an Int yet the map file showing in the text editor shows line breaks after each Col = xx parameter.
Here is the error log for the line that causes the crash:
Error occurred on line: 2320
java.lang.RuntimeException: Field: Addr not found in: java.lang.String
at anywheresoftware.b4a.shell.Shell$FieldCache.getField(Shell.java:832)
at anywheresoftware.b4a.shell.Shell.getField(Shell.java:596)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:246)
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.debug.Debug.delegate(Debug.java:64)
at b4j.r9_b4j.main._addressmapget(main.java:340)
at b4j.r9_b4j.main$ResumableSub_mControlScreen_Action.resume(main.java:7466)
at b4j.r9_b4j.main._mcontrolscreen_action(main.java:7348)
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:237)
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)
I put the file into a binary viewer and got this:
B4X:
0000-0010:  5b 49 73 49-6e 69 74 69-61 6c 69 7a-65 64 3d 74  [IsIniti alized=t
0000-0020:  72 75 65 2c-20 4e 64 78-3d 30 2c 20-4e 61 6d 65  rue,.Ndx =0,.Name
0000-0030:  3d 50 53 30-0a 2c 20 41-64 64 72 3d-31 2c 20 6c  =PS0.,.A ddr=1,.l
0000-0040:  73 74 42 69-74 73 3d 28-41 72 72 61-79 4c 69 73  stBits=( ArrayLis
0000-0050:  74 29 20 5b-5b 49 73 49-6e 69 74 69-61 6c 69 7a  t).[[IsI nitializ
0000-0060:  65 64 3d 74-72 75 65 2c-20 52 6f 77-3d 33 2c 20  ed=true, .Row=3,.
0000-0070:  43 6f 6c 3d-31 37 0a 2c-20 44 65 73-63 72 69 70  Col=17., .Descrip
0000-0080:  74 69 6f 6e-3d 2c 20 4d-73 67 54 72-75 65 3d 20  tion=,.M sgTrue=.
So the Name parameter is actually terminated with 0x0A which is unexpected but maybe explainable because it came from a file in MSDOS format (0x0D, 0x0A) but even more strange is that the Col parameter is also followed with 0x0A even though it is defined as an Int, not a String.
I must be missing something...
Anyhow, I wanted to save the list and retrieve it as-is to save time, I am surprised that I have errors reading a list I just saved.
This is under Windows 10 64 bits using OpenJDK-11.0.1
TIA
 

Didier9

Well-Known Member
Licensed User
Longtime User
I created a much smaller custom type with 3 members and it shows the same symptom (adding 0a characters in the output):

B4X:
Type MapType( Ndx As Int, Addr As Int, DeviceID As String )

Dim map As MapType
Dim lstout as List
lstout.Initialize
map.Initialize
map.Ndx = itm.Ndx ' an Int
map.Addr = itm.Addr ' an Int
map.DeviceID = itm.DeviceID.Replace( Chr(10),"").Replace(Chr(13),"") ' a string
lstout.Add( map )
Output:
B4X:
0000-0010:  5b 49 73 49-6e 69 74 69-61 6c 69 7a-65 64 3d 74  [IsIniti alized=t
0000-0020:  72 75 65 2c-20 4e 64 78-3d 30 2c 20-41 64 64 72  rue,.Ndx =0,.Addr
0000-0030:  3d 31 0a 2c-20 44 65 76-69 63 65 49-44 3d 20 44  =1.,.Dev iceID=.D
0000-0040:  65 76 69 63-65 20 30 5d-0a 5b 49 73-49 6e 69 74  evice.0] .[IsInit
0000-0050:  69 61 6c 69-7a 65 64 3d-74 72 75 65-2c 20 4e 64  ialized= true,.Nd
0000-0060:  78 3d 30 2c-20 41 64 64-72 3d 32 0a-2c 20 44 65  x=0,.Add r=2.,.De
0000-0070:  76 69 63 65-49 44 3d 20-44 65 76 69-63 65 20 31  viceID=. Device.1
0000-0080:  5d 0a 5b 49-73 49 6e 69-74 69 61 6c-69 7a 65 64  ].[IsIni tialized
0000-0090:  3d 74 72 75-65 2c 20 4e-64 78 3d 30-2c 20 41 64  =true,.N dx=0,.Ad
0000-00a0:  64 72 3d 33-0a 2c 20 44-65 76 69 63-65 49 44 3d  dr=3.,.D eviceID=
0000-00b0:  20 44 65 76-69 63 65 20-32 5d 0a 5b-49 73 49 6e  .Device. 2].[IsIn
0000-00c0:  69 74 69 61-6c 69 7a 65-64 3d 74 72-75 65 2c 20  itialize d=true,.
0000-00d0:  4e 64 78 3d-30 2c 20 41-64 64 72 3d-34 0a 2c 20  Ndx=0,.A ddr=4.,.
0000-00e0:  44 65 76 69-63 65 49 44-3d 20 44 65-76 69 63 65  DeviceID =.Device
0000-00f0:  20 33 5d 0a-5b 49 73 49-6e 69 74 69-61 6c 69 7a  .3].[IsI nitializ
0000-0100:  65 64 3d 74-72 75 65 2c-20 4e 64 78-3d 30 2c 20  ed=true, .Ndx=0,.
 
Upvote 0

xulihang

Active Member
Licensed User
Longtime User
I think it is better to use B4XSerializator or KeyValueStore in your situation.
 
Upvote 0

Didier9

Well-Known Member
Licensed User
Longtime User
I think it is better to use B4XSerializator or KeyValueStore in your situation.

Thank you. I know there are a number of alternative ways I can do this, but since I start with a list of custom types and I need to end with a list of the same custom types, I thought I would use the functions specifically intended to save and retrieve lists... It sounded too easy :)

At the moment, I wrote a custom routine that reads what was saved by the WriteList method since it does put all the data I need in the file. That works but it's not very elegant and it does not answer the question as to why the WriteList and ReadList methods are not compatible and symmetrical.

Also I do not know what format exactly the ReadList method expects. The format of the file saved by WriteList looks messed up, with unmatched square brackets and seemingly random line feeds, so I have to extract the data and reformat it for my list, a dozen lines of code.

Since I do not need all the data contained in the original list, I create a subset of what I need (3 parameters for each record), which makes it A LOT easier to find what I need when I read it.
 
Upvote 0
Top