私は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の出力は{"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 dump:
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"
}
}
}
私はそれを考え出した。 json_encodeはオブジェクトを文字列とは異なる方法で処理します。オブジェクトを文字列にキャストすると、それは今動作します。
foreach($xml->children() as $state)
{
$states[]= array('state' => (string)$state->name);
}
echo json_encode($states);
3行のXMLからのJson&Array:
$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;
}
}
?>
パーティーには少し時間がかかると思いますが、このタスクを実行するための小さな機能を書いています。属性、テキストの内容、そして同じnode-nameを持つ複数のノードが兄弟である場合でもそれは同様に面倒を見ます。
Dislaimer:私は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文書に1つだけ存在する必要があるルート要素でさえも、一貫したデータ構造を持つために、インスタンスのオブジェクトとともに配列として格納されます。
XMLノードのコンテンツとXML属性を区別できるように、各オブジェクトの属性は "$"に、コンテンツは "_"子に格納されています。
編集:入力データ例の出力を見忘れています
{
"states" : [{
"state" : [{
"$" : {
"id" : "AL"
},
"name" : [{
"_" : "Alabama"
}
]
}, {
"$" : {
"id" : "AK"
},
"name" : [{
"_" : "Alaska"
}
]
}
]
}
]
}
よくある落とし穴は、json_encode()
がtextvalue and属性を持つ要素を尊重しないことを忘れることです。それはdatalossを意味するそれらのうちの1つを選ぶでしょう。以下の関数はその問題を解決します。 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 ) );
そうすることで、<foo bar="3">Lorem</foo>
はあなたのJSONの{"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呼び出しでデバッグしている場合は、レスポンスボディも記録することをお勧めします。
このソリューションは、名前空間、属性を処理し、繰り返し要素を使用して一貫した結果を生成します(出現箇所が1つしかない場合でも、常に配列内にあります)。 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"]}]}
あなたがubuntuユーザーインストール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);
...表現が完全な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 McKamey および John 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-analogに基づいています。
この構文は、要素の定義と繰り返しです。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 ファイルにリンクします。
質問では言われませんが、通常PHPはJSONをWebページに返しています。
JS libを介してブラウザ/ページでXMLをJSONに変換する方がはるかに簡単です。
https://code.google.com/p/x2js/downloads/detail?name=x2js-v1.1.3.Zip
$state->name
変数が配列を保持しているように見えます。あなたが使用することができます
var_dump($state)
それをテストするためにforeach
の中。
その場合は、foreach
内の行を次のように変更できます。
$states[]= array('state' => array_shift($state->name));
それを修正します。
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;