使用PHP将XML解析为CSV时,无法获取所有节点数据

I have to parse the data from XML file to CSV using PHP. I have written the code in PHP, it gets only few details and it does not extract the node's details if it has children. I am testing with smaller part of XML, the original file has very large amount of data around 20,000 person nodes. Below is my XML data --> list.xml.

<persons>
    <person>
        <id>326324</id>
        <lastName>ABC</lastName>
        <firstName>XYZ</firstName>
        <middleName>PQR</middleName>
        <preferredfirstName></preferredfirstName>
        <details>
            <JobTitle>Engineer</JobTitle>
            <Dept>Healthcare</Dept>
            <emp_position_no>PT0970</emp_position_no>
            <emp_class_code>AJ</emp_class_code>
            <emp_class_desc>Developer</emp_class_desc>
            <emp_rank></emp_rank>
            <CA_AddrStreet></CA_AddrStreet>
            <CA_AddrCity></CA_AddrCity>
            <CA_AddrState></CA_AddrState>
            <CA_AddrZip></CA_AddrZip>
            <CA_Phone></CA_Phone>
            <EmployeeEmail>ABC@ABDA.COM</EmployeeEmail>
        </details>
        <Projects>
            <subj_crse>MKTG 311</subj_crse>
            <subj_crse>MKTG 428</subj_crse>
        </Projects>
    </person>
    <person>
        <id>956197</id>
        <lastName>YTRG</lastName>
        <firstName>WDES</firstName>
        <middleName>BVCX</middleName>
        <preferredfirstName></preferredfirstName>
        <details>
            <JobTitle>TECHNICIAN</JobTitle>
            <Dept>Education</Dept>
            <emp_position_no>PT1010</emp_position_no>
            <emp_class_code>AJ</emp_class_code>
            <emp_class_desc>Technician</emp_class_desc>
            <emp_rank></emp_rank>
            <CA_AddrStreet></CA_AddrStreet>
            <CA_AddrCity></CA_AddrCity>
            <CA_AddrState></CA_AddrState>
            <CA_AddrZip></CA_AddrZip>
            <CA_Phone></CA_Phone>
            <EmployeeEmail>TRED@HDSEYY.COM</EmployeeEmail>
        </details>
        <Projects>
            <subj_crse>TCHCS 321</subj_crse>
        </Projects>
    </person>
</persons>

I am able to retrieve the data till the node preferredfirstName and after that since details node has further children I am unable to extract the data. Below is my PHP code:

   $xml = simplexml_load_file('list.xml');
   $i = 1;           
   $values = [];     

   $columns = array('id', 'lastName', 'firstName',
                    'middleName', 'preferredfirstName', 'details', 'Projects');

   $fs = fopen('ODU.csv', 'w');
   fputcsv($fs, $columns);      
   fclose($fs);

   $node = $xml->xpath('//person');

   foreach ($node as $n) {           


       $child = $xml->xpath('//person['.$i.']/*');      

       foreach ($child as $value) {
          $values[] = $value;         
       }


       $fs = fopen('test.csv', 'a');
       fputcsv($fs, $values);      
       fclose($fs);  

       $values = [];    
       $i++;            
   }

I need the output in CSV file with each node details under separate colums like below: enter image description here

I never use simplexml but I would guess you could easily accomplish the same thing using that rather than DOMDocument as below - I don't see, in this case, any real need for the XPath query - it's all quite straightforward XML. Hopefully you'll be able to make use of the following somehow.

$strxml='<persons>
            <person>
                <id>326324</id>
                <lastName>ABC</lastName>
                <firstName>XYZ</firstName>
                <middleName>PQR</middleName>
                <preferredfirstName></preferredfirstName>
                <details>
                    <JobTitle>Engineer</JobTitle>
                    <Dept>Healthcare</Dept>
                    <emp_position_no>PT0970</emp_position_no>
                    <emp_class_code>AJ</emp_class_code>
                    <emp_class_desc>Developer</emp_class_desc>
                    <emp_rank></emp_rank>
                    <CA_AddrStreet></CA_AddrStreet>
                    <CA_AddrCity></CA_AddrCity>
                    <CA_AddrState></CA_AddrState>
                    <CA_AddrZip></CA_AddrZip>
                    <CA_Phone></CA_Phone>
                    <EmployeeEmail>ABC@ABDA.COM</EmployeeEmail>
                </details>
                <Projects>
                    <subj_crse>MKTG 311</subj_crse>
                    <subj_crse>MKTG 428</subj_crse>
                </Projects>
            </person>
            <person>
                <id>956197</id>
                <lastName>YTRG</lastName>
                <firstName>WDES</firstName>
                <middleName>BVCX</middleName>
                <preferredfirstName></preferredfirstName>
                <details>
                    <JobTitle>TECHNICIAN</JobTitle>
                    <Dept>Education</Dept>
                    <emp_position_no>PT1010</emp_position_no>
                    <emp_class_code>AJ</emp_class_code>
                    <emp_class_desc>Technician</emp_class_desc>
                    <emp_rank></emp_rank>
                    <CA_AddrStreet></CA_AddrStreet>
                    <CA_AddrCity></CA_AddrCity>
                    <CA_AddrState></CA_AddrState>
                    <CA_AddrZip></CA_AddrZip>
                    <CA_Phone></CA_Phone>
                    <EmployeeEmail>TRED@HDSEYY.COM</EmployeeEmail>
                </details>
                <Projects>
                    <subj_crse>TCHCS 321</subj_crse>
                </Projects>
            </person>
        </persons>';

$dom=new DOMDocument;
$dom->loadXML( $strxml );
/*
    to load a file rather than a string of xml
    ------------------------------------------
    $dom->load( realpath( 'list.xml' ) );
*/



/* file handles */
$output=__DIR__.'\odu.csv';
$tmp=tempnam( sys_get_temp_dir(), 'csv' );
$handle=fopen( $tmp, 'r+' );



$col=$dom->getElementsByTagName('person');

if( $col ){
    foreach( $col as $person ){
        if( $person->nodeType==XML_ELEMENT_NODE && $person->hasChildNodes() ){

            /* arrays to hold data */
            $row=array();
            $cols=array();

            foreach( $person->childNodes as $node ) {

                if( $node->nodeType==XML_ELEMENT_NODE ){

                    if( $node->hasChildNodes() && $node->childNodes->length > 1 ) {
                        foreach( $node->childNodes as $detail ){
                            if( $detail->nodeType==XML_ELEMENT_NODE ) {

                                /* add values from details */
                                $row[ $detail->tagName ]=$detail->nodeValue;

                                /* add column headers / tagnames for nodes within details */
                                $cols[]=$detail->tagName;
                            }
                        }
                    } else {
                        $cols[]=$node->tagName;
                        $row[ $node->tagName ]=$node->nodeValue;
                    }
                }
            }
            fputcsv( $handle, $row );
        }
    }
}

/* change handle to the final output file & truncate before writing */
$handle=fopen( $output, 'w+' );

/* add column headers */
fputcsv( $handle, $cols );

/* close file handle */
@fclose( $handle );

/* copy contents of temp file to output */
file_put_contents( $output, file_get_contents( $tmp ), FILE_APPEND );

/* delete temp file */
@unlink( $tmp );


$dom=$col=$person=$node=$handle=null;