Trouble reading from JSObject created from XML

I'm trying to use a flow to process an XML generated by GXWorks3 for industrial PLCs into some sort of array for use in more complex flows. I've puzzled over it for a couple days, tried ChatGPT, and finally turn to the forum in hopes of help.

flow:

Flow XML to JSON - Pastebin.com

Input is a massive XML with a lot of extra nonsense, we chop it down to the bit we need, then operate on that using a function that should output a JSON (or JSObject, I'm not sure, not a JS developer), similar to this format: Desired output format - Pastebin.com.

After much experimentation, I'm trying:

var arr = msg.payload; // get the array
var rv = {};// create a new empty object

for (var i = 0; i < arr.length; ++i) {
    let key = msg.payload[i].variable[i].$.name;
    rv[key] = {
        address: msg.payload[i].variable[i].$.address, 
        array_data_type_name: "", // empty
        fc: '', // empty, change sign?
        quantity: 1, // empty
        type_name: Object.keys(msg.payload[i].variable[i].type[0]), //object keys
        word_length: 1 // empty
    }; // replace arr[i] with selected pieces  
}

msg.payload = rv;
return msg;

based off examples from elsewhere in the forum, but it chokes at "$", (or elsewhere, if I eliminate that) with "TypeError: Cannot read properties of undefined (reading '$')".

I've done hobby web development and programming at university, but am not a professional JS developer. Can anyone please help me solve this problem?

The variable array are different lengths. so your for loop works for payload but not for the variable array.
also some variable arrays do not contain address.
try this

let arr = msg.payload; // get the array
let rv = {};// create a new empty object
arr.forEach(obj_payload =>{
    if(obj_payload.variable){
        obj_payload.variable.forEach(obj_variable => {
            let key = obj_variable.$.name;
            if(obj_variable.$.address){
                rv[key] = {
                    address: obj_variable.$.address, 
                    array_data_type_name: "", // empty
                    fc: '', // empty, change sign?
                    quantity: 1, // empty
                    type_name: Object.keys(obj_variable.type[0]), //object keys
                    word_length: 1 // empty
                }; // replace arr[i] with selected pieces  
            }
        })
    }
});
msg.payload = rv;
return msg;

1 Like

That is awesome and works very well! I experimented with my boss until late yesterday, and we were approaching a solution, but this is beyond what we did and has solved several issues I had yet to solve (e.g. irregularly nested names). Thank you so much!

Once I've figured out how to adjust type_name: Object.keys(obj_variable.type[0]) to return BOOL instead of 0: 'BOOL', I reckon it'll be 100% sorted. Wondering if it is maybe impossible in JS?

E1cid's solution works, I only edited type_name to:

type_name: Object.keys(obj_variable.type[0]).toString(),

which will pick out BOOL, INT, etc. from the horridly formatted XML. This was based off array to string on MDN.

My original code was inspired by this post by Steve-Mcl (and I just noticed you chimed in with a similar fix back then, too, E1cid!)

Thank you so much!

I thought that may be wrong and left it for you to figure out

Object.keys(obj_variable.type[0])[0]

should work, if only 1 item.
Else

Object.keys(obj_variable.type[0]).join(",")

If it returns more than 1 then the text will be one,two

1 Like

BOOL array
Future readers, that's basically what it looks like, and unlike getting the values of the object fields for the rest of the XML, the type_name field is malformed and we use the above methods to get field name "BOOL" or "INT" etc. (array value is always empty)

Thank you so much for helping me!

Would var look_up_table = Object(msg.payload); be enough in the next function node to get a JS object of the correctly generated object from the above? EDIT: after logging to console, yes, it appears it is.

I'm happy, the bit of the program I was working on now seems to be 100% functional.

There are some other flows and stuff connected that still have issues; not sure why, but they're in the general program, not in this section. Odd that it isn't 100% yet, as the generated data from this topic's node looks the same as one of our working installations.

Turns out, there were some undocumented variables that I needed to account for that no-one informed me on. The corrected code:

let arr = msg.payload; // get the array
let rv = {}; // create a new empty object
let fc = []; 

arr.forEach(obj_payload => {
    if (obj_payload.variable) {
        obj_payload.variable.forEach(obj_variable => {
                let key = obj_variable.$.name;
                if (obj_variable.$.address) {

                    // assign function code depending on D or M (other) prefix
                    if (obj_variable.$.address.indexOf('D') > -1) {
                        fc = 1;
                    } else {
                        fc = 3;
                    }

                    // strip D/M from head of address
                    obj_variable.$.address = obj_variable.$.address.substring(1); 

                    // populate the rest of the JSObject
                    rv[key] = {
                        address: obj_variable.$.address, // addresses, trimmed of letters
                        array_data_type_name: "", // empty
                        fc: fc, // function code
                        quantity: 1, // empty
                        type_name: Object.keys(obj_variable.type[0]).toString(), //object keys
                        word_length: 1, // empty
                        unitid: 255 // previously added by lvc_labels
                    }; // replace arr[i] with selected pieces  
            }
        })
    }
});

msg.payload = rv;
return msg;

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.