所有
我有一个inputXml.xml文件,如下所示:
<content>
<item name="book" label="Book">
<![CDATA[ book name ]]>
</item>
<item name="price" label="Price">
<![CDATA[ 35 ]]>
</item>
</content>
当我使用下面的代码解析xml文件时:
$obj = simplexml_load_string(file_get_contents($inputXml),'SimpleXMLElement', LIBXML_NOCDATA);
$json = json_encode($obj);
$inputArray = json_decode($json,TRUE);
我得到如下数组:
[content] => Array
(
[item] => Array
(
[0] => book name
[1] => 35
)
)
我想知道,是否可以通过使用属性的值来获得关联数组&#34; name&#34;或&#34;标签&#34;作为关键如下:
[content] => Array
(
[item] => Array
(
[name] => book name
[price] => 35
)
)
答案 0 :(得分:1)
首先,您被json_encode
和json_decode
所需的其他代码所愚弄,以便从 SimpleXMLElement 中获取数组。相反,您只需要转换为数组:
$inputArray = (array) $obj;
然后您遇到的问题是,您正在寻找的阵列序列化不是 SimpleXMLElement 为该XML提供的默认序列化。
此外,您遇到的另一个小问题是依赖于使用LIBXML_NOCDATA
,否则您将无法获得接近的格式。但是,不依赖于那个标志(因此,如果基础XML将使用CDATA或不使用元素值XML编码),那么也可以获得代码的某种稳定性。
由于 SimpleXMLElement 无法提供您想要的行为,因此您通常有两个选项:从 SimpleXMLElement 扩展或装饰它。我通常建议装饰,因为延伸是有限的。例如。你不能通过扩展来干扰(array)
转换,但是你可以进行JSON序列化。但这不是你想要的,你正在寻找阵列序列化。
因此,对于 SimpleXMLElement 的标准数组序列化,您可以使用序列化程序和策略对象来实现对数组进行数组序列化。
首先需要序列化程序:
interface ArraySerializer
{
public function arraySerialize();
}
class SimpleXMLArraySerializer implements ArraySerializer
{
/**
* @var SimpleXMLElement
*/
private $subject;
/**
* @var SimpleXMLArraySerializeStrategy
*/
private $strategy;
public function __construct(SimpleXMLElement $element, SimpleXMLArraySerializeStrategy $strategy = NULL) {
$this->subject = $element;
$this->strategy = $strategy ?: new DefaultSimpleXMLArraySerializeStrategy();
}
public function arraySerialize() {
$strategy = $this->getStrategy();
return $strategy->serialize($this->subject);
}
/**
* @return SimpleXMLArraySerializeStrategy
*/
public function getStrategy() {
return $this->strategy;
}
}
此阵列序列化程序缺少序列化功能。这已经针对一种策略,以便以后可以轻松交换。这是一个默认策略:
abstract class SimpleXMLArraySerializeStrategy
{
abstract public function serialize(SimpleXMLElement $element);
}
class DefaultSimpleXMLArraySerializeStrategy extends SimpleXMLArraySerializeStrategy
{
public function serialize(SimpleXMLElement $element) {
$array = array();
// create array of child elements if any. group on duplicate names as an array.
foreach ($element as $name => $child) {
if (isset($array[$name])) {
if (!is_array($array[$name])) {
$array[$name] = [$array[$name]];
}
$array[$name][] = $this->serialize($child);
} else {
$array[$name] = $this->serialize($child);
}
}
// handle SimpleXMLElement text values.
if (!$array) {
$array = (string)$element;
}
// return empty elements as NULL (self-closing or empty tags)
if (!$array) {
$array = NULL;
}
return $array;
}
}
此对象包含将 SimpleXMLElement 转换为数组的常用方法。它的行为与您的XML相似, SimpleXMLElement 与LIBXML_NOCDATA
已经相同。但它没有CDATA的问题。为了显示这一点,以下示例已经给出了您的输出:
$obj = new SimpleXMLElement($xml);
$serializer = new SimpleXMLArraySerializer($obj);
print_r($serializer->arraySerialize());
现在到目前为止,阵列序列化已经以它自己的类型实现,很容易根据需要进行更改。对于 content 元素,您可以使用不同的策略将其转换为数组。它也容易得多:
class ContentXMLArraySerializeStrategy extends SimpleXMLArraySerializeStrategy
{
public function serialize(SimpleXMLElement $element) {
$array = array();
foreach ($element->item as $item) {
$array[(string) $item['name']] = (string) $item;
}
return array('item' => $array);
}
}
剩下的就是在正确的情况下将其连接到SimpleXMLArraySerializer
。例如。取决于元素的名称:
...
/**
* @return SimpleXMLArraySerializeStrategy
*/
public function getStrategy() {
if ($this->subject->getName() === 'content') {
return new ContentXMLArraySerializeStrategy();
}
return $this->strategy;
}
}
现在来自上面的相同例子:
$obj = new SimpleXMLElement($xml);
$serializer = new SimpleXMLArraySerializer($obj);
print_r($serializer->arraySerialize());
会给你想要的输出(美化):
Array
(
[item] => Array
(
[book] => book name
[price] => 35
)
)
由于您的XML可能只有这一个元素,我会说这样的抽象级别可能有点多。但是,如果XML将要更改并且您在同一文档中实际存在多个数组格式需求,那么这是一种合理的方式。
我在我的示例中使用的默认序列化基于SimpleXML and JSON Encode in PHP – Part III and End中的装饰示例。
答案 1 :(得分:0)
我快速浏览了the SimpleXMLElement
docs,这表明构建一个你想要的数组实际上非常容易:
$xml = simplexml_load_file($file, 'SimpleXMLElement', LIBXML_NOCDATA);
$result = array();//store assoc array here
foreach ($xml->item as $item)
{//iterate over item nodes
if (isset($item['name']))
{//attributes are accessible as array keys
$result[(string) $item['name']] = (string) $item;//casts required!
}
}
var_dump($result);
这是因为SimpleXMLElement
是一个可遍历的对象,因此您可以像访问数组一样访问其属性。但是,我们确实需要强制转换属性,因为它们都是SimpleXMLElement
类的实例
上面的代码是我最初编写的简化版本:
$xml = simplexml_load_file($fileName, 'SimpleXMLElement', LIBXML_NOCDATA);
foreach ($xml as $name => $node)
{
if ($name === 'item')
{
$key = false;
foreach ($node->attributes() as $name => $attr)
{
if ($name == 'name')
{
$key = (string) $attr;//attr is object, still
break;
}
}
if ($key !== false)
$result[$key] = (string) $node;
}
}
这也有效。然而,代码看起来,我认为你会同意,相当混乱。我坚持我在这里发布的第一个版本......
初步回答(使用DOMDocument
)
我将使用simpleXML研究如何执行此操作,但就目前而言,我是如何设置使用DOMDocument
API获取CDATA值的业务:
$dom = new DOMDocument;
$dom->load($file);
//get items
$items = $dom->getElementsByTagName('item');
$cData = array();
foreach ($items as $node)
{
if ($node->hasChildNodes())
{
foreach ($node->childNodes as $cNode)
{
if ($cNode->nodeType === XML_CDATA_SECTION_NODE)
$cData[] = $cNode->textContent;//get contents
}
}
}
将此功能与$node->attributes->getNamedItem('name');
等其他方法结合使用,可获取节点的属性$node->attributes->getNamedItem('name')->nodeValue;
以获取该属性的值。
我承认,DOMDocument
api看起来相当冗长(因为它是),感觉有点笨拙(就像它一直做的那样),但是一旦你{{3 }}