With the recently introduced BigQuery Standard SQL , everything is much better!
Try below (make sure to clear the Use Legacy SQL checkbox in the Show Options section)
WITH parents AS ( SELECT "James" AS parentName, STRUCT( ["name1", "name2", "name3"] AS name, [5, 50, 33] AS age, ["M", "F", "M"] AS gender ) AS children ) SELECT parentName, childrenName, childrenAge, childrenGender FROM parents, UNNEST(children.name) AS childrenName WITH OFFSET AS pos_name, UNNEST(children.age) AS childrenAge WITH OFFSET AS pos_age, UNNEST(children.gender) AS childrenGender WITH OFFSET AS pos_gender WHERE pos_name = pos_age AND pos_name = pos_gender
Here - the source table - parents - below the data

with appropriate schema like
[{ "parentName": "James", "children": { "name": ["name1", "name2", "name3"], "age": ["5", "50", "33" ], "gender": ["M", "F", "M"] } }]
and output is

Note: the above is based solely on what I see in the original question and most likely needs to be adjusted to take into account any specific needs that you have. Hope this helps in terms of direction to go and where to start!
Added:
Above Query uses row-based CROSS JOINS, which means that all options are for the same parent first, and then WHERE clauses filter out the βwrongβ ones.
Unlike below, use INNER JOIN to eliminate this "side effect"
WITH parents AS ( SELECT "James" AS parentName, STRUCT( ["name1", "name2", "name3"] AS name, [5, 50, 33] AS age, ["M", "F", "M"] AS gender ) AS children ) SELECT parentName, childrenName, childrenAge, childrenGender FROM parents, UNNEST(children.name) AS childrenName WITH OFFSET AS pos_name JOIN UNNEST(children.age) AS childrenAge WITH OFFSET AS pos_age ON pos_name = pos_age JOIN UNNEST(children.gender) AS childrenGender WITH OFFSET AS pos_gender ON pos_age = pos_gender
Intuitively, I would expect the second version to be slightly more efficient for large tables