hmztl976
hmztl976

Reputation: 23

Is there a way to type an object parsed from json in qml

The json document is like this. I want to define a class for it in qml, just like the interface keyword in typescript does.

{
    "ScannerID": "ID",
    "Status": 1,
    "SuccessRate": 0.999,
    "Result": [{
            "Codes": "result_11111",
            "Positions": {
                "CenterX": 10.0,
                "CenterY": 10.0,
                "Width": 100.0,
                "Height": 100.0,
                "Angle": 50
            },
            "Types": "QrCode"
        },
        //more items
    ]
}

I tried to define a qml class in file ScannerResult.qml

import QtQuick 2.4

QtObject {
    property string pScannerID
    property int pStatus
    property real pSuccessRate
    // how to define Result with strong type?
    property ? pResult

    function load(obj) {
        pScannerID = obj.ScannerID
        //......
    }
}

then use it

...
ScannerResult {
    id: scannerResult
}

function log(jsonstr) {
    let obj = JSON.parse(jsonstr);
    scannerResult.load(obj)
    console.log(scannerResult.pScannerID) // it works
}
...

But it's hard to handle the vector of objects under "Result" key. Because qml doesn't allow to define class B under class A.
So does anyone have a good idea about how to define a strong-typed class to hold the object parsed from json in qml? Then I can use that class with auto-completion in qtcreator.

Upvotes: 2

Views: 2073

Answers (1)

fallerd
fallerd

Reputation: 1738

You can create a property var and assign it as a list/array/vector to hold pResult. To populate this list, you can create a "class B" Component, and dynamically create instances of this class them when parsing your JSON object.

Using your code structure, here is an updated version of ScannerResult.qml:

import QtQuick 2.4

QtObject {
    property string pScannerID
    property int pStatus
    property real pSuccessRate
    property var pResult: []

    function load(obj) {
        pScannerID = obj.ScannerID
        //... etc
        for (var element of obj.Result) {
            let resultQtObject = resultComponent.createObject(this, {})
            resultQtObject.pCodes = element.Codes
            resultQtObject.pTypes = element.Types
            resultQtObject.pPositions.pCenterX = element.Positions.CenterX
            //... etc
            pResult.push(resultQtObject)
        }
    }

    property Component resultComponent: Component {

        QtObject {
            property string pCodes
            property string pTypes
            property QtObject pPositions: QtObject {
                property real pCenterX
                property real pCenterY
                property real pWidth
                property real pHeight
                property int pAngle
            }
        }
    }
}

Your updated log function works as follows:

    function log(jsonstr) {
        let obj = JSON.parse(jsonstr);
        scannerResult.load(obj)
        console.log(scannerResult.pScannerID) // it works
        console.log(scannerResult.pResult[0].pPositions.pCenterX) // it works
    }

Because you are dynamically creating instances of this Component at runtime, QtCreator cannot make auto-completion suggestions for it. As you mentioned your vector is unknown length, I believe that runtime instantiation is your only option, and thus there is no solution that can accommodate auto-completion.

One other thing to note is that if you are re-running log()/load() many times you will need to clean the pResult list and destroy() the dynamically created objects.

Upvotes: 1

Related Questions