一种简单的方法来完全(实体,标签,注释等)读取php中结构未知的xml文档?

时间:2019-02-11 21:25:44

标签: php xml

我只是停留在一个我认为没有那么难解决的问题上。

我正在开发一个可以上传和下载xml文件的网站。 xml文件必须完全读取,我想将信息写入数据库。

可能的结构数量有限,但未定义上载文档中存在的结构。这必须由系统本身来识别。理想情况下,应该可以从数据库中的条目重新创建上载的文档。

应阅读文档的所有元素。特别重要:

  • 命名实体
  • 所有标签,属性和值
  • 评论及其上方的元素
  • 文档中实体的使用也应存储在数据库中。

我附上了两个样本文件。这些文件实际上更加广泛。我只是想尽可能简单地提出问题。

关于PHP类/库/函数可以满足我的要求的提示,我将非常感激。我一直在看整个星期天,但我只在圈子里移动...

我不是专业的PHP开发人员,这只是一个休闲项目。如果这个问题容易回答,请亲切。

inhabitants.xml:

<?xml version="1.0"?>
<!DOCTYPE inhabitants [
  <!ENTITY cName "Ultra Long Surname">
]>
<inhabitants>
    <inhabitant alias="bob">Bob &cName;</inhabitant>
    <inhabitant alias="tom">Tom &cName;</inhabitant>
    <inhabitant alias="tim">Tim Short</inhabitant>
    <inhabitant alias="leo">Leo Short</inhabitant>
</inhabitants>

streets.xml:

<?xml version="1.0"?>
<districts>
    <district name="d1">
        <street size="small">Nameless street</street>
        <street size="long">Sutton Lane</street>
    </district>
    <district name="d2">
        <!-- Not explored -->
        <street size="unknown">Street of Death</street>
    </district>
</districts>

我最后一次尝试或已经发现的结果:

<?php
$dom = new DOMDocument();
$dom->load('files/inhabitants.xml');

// How to get "Ultra Long Surname" ?
echo 'Value of "cName": '.$dom->doctype->entities->item(0)->nodeValue;
// Output: "Value of "cName": "

// How do I get a list of all tags?

foreach ($dom->getElementsByTagName('inhabitant') as $inhabitant){
    if ($inhabitant instanceof DOMElement){
        echo $inhabitant->getAttribute('alias');
        echo ' - ';
        echo $inhabitant->nodeValue;
        echo '<br>';
    }
}
/*
 * Output:
 *
 * bob - Bob Ultra Long Surname
 * tom - Tom Ultra Long Surname
 * tim - Tim Short
 * leo - Leo Short
 */

// How to get "&cName;" instead of "Ultra Long Surname"?

2 个答案:

答案 0 :(得分:0)

使用simpleXML。它简单而有效。

http://php.net/manual/en/simplexml.examples.php

除非明确排除,否则从5.1.x开始,它也与php捆绑在一起。

答案 1 :(得分:0)

DOM中的任何内容都是一个节点。您已经找到了实体定义,实体引用也是一个节点。但是,仅由于详细程度,在此级别上进行工作会很费力。 Xpath可以提供很多帮助。它允许您获取特定的节点。

要获取实体引用,您必须查看元素内的节点。

$xml = <<<'XML'
<?xml version="1.0"?>
<!DOCTYPE inhabitants [
  <!ENTITY cName "Ultra Long Surname">
]>
<inhabitants>
    <inhabitant alias="bob">Bob &cName;</inhabitant>
    <inhabitant alias="tom">Tom &cName;</inhabitant>
    <inhabitant alias="tim">Tim Short</inhabitant>
    <inhabitant alias="leo">Leo Short</inhabitant>
</inhabitants>
XML;

$document = new DOMDocument;
$document->loadXML($xml);
$xpath = new DOMXpath($document);

foreach ($xpath->evaluate('//inhabitant') as $inhabitant) {
    echo $inhabitant->getAttribute('alias');
    echo ' - ';
    foreach ($inhabitant->childNodes as $node) {
        echo get_class($node), ": ";
        if ($node instanceof DOMText) {
            echo $node->textContent;
        } elseif ($node instanceof DOMEntityReference) {
            echo $node->nodeName;
        }
    }
    echo "\n";
}

输出:

bob - DOMText: Bob DOMEntityReference: cName 
tom - DOMText: Tom DOMEntityReference: cName 
tim - DOMText: Tim Short 
leo - DOMText: Leo Short

我不确定您要对数据库中的实体引用做什么。与文档断开连接将不会引用任何值。

可以使用Xpath完成在节点之前获取注释的操作

$xml = <<<'XML'
<?xml version="1.0"?>
<districts>
    <district name="d1">
        <street size="small">Nameless street</street>
        <street size="long">Sutton Lane</street>
    </district>
    <district name="d2">
        <!-- Not explored -->
        <street size="unknown">Street of Death</street>
    </district>
</districts>
XML;

$document = new DOMDocument;
$document->loadXML($xml);
$xpath = new DOMXpath($document);

foreach ($xpath->evaluate('//district/street') as $street) {
    echo $street->textContent, ':';
    echo $xpath->evaluate(
        'string(preceding-sibling::node()[normalize-space(.) != ""][1][self::comment()])', 
        $street
    );
    echo "\n";
}

输出:

Nameless street: 
Sutton Lane: 
Street of Death: Not explored 

Xpath表达式:

  • 获取不只是空格的前面的兄弟节点
    preceding-sibling::node()[normalize-space(.) != ""]
  • 按位置过滤
    preceding-sibling::node()[normalize-space(.) != ""][1]
  • 已针对评论节点进行过滤
    preceding-sibling::node()[normalize-space(.) != ""][1][self::comment()]
  • 铸成字符串
    string(preceding-sibling::node()[normalize-space(.) != ""][1][self::comment()])