TL;DR: Please test this B4X Class. Let me know if you find any bugs.
I'vejust finished (work-in-progress) developing a B4X data storage class (for my own needs).
It's a JSON-like object container that's loosely based on Python lists and dictionaries.
Can you guys give it a test drive?
GitLab repository: https://gitlab.com/brunowonder/ninjadata
Direct link: https://gitlab.com/brunowonder/ninjadata/-/archive/master/ninjadata-master.zip
How does it work:
- Initialize it with a data source (text or object)
- There are 4 main methods: get(), set(), push() and pop()
- Navigate the object like you would navigate a file system
Limitations:
- While you can add custom types (given by the Type keyword), class based objects are not supported (with the exception of NinjaData itself)
- Arrays (as opposed to Lists) of custom types are not supported (see section J in the example code).
- It's still kinda buggy (work-in-progress)
Comparison:
** VS. **
Let's have a look at the test module and its output for understanding what it does:
Expected output:
I've
It's a JSON-like object container that's loosely based on Python lists and dictionaries.
Can you guys give it a test drive?
GitLab repository: https://gitlab.com/brunowonder/ninjadata
Direct link: https://gitlab.com/brunowonder/ninjadata/-/archive/master/ninjadata-master.zip
How does it work:
- Initialize it with a data source (text or object)
- There are 4 main methods: get(), set(), push() and pop()
- Navigate the object like you would navigate a file system
Limitations:
- While you can add custom types (given by the Type keyword), class based objects are not supported (with the exception of NinjaData itself)
- Arrays (as opposed to Lists) of custom types are not supported (see section J in the example code).
- It's still kinda buggy (work-in-progress)
Comparison:
B4X:
'*** Vanilla B4X ***
'Example A
Dim permissions As List
permissions.Initialize
permissions.Add("read")
permissions.Add("write")
permissions.Add("execute")
Dim john As Map
john.Initialize
john.Put("Permissions", permissions)
Dim users As Map
users.Initialize
users.Put("John", john)
Dim system As Map
system.Initialize
system.Put("Users", users)
system.Put("Name", "Workstation")
'Example B:
Dim users As Map = system.Get("Users")
Dim john As Map = users.Get("John")
Dim permissions As List = john.Get("Permissions")
permissions.add("modify")
** VS. **
B4X:
'*** NinjaData ***
'Example A:
Dim System as NinjaData
system.Initialize($"
{
"Name": "Workstation",
"Users": {
"John": {
"Permissions": [
"read",
"write",
"execute"
]
}
}
}
"$)
'Example B:
system.Push("/Users/John/Permissions", "modify")
Let's have a look at the test module and its output for understanding what it does:
B4X:
'Non-UI application (console / server application)
#Region Project Attributes
#CommandLineArgs:
#MergeLibraries: True
#End Region
Sub Process_Globals
Type Person(name As String, age As Int)
End Sub
Sub AppStart (Args() As String)
'https://json.org/example.html
Dim json_str = $"
{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S",
"GlossList": {
"GlossEntry": {
"ID": "SGML",
"SortAs": "SGML",
"GlossTerm": "Standard Generalized Markup Language",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": ["GML", "XML"]
},
"GlossSee": "markup"
}
}
}
}
} "$ As String
Dim src As NinjaData
Dim dst As NinjaData
'Load the object from a JSON string
Log("A ===============================================")
src.Initialize(json_str)
Log(src.PrettyJSON)
'Deep copy the 'src' object using the get command
Log("B ===============================================")
dst.Initialize(src.Get("/"))
Log(dst.PrettyJSON)
'Destroy the content of the 'src' object using the set command
Log("C ===============================================")
src.Set("/", Null)
Log(src.PrettyJSON)
'Deep copy the 'dst' object without using the get command
Log("D ===============================================")
src.Initialize(dst)
Log(src.PrettyJSON)
'Destroy the 'src' object
Log("E ===============================================")
src = Null
Log(dst.PrettyJSON) 'The 'dst' object isn't affected
'Let's have some fun
Log("F ===============================================")
dst.ShowWarnings = True
Log(dst.Pop ("/glossary/GlossDiv/GlossList/GlossEntry/GlossDef/GlossSeeAlso/0"))
Log(dst.Push("/glossary/GlossDiv/GlossList/GlossEntry/GlossDef/GlossSeeAlso", "EXTRA ITEM 1"))
Log(dst.Push("/glossary/GlossDiv/GlossList/GlossEntry/GlossDef/GlossSeeAlso", Array As Float(1, 2, 3)))
Log(dst.Get("/glossary/GlossDiv/GlossList/GlossEntry/GlossDef/GlossSeeAlso/2/0"))
Log(dst.Pop("/glossary/GlossDiv/GlossList/GlossEntry/GlossDef/GlossSeeAlso/2/0"))
Log(dst.Push("/glossary/GlossDiv/GlossList/GlossEntry/GlossDef/GlossSeeAlso/2", "EXTRA ITEM 2"))
Log(dst.Set("/glossary/GlossDiv/GlossList/GlossEntry/GlossDef/GlossSeeAlso/2", "EXTRA ITEM 3"))
Log(dst.Set("/glossary/GlossDiv/GlossList/GlossEntry/ID", "NEW VALUE 1"))
Log(dst.Get("/glossary/GlossDiv/GlossList/GlossEntry/Bananas"))
Log(dst.Pop("/glossary/GlossDiv/GlossList/GlossEntry/Bananas"))
'Trigger some errors
Log("G ===============================================")
dst.IgnoreErrors = True
Log(dst.Push("/glossary/GlossDiv/GlossList/GlossEntry/GlossDef/GlossSeeAlso/2/ok", "THIS WILL FAIL"))
Log(dst.Push("/glossary/GlossDiv/GlossList/GlossEntry/GlossDef/", "THIS WILL FAIL"))
Log(dst.Set("/glossary/GlossDiv/GlossList/GlossEntry/GlossDef/GlossSeeAlso/23/", "THIS WILL FAIL"))
Log(dst.Get("/glossary/GlossDiv/GlossList/GlossEntry/GlossDef/GlossSeeAlso/23/"))
Log(dst.PrettyJSON)
'Use a custom type as an input source
Log("H ===============================================")
Dim p1 As Person
p1.Initialize
p1.name = "John"
p1.age = 25
Dim myObj As NinjaData
myObj.Initialize(p1)
Log(myObj.PrettyJSON)
'Data complexity is preserved
Log("I ===============================================")
Dim p2 As Person
p2.Initialize
p2.name = "Kate"
p2.age = 30
Dim myObj As NinjaData
myObj.Initialize("{}")
myObj.Set("user", p2)
Log(myObj.PrettyJSON)
Log(GetType(myObj.Get("/user"))) 'Data type is preserved ($_person)
Dim p3 = myObj.Get("/user") As Person
Log(p3.name) 'Kate
'Destroy data complexity
myObj.ReloadAsJSONString 'Object is converted to JSON string and reloaded
Log(GetType(myObj.Get("/user"))) 'Data type is destroyed (converted to Map)
Try
Dim p4 = myObj.Get("/user") As Person
Log(p4.name) 'Error
Catch
LogError(LastException)
End Try
'Arrays of objects are NOT supported
Log("J ===============================================")
Dim arr(2) As Person
arr(0) = p1
arr(1) = p2
'This doesn't work...
Dim arr_persons As NinjaData
arr_persons.Initialize(arr)
Log(arr_persons.PrettyJSON)
'...but this does!
Dim lst_persons As NinjaData
lst_persons.Initialize("[]")
For Each p As Person In arr
lst_persons.Push("/", p)
Next
Log(lst_persons.PrettyJSON)
'Remove using pop
Log("J ===============================================")
Log(lst_persons.Pop("1"))
Log(lst_persons.PrettyJSON)
Log(dst.Pop("/glossary/GlossDiv"))
Log(dst.PrettyJSON)
Log(dst.Pop("/"))
Log(dst.PrettyJSON)
End Sub
'Return true to allow the default exceptions handler to handle the uncaught exception.
Sub Application_Error (Error As Exception, StackTrace As String) As Boolean
Return True
End Sub
Expected output:
B4X:
A ===============================================
{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"GlossList": {
"GlossEntry": {
"GlossTerm": "Standard Generalized Markup Language",
"GlossSee": "markup",
"SortAs": "SGML",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": [
"GML",
"XML"
]
},
"ID": "SGML",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986"
}
},
"title": "S"
}
}
}
B ===============================================
{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"GlossList": {
"GlossEntry": {
"GlossTerm": "Standard Generalized Markup Language",
"GlossSee": "markup",
"SortAs": "SGML",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": [
"GML",
"XML"
]
},
"ID": "SGML",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986"
}
},
"title": "S"
}
}
}
C ===============================================
null
D ===============================================
{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"GlossList": {
"GlossEntry": {
"GlossTerm": "Standard Generalized Markup Language",
"GlossSee": "markup",
"SortAs": "SGML",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": [
"GML",
"XML"
]
},
"ID": "SGML",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986"
}
},
"title": "S"
}
}
}
E ===============================================
{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"GlossList": {
"GlossEntry": {
"GlossTerm": "Standard Generalized Markup Language",
"GlossSee": "markup",
"SortAs": "SGML",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": [
"GML",
"XML"
]
},
"ID": "SGML",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986"
}
},
"title": "S"
}
}
}
F ===============================================
GML
[XML, EXTRA ITEM 1]
[XML, EXTRA ITEM 1, [F@1f32e575]
1.0
1.0
[2.0, 3.0, EXTRA ITEM 2]
[XML, EXTRA ITEM 1, EXTRA ITEM 3]
{GlossTerm=Standard Generalized Markup Language, GlossSee=markup, SortAs=SGML, GlossDef={para=A meta-markup language, used to create markup languages such as DocBook., GlossSeeAlso=[XML, EXTRA ITEM 1, EXTRA ITEM 3]}, ID=NEW VALUE 1, Acronym=SGML, Abbrev=ISO 8879:1986}
null
[WARNING] NinjaData: Key 'Bananas' not found - Null object returned.
[WARNING] NinjaData: Key 'Bananas' not found - Null object returned.
null
G ===============================================
[ERROR] NinjaData: Target object (String) is not a List!
null
[ERROR] NinjaData: Target object (Map) is not a List!
null
[ERROR] NinjaData: Index #23 out-of-bounds (list size is 3)!
[XML, EXTRA ITEM 1, EXTRA ITEM 3]
[WARNING] NinjaData: Index #23 out-of-bounds (list size is 3) - Null object returned.
null
{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"GlossList": {
"GlossEntry": {
"GlossTerm": "Standard Generalized Markup Language",
"GlossSee": "markup",
"SortAs": "SGML",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": [
"XML",
"EXTRA ITEM 1",
"EXTRA ITEM 3"
]
},
"ID": "NEW VALUE 1",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986"
}
},
"title": "S"
}
}
}
H ===============================================
{
"name": "John",
"IsInitialized": "true",
"age": "25"
}
I ===============================================
{
"user": {
"name": "Kate",
"IsInitialized": "true",
"age": "30"
}
}
b4j.example.main$_person
Kate
anywheresoftware.b4a.objects.collections.Map$MyMap
(ClassCastException) java.lang.ClassCastException: anywheresoftware.b4a.objects.collections.Map$MyMap cannot be cast to b4j.example.main$_person
J ===============================================
[
"ERROR: Unsupported array type (_person)"
]
[
{
"name": "John",
"IsInitialized": "true",
"age": "25"
},
{
"name": "Kate",
"IsInitialized": "true",
"age": "30"
}
]
J ===============================================
[IsInitialized=true, name=Kate, age=30
]
[
{
"name": "John",
"IsInitialized": "true",
"age": "25"
}
]
{GlossList={GlossEntry={GlossTerm=Standard Generalized Markup Language, GlossSee=markup, SortAs=SGML, GlossDef={para=A meta-markup language, used to create markup languages such as DocBook., GlossSeeAlso=[XML, EXTRA ITEM 1, EXTRA ITEM 3]}, ID=NEW VALUE 1, Acronym=SGML, Abbrev=ISO 8879:1986}}, title=S}
{
"glossary": {
"title": "example glossary"
}
}
{glossary={title=example glossary}}
null
Last edited: