development

PHP에서 XML을 JSON으로 변환

big-blog 2020. 6. 24. 07:06
반응형

PHP에서 XML을 JSON으로 변환


PHP에서 XML을 json으로 변환하려고합니다. 간단한 xml 및 json_encode를 사용하여 간단한 변환을 수행하면 xml의 속성이 표시되지 않습니다.

$xml = simplexml_load_file("states.xml");
echo json_encode($xml);

그래서 나는 이것을 수동으로 파싱하려고합니다.

foreach($xml->children() as $state)
{
    $states[]= array('state' => $state->name); 
}       
echo json_encode($states);

상태 출력은 {"state":{"0":"Alabama"}}오히려{"state":"Alabama"}

내가 뭘 잘못하고 있죠?

XML :

<?xml version="1.0" ?>
<states>
    <state id="AL">     
    <name>Alabama</name>
    </state>
    <state id="AK">
        <name>Alaska</name>
    </state>
</states>

산출:

[{"state":{"0":"Alabama"}},{"state":{"0":"Alaska"}

var 덤프 :

object(SimpleXMLElement)#1 (1) {
["state"]=>
array(2) {
[0]=>
object(SimpleXMLElement)#3 (2) {
  ["@attributes"]=>
  array(1) {
    ["id"]=>
    string(2) "AL"
  }
  ["name"]=>
  string(7) "Alabama"
}
[1]=>
object(SimpleXMLElement)#2 (2) {
  ["@attributes"]=>
  array(1) {
    ["id"]=>
    string(2) "AK"
  }
  ["name"]=>
  string(6) "Alaska"
}
}
}

3 줄로 된 XML의 JSON 및 배열 :

$xml = simplexml_load_string($xml_string);
$json = json_encode($xml);
$array = json_decode($json,TRUE);

이전 게시물에 답변 해 주셔서 죄송하지만이 기사에서는 비교적 짧고 간결하며 유지 관리가 쉬운 접근 방식에 대해 간략하게 설명합니다. 나는 그것을 직접 테스트하고 잘 작동합니다.

http://lostechies.com/seanbiefeld/2011/10/21/simple-xml-to-json-with-php/

<?php   
class XmlToJson {
    public function Parse ($url) {
        $fileContents= file_get_contents($url);
        $fileContents = str_replace(array("\n", "\r", "\t"), '', $fileContents);
        $fileContents = trim(str_replace('"', "'", $fileContents));
        $simpleXml = simplexml_load_string($fileContents);
        $json = json_encode($simpleXml);

        return $json;
    }
}
?>

나는 그것을 알아. json_encode는 문자열과 다르게 객체를 처리합니다. 객체를 문자열로 캐스팅하면 이제 작동합니다.

foreach($xml->children() as $state)
{
    $states[]= array('state' => (string)$state->name); 
}       
echo json_encode($states);

나는 파티에 조금 늦었다 고 생각하지만이 작업을 수행하기 위해 작은 기능을 작성했습니다. 또한 속성, 텍스트 내용 및 동일한 node-name을 가진 여러 노드가 형제 인 경우에도 처리합니다.

면책 조항 : 저는 PHP 네이티브가 아니므로 간단한 실수를 감수하십시오.

function xml2js($xmlnode) {
    $root = (func_num_args() > 1 ? false : true);
    $jsnode = array();

    if (!$root) {
        if (count($xmlnode->attributes()) > 0){
            $jsnode["$"] = array();
            foreach($xmlnode->attributes() as $key => $value)
                $jsnode["$"][$key] = (string)$value;
        }

        $textcontent = trim((string)$xmlnode);
        if (count($textcontent) > 0)
            $jsnode["_"] = $textcontent;

        foreach ($xmlnode->children() as $childxmlnode) {
            $childname = $childxmlnode->getName();
            if (!array_key_exists($childname, $jsnode))
                $jsnode[$childname] = array();
            array_push($jsnode[$childname], xml2js($childxmlnode, true));
        }
        return $jsnode;
    } else {
        $nodename = $xmlnode->getName();
        $jsnode[$nodename] = array();
        array_push($jsnode[$nodename], xml2js($xmlnode, true));
        return json_encode($jsnode);
    }
}   

사용 예 :

$xml = simplexml_load_file("myfile.xml");
echo xml2js($xml);

입력 예 (myfile.xml) :

<family name="Johnson">
    <child name="John" age="5">
        <toy status="old">Trooper</toy>
        <toy status="old">Ultrablock</toy>
        <toy status="new">Bike</toy>
    </child>
</family>

출력 예 :

{"family":[{"$":{"name":"Johnson"},"child":[{"$":{"name":"John","age":"5"},"toy":[{"$":{"status":"old"},"_":"Trooper"},{"$":{"status":"old"},"_":"Ultrablock"},{"$":{"status":"new"},"_":"Bike"}]}]}]}

꽤 인쇄 :

{
    "family" : [{
            "$" : {
                "name" : "Johnson"
            },
            "child" : [{
                    "$" : {
                        "name" : "John",
                        "age" : "5"
                    },
                    "toy" : [{
                            "$" : {
                                "status" : "old"
                            },
                            "_" : "Trooper"
                        }, {
                            "$" : {
                                "status" : "old"
                            },
                            "_" : "Ultrablock"
                        }, {
                            "$" : {
                                "status" : "new"
                            },
                            "_" : "Bike"
                        }
                    ]
                }
            ]
        }
    ]
}

유의 사항 : 동일한 태그 이름을 가진 여러 태그가 형제 일 수 있습니다. 다른 솔루션은 마지막 형제를 제외한 모든 솔루션을 삭제합니다. 이를 방지하기 위해 하나의 자식 만있는 경우에도 각각의 모든 단일 노드는 tagname의 각 인스턴스에 대한 객체를 보유하는 배열입니다. (예에서 여러 개의 ""요소 참조)

유효한 XML 문서에 하나만 있어야하는 루트 요소조차도 일관된 데이터 구조를 갖기 위해 인스턴스의 객체와 함께 배열로 저장됩니다.

XML 노드 컨텐츠와 XML 속성을 구별 할 수 있도록 각 오브젝트 속성은 "$"에 저장되고 "_"하위의 내용에 저장됩니다.

편집 : 예제 입력 데이터의 출력을 표시하는 것을 잊었습니다 .

{
    "states" : [{
            "state" : [{
                    "$" : {
                        "id" : "AL"
                    },
                    "name" : [{
                            "_" : "Alabama"
                        }
                    ]
                }, {
                    "$" : {
                        "id" : "AK"
                    },
                    "name" : [{
                            "_" : "Alaska"
                        }
                    ]
                }
            ]
        }
    ]
}

일반적인 함정은 json_encode()텍스트 값 속성을 가진 요소를 존중하지 않는 것입니다 . 데이터 손실을 의미하는 것 중 하나를 선택합니다. 아래 기능은 그 문제를 해결합니다. json_encode/ decode을 가기로 결정 하면 다음 기능이 권장됩니다.

function json_prepare_xml($domNode) {
  foreach($domNode->childNodes as $node) {
    if($node->hasChildNodes()) {
      json_prepare_xml($node);
    } else {
      if($domNode->hasAttributes() && strlen($domNode->nodeValue)){
         $domNode->setAttribute("nodeValue", $node->textContent);
         $node->nodeValue = "";
      }
    }
  }
}

$dom = new DOMDocument();
$dom->loadXML( file_get_contents($xmlfile) );
json_prepare_xml($dom);
$sxml = simplexml_load_string( $dom->saveXML() );
$json = json_decode( json_encode( $sxml ) );

그렇게하면 JSON <foo bar="3">Lorem</foo>처럼 끝나지 않습니다 {"foo":"Lorem"}.


이것을 사용해보십시오

$xml = ... // Xml file data

// first approach
$Json = json_encode(simplexml_load_string($xml));

---------------- OR -----------------------

// second approach
$Json = json_encode(simplexml_load_string($xml, "SimpleXMLElement", LIBXML_NOCDATA));

echo $Json;

또는

이 라이브러리를 사용할 수 있습니다 : https://github.com/rentpost/xml2array


이 목적으로 Miles Johnson의 TypeConverter 를 사용했습니다. Composer를 사용하여 설치할 수 있습니다.

이것을 사용하여 다음과 같이 작성할 수 있습니다.

<?php
require 'vendor/autoload.php';
use mjohnson\utility\TypeConverter;

$xml = file_get_contents("file.xml");
$arr = TypeConverter::xmlToArray($xml, TypeConverter::XML_GROUP);
echo json_encode($arr);

Antonio Max 답변 최적화 :

$xmlfile = 'yourfile.xml';
$xmlparser = xml_parser_create();

// open a file and read data
$fp = fopen($xmlfile, 'r');
//9999999 is the length which fread stops to read.
$xmldata = fread($fp, 9999999);

// converting to XML
$xml = simplexml_load_string($xmldata, "SimpleXMLElement", LIBXML_NOCDATA);

// converting to JSON
$json = json_encode($xml);
$array = json_decode($json,TRUE);

이것은 Antonio Max가 가장 많이 지원하는 솔루션을 개선 한 것으로, 네임 스페이스가있는 XML 과도 작동합니다 (콜론을 밑줄로 바꿈). 또한 추가 옵션이 있으며 <person my-attribute='name'>John</person>올바르게 구문 분석 합니다.

function parse_xml_into_array($xml_string, $options = array()) {
    /*
    DESCRIPTION:
    - parse an XML string into an array
    INPUT:
    - $xml_string
    - $options : associative array with any of these keys:
        - 'flatten_cdata' : set to true to flatten CDATA elements
        - 'use_objects' : set to true to parse into objects instead of associative arrays
        - 'convert_booleans' : set to true to cast string values 'true' and 'false' into booleans
    OUTPUT:
    - associative array
    */

    // Remove namespaces by replacing ":" with "_"
    if (preg_match_all("|</([\\w\\-]+):([\\w\\-]+)>|", $xml_string, $matches, PREG_SET_ORDER)) {
        foreach ($matches as $match) {
            $xml_string = str_replace('<'. $match[1] .':'. $match[2], '<'. $match[1] .'_'. $match[2], $xml_string);
            $xml_string = str_replace('</'. $match[1] .':'. $match[2], '</'. $match[1] .'_'. $match[2], $xml_string);
        }
    }

    $output = json_decode(json_encode(@simplexml_load_string($xml_string, 'SimpleXMLElement', ($options['flatten_cdata'] ? LIBXML_NOCDATA : 0))), ($options['use_objects'] ? false : true));

    // Cast string values "true" and "false" to booleans
    if ($options['convert_booleans']) {
        $bool = function(&$item, $key) {
            if (in_array($item, array('true', 'TRUE', 'True'), true)) {
                $item = true;
            } elseif (in_array($item, array('false', 'FALSE', 'False'), true)) {
                $item = false;
            }
        };
        array_walk_recursive($output, $bool);
    }

    return $output;
}

XML의 특정 부분 만 JSON으로 변환하려는 경우 XPath를 사용하여이를 검색하여 JSON으로 변환 할 수 있습니다.

<?php
$file = @file_get_contents($xml_File, FILE_TEXT);
$xml = new SimpleXMLElement($file);
$xml_Excerpt = @$xml->xpath('/states/state[@id="AL"]')[0]; // [0] gets the node
echo json_encode($xml_Excerpt);
?>

Xpath가 올바르지 않으면 오류가 발생하여 죽습니다. 따라서 AJAX 호출을 통해 이것을 디버깅하는 경우 응답 본문도 기록하는 것이 좋습니다.


$state->name변수가 배열을 보유하고있는 것 같습니다 . 당신이 사용할 수있는

var_dump($state)

foreach그것을 테스트 하기 위해 내부 .

이 경우 내부의 줄 foreach

$states[]= array('state' => array_shift($state->name)); 

수정하십시오.


질문은 그것을 말하지 않지만 일반적으로 PHP는 JSON을 웹 페이지로 반환합니다.

JS lib를 통해 브라우저 / 페이지에서 XML을 JSON으로 변환하는 것이 훨씬 쉽다는 것을 알았습니다.

https://code.google.com/p/x2js/downloads/detail?name=x2js-v1.1.3.zip

여기에있는 모든 솔루션에는 문제가 있습니다!

... 표현에 완전한 XML 해석이 필요한 경우 (속성에 문제 없음) 모든 text-tag-text-tag-text -... 및 태그 순서를 재현해야합니다. 또한 JSON 객체는 "정렬되지 않은 세트"(반복 키가 아니며 키가 사전 정의 된 순서를 가질 수 없음)라는 점을 기억하십시오. ZF의 xml2json 조차도 XML 구조를 정확하게 유지하지 않기 때문에 잘못되었습니다!

여기의 모든 솔루션에는이 간단한 XML에 문제가 있습니다.

    <states x-x='1'>
        <state y="123">Alabama</state>
        My name is <b>John</b> Doe
        <state>Alaska</state>
    </states>

... @FTav 솔루션은 3 줄 솔루션보다 낫지 만이 XML로 테스트 할 때 버그가 거의 없습니다.

오래된 솔루션이 최고입니다 (무손실 표현)

오늘날 jsonML 로 잘 알려진이 솔루션 Zorba 프로젝트 및 기타 업체에서 사용되며 ~ 2006 년 또는 ~ 2007 년에 (별도의) Stephen McKameyJohn Snelson에 의해 처음 발표되었습니다 .

// the core algorithm is the XSLT of the "jsonML conventions"
// see  https://github.com/mckamey/jsonml
$xslt = 'https://raw.githubusercontent.com/mckamey/jsonml/master/jsonml.xslt';
$dom = new DOMDocument;
$dom->loadXML('
    <states x-x=\'1\'>
        <state y="123">Alabama</state>
        My name is <b>John</b> Doe
        <state>Alaska</state>
    </states>
');
if (!$dom) die("\nERROR!");
$xslDoc = new DOMDocument();
$xslDoc->load($xslt);
$proc = new XSLTProcessor();
$proc->importStylesheet($xslDoc);
echo $proc->transformToXML($dom);

생산액

["states",{"x-x":"1"},
    "\n\t    ",
    ["state",{"y":"123"},"Alabama"],
    "\n\t\tMy name is ",
    ["b","John"],
    " Doe\n\t    ",
    ["state","Alaska"],
    "\n\t"
]

http://jsonML.org 또는 github.com/mckamey/jsonml을 참조하십시오 . 이 JSON의 프로덕션 규칙은 JSON- 아날로그 요소를 기반으로합니다 .

여기에 이미지 설명을 입력하십시오

이 구문은을 사용하여 요소 정의 및 반복입니다
element-list ::= element ',' element-list | element.


약간의 모든 답변을 조사한 후 브라우저 (콘솔 / 개발자 도구 포함)의 JavaScript 함수와 잘 작동하는 솔루션을 생각해 냈습니다.

<?php

 // PHP Version 7.2.1 (Windows 10 x86)

 function json2xml( $domNode ) {
  foreach( $domNode -> childNodes as $node) {
   if ( $node -> hasChildNodes() ) { json2xml( $node ); }
   else {
    if ( $domNode -> hasAttributes() && strlen( $domNode -> nodeValue ) ) {
     $domNode -> setAttribute( "nodeValue", $node -> textContent );
     $node -> nodeValue = "";
    }
   }
  }
 }

 function jsonOut( $file ) {
  $dom = new DOMDocument();
  $dom -> loadXML( file_get_contents( $file ) );
  json2xml( $dom );
  header( 'Content-Type: application/json' );
  return str_replace( "@", "", json_encode( simplexml_load_string( $dom -> saveXML() ), JSON_PRETTY_PRINT ) );
 }

 $output = jsonOut( 'https://boxelizer.com/assets/a1e10642e9294f39/b6f30987f0b66103.xml' );

 echo( $output );

 /*
  Or simply 
  echo( jsonOut( 'https://boxelizer.com/assets/a1e10642e9294f39/b6f30987f0b66103.xml' ) );
 */

?>

기본적으로 새 DOMDocument,로드 및 XML 파일을 작성하고 각 노드 및 하위 노드를 통해 데이터 / 매개 변수를 가져와 성가신 "@"부호없이 JSON으로 내 보냅니다.

XML 파일에 연결 하십시오.


우분투 사용자 인 경우 XML 리더를 설치하십시오 (PHP 5.6이 있습니다. 다른 패키지가 있으면 설치하여 설치하십시오)

sudo apt-get install php5.6-xml
service apache2 restart

$fileContents = file_get_contents('myDirPath/filename.xml');
$fileContents = str_replace(array("\n", "\r", "\t"), '', $fileContents);
$fileContents = trim(str_replace('"', "'", $fileContents));
$oldXml = $fileContents;
$simpleXml = simplexml_load_string($fileContents);
$json = json_encode($simpleXml);

This is better solution

$fileContents= file_get_contents("https://www.feedforall.com/sample.xml");
$fileContents = str_replace(array("\n", "\r", "\t"), '', $fileContents);
$fileContents = trim(str_replace('"', "'", $fileContents));
$simpleXml = simplexml_load_string($fileContents);
$json = json_encode($simpleXml);
$array = json_decode($json,TRUE);
return $array;

$templateData =  $_POST['data'];

// initializing or creating array
$template_info =  $templateData;

// creating object of SimpleXMLElement
$xml_template_info = new SimpleXMLElement("<?xml version=\"1.0\"?><template></template>");

// function call to convert array to xml
array_to_xml($template_info,$xml_template_info);

//saving generated xml file
 $xml_template_info->asXML(dirname(__FILE__)."/manifest.xml") ;

// function defination to convert array to xml
function array_to_xml($template_info, &$xml_template_info) {
    foreach($template_info as $key => $value) {
        if(is_array($value)) {
            if(!is_numeric($key)){
                $subnode = $xml_template_info->addChild($key);
                if(is_array($value)){
                    $cont = 0;
                    foreach(array_keys($value) as $k){
                        if(is_numeric($k)) $cont++;
                    }
                }

                if($cont>0){
                    for($i=0; $i < $cont; $i++){
                        $subnode = $xml_body_info->addChild($key);
                        array_to_xml($value[$i], $subnode);
                    }
                }else{
                    $subnode = $xml_body_info->addChild($key);
                    array_to_xml($value, $subnode);
                }
            }
            else{
                array_to_xml($value, $xml_template_info);
            }
        }
        else {
            $xml_template_info->addChild($key,$value);
        }
    }
}

$xml = simplexml_load_string($xml_string);
$json = json_encode($xml);
$array = json_decode($json,TRUE);

그 세 줄을 추가하면 올바른 출력을 얻을 수 있습니다.


이 솔루션은 네임 스페이스, 속성을 처리하고 반복되는 요소 (항상 배열이 하나만있는 경우에도 항상 배열)로 일관된 결과를 생성합니다. ratfactor의 sxiToArray ()에서 영감을 얻었습니다 .

/**
 * <root><a>5</a><b>6</b><b>8</b></root> -> {"root":[{"a":["5"],"b":["6","8"]}]}
 * <root a="5"><b>6</b><b>8</b></root> -> {"root":[{"a":"5","b":["6","8"]}]}
 * <root xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"><a>123</a><wsp:b>456</wsp:b></root> 
 *   -> {"root":[{"xmlns:wsp":"http://schemas.xmlsoap.org/ws/2004/09/policy","a":["123"],"wsp:b":["456"]}]}
 */
function domNodesToArray(array $tags, \DOMXPath $xpath)
{
    $tagNameToArr = [];
    foreach ($tags as $tag) {
        $tagData = [];
        $attrs = $tag->attributes ? iterator_to_array($tag->attributes) : [];
        $subTags = $tag->childNodes ? iterator_to_array($tag->childNodes) : [];
        foreach ($xpath->query('namespace::*', $tag) as $nsNode) {
            // the only way to get xmlns:*, see https://stackoverflow.com/a/2470433/2750743
            if ($tag->hasAttribute($nsNode->nodeName)) {
                $attrs[] = $nsNode;
            }
        }

        foreach ($attrs as $attr) {
            $tagData[$attr->nodeName] = $attr->nodeValue;
        }
        if (count($subTags) === 1 && $subTags[0] instanceof \DOMText) {
            $text = $subTags[0]->nodeValue;
        } elseif (count($subTags) === 0) {
            $text = '';
        } else {
            // ignore whitespace (and any other text if any) between nodes
            $isNotDomText = function($node){return !($node instanceof \DOMText);};
            $realNodes = array_filter($subTags, $isNotDomText);
            $subTagNameToArr = domNodesToArray($realNodes, $xpath);
            $tagData = array_merge($tagData, $subTagNameToArr);
            $text = null;
        }
        if (!is_null($text)) {
            if ($attrs) {
                if ($text) {
                    $tagData['_'] = $text;
                }
            } else {
                $tagData = $text;
            }
        }
        $keyName = $tag->nodeName;
        $tagNameToArr[$keyName][] = $tagData;
    }
    return $tagNameToArr;
}

function xmlToArr(string $xml)
{
    $doc = new \DOMDocument();
    $doc->loadXML($xml);
    $xpath = new \DOMXPath($doc);
    $tags = $doc->childNodes ? iterator_to_array($doc->childNodes) : [];
    return domNodesToArray($tags, $xpath);
}

예:

php > print(json_encode(xmlToArr('<root a="5"><b>6</b></root>')));
{"root":[{"a":"5","b":["6"]}]}

참고 URL : https://stackoverflow.com/questions/8830599/php-convert-xml-to-json

반응형