PHP конвертирует XML в JSON

Я пытаюсь преобразовать xml в json в php. Если я делаю простой конвертировать, используя простые 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 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"
}
}
}

Ответ 1

Я понял это. json_encode обрабатывает объекты по-разному, чем строки. Я передаю объект в строку и теперь работает.

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

Ответ 2

Json & Массив из XML в 3 строки:

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

Ответ 3

Извините за ответ на старое сообщение, но в этой статье описывается подход, который относительно короткий, краткий и простой в обслуживании. Я сам тестировал и работал очень хорошо.

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;
    }
}
?>

Ответ 4

Думаю, я немного опаздываю на вечеринку, но я написал небольшую функцию для выполнения этой задачи. Он также заботится об атрибутах, текстовом содержимом и даже если несколько узлов с тем же именем node являются братьями и сестрами.

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"
                        }
                    ]
                }
            ]
        }
    ]
}

Привычки: Несколько тегов с одним и тем же тэгом могут быть братьями и сестрами. Другие решения, скорее всего, оставят всех, кроме последнего брата. Чтобы избежать этого, каждый node, даже если он имеет только один дочерний элемент, представляет собой массив, который содержит объект для каждого экземпляра тэга. (См. Несколько примеров "" в примере)

Даже корневой элемент, из которого только один должен существовать в действительном XML-документе, хранится как массив с объектом экземпляра, просто чтобы иметь согласованную структуру данных.

Чтобы иметь возможность различать XML node контент и атрибуты XML, каждый атрибут объектов хранится в "$" и содержимом в "_" дочернем.

Edit: Я забыл показать вывод для вашего примера входных данных

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

Ответ 5

Общей ошибкой является то, что json_encode() не учитывает элементы с атрибутами и. Он выберет один из них, что означает dataloss. Функция ниже решает эту проблему. Если вы решите пойти на путь 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> не будет заканчиваться как {"foo":"Lorem"} в вашем JSON.

Ответ 6

Попробуйте использовать этот

$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

Ответ 7

Я использовал 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);

Ответ 8

Оптимизация ответа Антонио Макс:

$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);

Ответ 9

Это усовершенствование наиболее употребимого решения 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;
}

Ответ 10

Если вы хотите только преобразовать определенную часть 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, я рекомендую вам также регистрировать тела ответа.

Ответ 11

Похоже, что переменная $state->name содержит массив. Вы можете использовать

var_dump($state)

внутри foreach, чтобы проверить это.

Если это случай, вы можете изменить строку внутри foreach на

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

чтобы исправить его.

Ответ 12

Вопрос не говорит об этом, но обычно PHP возвращает JSON на веб-страницу.

Мне гораздо проще преобразовать XML в JSON в браузер/страницу через JS lib, например:

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

Ответ 13

Все решения здесь имеют проблемы!

... Когда представление нуждается в идеальной интерпретации XML (без проблем с атрибутами) и для воспроизведения всего текста-тега-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 решение кажется лучше, чем трехлинейное решение, но также имеет небольшую ошибку при тестировании с этим XML.

Старое решение является лучшим (для представления без потерь)

Решение, сегодня хорошо известное как jsonML, используется проект Zorba и другие, и было впервые представленный в ~ 2006 или ~ 2007 году, (отдельно) Stephen McKamey и Джон Снельсон.

// 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.

Ответ 14

Изучив немного все ответы, я нашел решение, которое прекрасно работало с моими функциями 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.

Ответ 15

Если вы пользователь Ubuntu, установите xml reader (у меня есть 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);

Ответ 16

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;

Ответ 17

$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);
        }
    }
}

Ответ 18

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

просто добавьте эти три строки, и вы получите правильный вывод :-)

Ответ 19

Это решение обрабатывает пространства имен, атрибуты и выдает согласованный результат с повторяющимися элементами (всегда в массиве, даже если есть только одно вхождение). Вдохновлено 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"]}]}