B4A Library [B4X] NinjaData - Easy-to-use data structure with JSON input/output

Discussion in 'Additional libraries, classes and official updates' started by wonder, Jul 16, 2019.

  1. wonder

    wonder Expert Licensed User

    TL;DR: Please test this B4X Class. Let me know if you find any bugs.

    I've just 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:
    Code:
    '*** 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. **
    Code:
    '*** 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:
    Code:
    '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(123)))
        
    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(2As 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 StringAs Boolean
        
    Return True
    End Sub
    Expected output:
    Code:
    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: Jul 19, 2019
  2. sorex

    sorex Expert Licensed User

    I don't really get what there is to crack as the output is still plain text?
    Maybe I'm missing the point of it.

    Your other 'impossible to crack' thingy was more interesting than this.
     
  3. wonder

    wonder Expert Licensed User

    @sorex, by "breaking" I meant "crash it and/or find any bugs" with it... :D :) Sorry for the misunderstanding.

    So in other words, can you guys give it a "test drive" and see if it's stable enough? It's very easy to miss the corner cases in this kind of library...
     
    Beja likes this.
  4. Enrique Gonzalez R

    Enrique Gonzalez R Well-Known Member Licensed User

    Hi!

    i have a question, why like this:

    Code:
    Select True
            
    Case t == "[i"
    and not like this:

    Code:
    Select t
            
    Case "[i"
     
    wonder likes this.
  5. wonder

    wonder Expert Licensed User

    If that’s where I’m thinking of, then the reason was probably a copy-paste error and being too sleepy to realize it. :D
    Thanks!! I’ll fix it soon. :)
     
    Enrique Gonzalez R likes this.
  6. Enrique Gonzalez R

    Enrique Gonzalez R Well-Known Member Licensed User

    nice! i thought it was some kind of coding grammar.
     
    wonder likes this.
  7. wonder

    wonder Expert Licensed User

    Done! :)
    - Fixed the 'select case' statements
    - Improved the Pop() method to work on every object
     
    Last edited: Jul 17, 2019
  8. Beja

    Beja Expert Licensed User

    This also called torture testing
     
    wonder likes this.
  9. wonder

    wonder Expert Licensed User

    @Erel, I feel this project is stable enough. Can you move it to "Additional libraries, classes and official updates"?
    Thanks in advance! :)
     
    Erel and Cableguy like this.
  10. wonder

    wonder Expert Licensed User

    I've just fixed two bugs, so please remember to check the git repo for updates. :)
     
    José J. Aguilar likes this.
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice