使用parent-child元素重新创建XML feed

时间:2016-03-28 19:13:46

标签: php xml

我有一个令人难以置信的任务。我有一个直截了当的饲料。出租的属性是精确的,它看起来像这样:

<ad>
<name>Property 1</name>
<latitude>29.723085</latitude>
<longitude>-95.66024</longitude>
<area>2000</area>
</ad>
<ad>
<name>Property 1</name>
<latitude>29.723085</latitude>
<longitude>-95.66024</longitude>
<area>2500</area>
</ad>

在上面的例子中,有一个属性有两个不同的单位。两者唯一共同的是名称和纬度,经度字段。

现在,我需要使用PHP将此XML重新创建为新的XML,并将所有不同的单元放置到一个属性中。所以新的Feed将如下所示:

<ad>
<property_name>Property 1</property_name>
<latitude>29.723085</latitude>
<longitude>-95.66024</longitude>
<units>
    <unit>
       <area>2000</area>
    </unit>
    <unit>
       <area>2500</area>
    </unit>
</units>
</ad>

有人可以帮我解决这个问题吗?非常感谢

1 个答案:

答案 0 :(得分:1)

有更多选项可以获得您想要的结果。使用DOMDocument,您可以使用importNode,也可以直接修改原始XML。

我将向您展示一种创建全新XML的方法。

首先,您必须在DOMDocument对象中加载现有XML:

$src = new DOMDocument();
libxml_use_internal_errors(1);
$src->loadXML( $xml );

然后创建目标DOMDocument对象。为此,我使用通用<root>标记;您可以将其替换为完整的XML包装<ad>标记:

$dom = new DOMDocument();
libxml_use_internal_errors(1);
$dom->loadXML( '<root></root>' );
$dom->formatOutput = True;
$root = $dom->getElementsByTagName( 'root' )->item(0);

现在,为目标XML初始化DOMXpath对象。 DOMXPath允许执行复杂的XML查询:

$xpath = new DOMXPath( $dom );

此时,在源XML的所有foreach个节点中执行<ad>,并且 - 对于每个节点 - 检索<name><area>值:

foreach( $src->getElementsByTagName( 'ad' ) as $node )
{
    $name = $node->getElementsByTagName( 'name' )->item(0)->nodeValue;
    $area = $node->getElementsByTagName( 'area' )->item(0)->nodeValue;

现在使用DOMXPath查找目标XML中是否已存在<ad><name>值=已检索名称的节点:

    $found = $xpath->query( '//ad[name[.="'.$name.'"]]' );

如果找到目标节点,请将其<units>设置为$child

    if($found->length)
    {
        $child = $found->item(0)->getElementsByTagName('units')->item(0);
    }

否则,创建一个从源添加基础数据的新节点,然后将其设置为$child

    else
    {
        $lat  = $node->getElementsByTagName( 'latitude' )->item(0)->nodeValue;
        $long = $node->getElementsByTagName( 'longitude' )->item(0)->nodeValue;

        $child = $dom->createElement( 'ad' );

        $child->appendChild( $dom->createElement( 'name', $name ) );
        $child->appendChild( $dom->createElement( 'latitude', $lat ) );
        $child->appendChild( $dom->createElement( 'longitude', $long ) );
        $child->appendChild( $dom->createElement( 'units' ) );

        $root->appendChild( $child );

        $child = $child->getElementsByTagName('units')->item(0);
    }

此时,您有正确的$child节点来添加<unit>

    $unit = $dom->createElement( 'unit' );
    $unit->appendChild( $dom->createElement( 'area', $area ) );
    $child->appendChild( $unit );
}

foreach()循环结束时,您可以打印获取的XML:

echo $dom->saveXML();

输出:

<?xml version="1.0"?>
<root>
  <ad>
    <name>Property 1</name>
    <latitude>29.723085</latitude>
    <longitude>-95.66024</longitude>
    <units/>
    <unit>
      <area>2000</area>
    </unit>
    <unit>
      <area>2500</area>
    </unit>
  </ad>
</root>

eval.in demo

附加说明:

在上面的脚本中,我假设每个属性名称都有唯一的long / lat。如果可以存在具有相同名称但长/纬度不同的属性,则必须不仅按名称查找节点,还要按纬度/经度查找节点。

我还假设您的结构样本在每个节点中都得到了尊重。否则,$node->getElementsByTagName( 'latitude' )->item(0)->nodeValue之类的语法可能会失败,您必须将其替换为:

if( $node->getElementsByTagName( 'latitude' )->length )
{
    $lat = $node->getElementsByTagName( 'latitude' )->item(0)->nodeValue;
}

以及->item(0)语法的每个代码行。