55

SimpleXML でアクセスしている異なる属性を持ついくつかの同一の要素があります。

<data>
    <seg id="A1"/>
    <seg id="A5"/>
    <seg id="A12"/>
    <seg id="A29"/>
    <seg id="A30"/>
</data>

ID が「A12」の特定のセグメント要素を削除する必要があります。どうすればよいですか? セグメント要素をループして特定の要素を設定解除しようとしましたが、これは機能せず、要素が残ります。

foreach($doc->seg as $seg)
{
    if($seg['id'] == 'A12')
    {
        unset($seg);
    }
}
4

18 に答える 18

59

SimpleXMLは XML ノードを削除する方法を提供しますが、その変更機能は多少制限されています。もう 1 つの解決策は、 DOM拡張機能を使用することです。dom_import_simplexml()は、を に変換するのに役立ちSimpleXMLElementますDOMElement

サンプルコード (PHP 5.2.5 でテスト済み):

$data='<data>
    <seg id="A1"/>
    <seg id="A5"/>
    <seg id="A12"/>
    <seg id="A29"/>
    <seg id="A30"/>
</data>';
$doc=new SimpleXMLElement($data);
foreach($doc->seg as $seg)
{
    if($seg['id'] == 'A12') {
        $dom=dom_import_simplexml($seg);
        $dom->parentNode->removeChild($dom);
    }
}
echo $doc->asXml();

出力

<?xml version="1.0"?>
<data><seg id="A1"/><seg id="A5"/><seg id="A29"/><seg id="A30"/></data>

ところで、特定のノードの選択は、XPath ( SimpleXMLElement->xpath )を使用するとより簡単になります。

$segs=$doc->xpath('//seq[@id="A12"]');
if (count($segs)>=1) {
    $seg=$segs[0];
}
// same deletion procedure as above
于 2008-11-04T17:14:48.653 に答える
25

ノードの設定を解除するだけです:

$str = <<<STR
<a>
  <b>
    <c>
    </c>
  </b>
</a>
STR;

$xml = simplexml_load_string($str);
unset($xml –&gt; a –&gt; b –&gt; c); // this would remove node c
echo $xml –&gt; asXML(); // xml document string without node c

このコードは、How to delete/remove nodes in SimpleXMLから取られたものです。

于 2008-12-27T06:04:48.597 に答える
10

ステファンの答えは正しいと思います。(一致するすべてのノードではなく) 1 つのノードのみを削除する場合は、別の例を次に示します。

//Load XML from file (or it could come from a POST, etc.)
$xml = simplexml_load_file('fileName.xml');

//Use XPath to find target node for removal
$target = $xml->xpath("//seg[@id=$uniqueIdToDelete]");

//If target does not exist (already deleted by someone/thing else), halt
if(!$target)
return; //Returns null

//Import simpleXml reference into Dom & do removal (removal occurs in simpleXML object)
$domRef = dom_import_simplexml($target[0]); //Select position 0 in XPath array
$domRef->parentNode->removeChild($domRef);

//Format XML to save indented tree rather than one line and save
$dom = new DOMDocument('1.0');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($xml->asXML());
$dom->save('fileName.xml');

Load XML... (最初) と Format XML... (最後) のセクションは、XML データの取得元と出力で何をしたいかによって、異なるコードに置き換えることができることに注意してください。ノードを見つけて削除するのは、その間のセクションです。

さらに、if ステートメントは、移動を試みる前にターゲット ノードが存在することを確認するためだけに存在します。このケースを処理または無視する別の方法を選択できます。

于 2009-11-25T09:04:49.173 に答える
5

これは私にとってはうまくいきます:

$data = '<data>
<seg id="A1"/>
<seg id="A5"/>
<seg id="A12"/>
<seg id="A29"/>
<seg id="A30"/></data>';

$doc = new SimpleXMLElement($data);

$segarr = $doc->seg;

$count = count($segarr);

$j = 0;

for ($i = 0; $i < $count; $i++) {

    if ($segarr[$j]['id'] == 'A12') {
        unset($segarr[$j]);
        $j = $j - 1;
    }
    $j = $j + 1;
}

echo $doc->asXml();
于 2010-08-26T17:57:40.383 に答える
4

基本の SimpleXMLElement クラスを拡張する場合は、次のメソッドを使用できます。

class MyXML extends SimpleXMLElement {

    public function find($xpath) {
        $tmp = $this->xpath($xpath);
        return isset($tmp[0])? $tmp[0]: null;
    }

    public function remove() {
        $dom = dom_import_simplexml($this);
        return $dom->parentNode->removeChild($dom);
    }

}

// Example: removing the <bar> element with id = 1
$foo = new MyXML('<foo><bar id="1"/><bar id="2"/></foo>');
$foo->find('//bar[@id="1"]')->remove();
print $foo->asXML(); // <foo><bar id="2"/></foo>
于 2010-09-10T19:13:23.877 に答える
2

特定の属性値を持つノードまたは属性値の配列に該当するノードを削除/保持するには、次のようにクラスを拡張できます(私のGitHub GistSimpleXMLElementの最新バージョン):

class SimpleXMLElementExtended extends SimpleXMLElement
{    
    /**
    * Removes or keeps nodes with given attributes
    *
    * @param string $attributeName
    * @param array $attributeValues
    * @param bool $keep TRUE keeps nodes and removes the rest, FALSE removes nodes and keeps the rest 
    * @return integer Number o affected nodes
    *
    * @example: $xml->o->filterAttribute('id', $products_ids); // Keeps only nodes with id attr in $products_ids
    * @see: http://stackoverflow.com/questions/17185959/simplexml-remove-nodes
    */
    public function filterAttribute($attributeName = '', $attributeValues = array(), $keepNodes = TRUE)
    {       
        $nodesToRemove = array();

        foreach($this as $node)
        {
            $attributeValue = (string)$node[$attributeName];

            if ($keepNodes)
            {
                if (!in_array($attributeValue, $attributeValues)) $nodesToRemove[] = $node;
            }
            else
            { 
                if (in_array($attributeValue, $attributeValues)) $nodesToRemove[] = $node;
            }
        }

        $result = count($nodesToRemove);

        foreach ($nodesToRemove as $node) {
            unset($node[0]);
        }

        return $result;
    }
}

次に、$docXML を使用して、<seg id="A12"/>ノードの呼び出しを削除できます。

$data='<data>
    <seg id="A1"/>
    <seg id="A5"/>
    <seg id="A12"/>
    <seg id="A29"/>
    <seg id="A30"/>
</data>';

$doc=new SimpleXMLElementExtended($data);
$doc->seg->filterAttribute('id', ['A12'], FALSE);

または複数の<seg />ノードを削除します。

$doc->seg->filterAttribute('id', ['A1', 'A12', 'A29'], FALSE);

<seg id="A5"/>ノードとノードのみを保持<seg id="A30"/>し、残りを削除するには:

$doc->seg->filterAttribute('id', ['A5', 'A30'], TRUE);
于 2016-08-29T09:25:13.103 に答える
1

新しいアイデア:simple_xml配列として機能します。

削除したい「配列」のインデックスを検索し、unset()関数を使用してこの配列インデックスを削除できます。私の例:

$pos=$this->xml->getXMLUser();
$i=0; $array_pos=array();
foreach($this->xml->doc->users->usr[$pos]->u_cfg_root->profiles->profile as $profile) {
    if($profile->p_timestamp=='0') { $array_pos[]=$i; }
    $i++;
}
//print_r($array_pos);
for($i=0;$i<count($array_pos);$i++) {
    unset($this->xml->doc->users->usr[$pos]->u_cfg_root->profiles->profile[$array_pos[$i]]);
}
于 2010-03-17T10:55:20.977 に答える
1

SimpleXml を介して子要素を削除する方法があります。コードは要素を探しますが、何もしません。それ以外の場合は、要素を文字列に追加します。次に、文字列をファイルに書き込みます。また、コードは元のファイルを上書きする前にバックアップを保存することに注意してください。

$username = $_GET['delete_account'];
echo "DELETING: ".$username;
$xml = simplexml_load_file("users.xml");

$str = "<?xml version=\"1.0\"?>
<users>";
foreach($xml->children() as $child){
  if($child->getName() == "user") {
      if($username == $child['name']) {
        continue;
    } else {
        $str = $str.$child->asXML();
    }
  }
}
$str = $str."
</users>";
echo $str;

$xml->asXML("users_backup.xml");
$myFile = "users.xml";
$fh = fopen($myFile, 'w') or die("can't open file");
fwrite($fh, $str);
fclose($fh);
于 2008-12-06T03:58:27.430 に答える
0

SimpleXMLには要素を削除する詳細な方法はありませんが、PHPを使用してSimpleXMLから要素を削除できunset()ます。これを行うための鍵は、目的の要素をターゲットにすることです。ターゲティングを行う少なくとも1つの方法は、要素の順序を使用することです。まず、削除する要素の注文番号を見つけて(たとえば、ループを使用して)、次に要素を削除します。

$target = false;
$i = 0;
foreach ($xml->seg as $s) {
  if ($s['id']=='A12') { $target = $i; break; }
  $i++;
}
if ($target !== false) {
  unset($xml->seg[$target]);
}

ターゲットアイテムの注文番号を配列に格納することで、これを使用して複数の要素を削除することもできます。array_reverse($targets)アイテムを削除すると、その後に続くアイテムの注文数が自然に減少するため、削除を逆の順序()で行うことを忘れないでください。

確かに、それはちょっとしたハックアラウンドですが、うまくいくようです。

于 2009-10-11T13:45:51.573 に答える
0

私は Gerry と同じ致命的なエラーに遭遇し、DOM に詳しくないので、次のようにすることにしました。

$item = $xml->xpath("//seg[@id='A12']");
$page = $xml->xpath("/data");
$id = "A12";

if (  count($item)  &&  count($page) ) {
    $item = $item[0];
    $page = $page[0];

     // find the numerical index within ->children().
    $ch = $page->children();
    $ch_as_array = (array) $ch;

    if (  count($ch_as_array)  &&  isset($ch_as_array['seg'])  ) {
        $ch_as_array = $ch_as_array['seg'];
        $index_in_array = array_search($item, $ch_as_array);
        if (  ($index_in_array !== false)
          &&  ($index_in_array !== null)
          &&  isset($ch[$index_in_array])
          &&  ($ch[$index_in_array]['id'] == $id)  ) {

             // delete it!
            unset($ch[$index_in_array]);

            echo "<pre>"; var_dump($xml); echo "</pre>";
        }
    }  // end of ( if xml object successfully converted to array )
}  // end of ( valid item  AND  section )
于 2013-07-02T13:15:01.567 に答える
0

RSS フィードの項目など、類似した (一意ではない) 子要素のリストを切り取りたい場合は、次のコードを使用できます。

for ( $i = 9999; $i > 10; $i--) {
    unset($xml->xpath('/rss/channel/item['. $i .']')[0]->{0});
}

RSS の末尾を 10 要素にカットします。で削除しようとしました

for ( $i = 10; $i < 9999; $i ++ ) {
    unset($xml->xpath('/rss/channel/item[' . $i . ']')[0]->{0});
}

しかし、それはどういうわけかランダムに機能し、一部の要素のみをカットします。

于 2016-03-09T09:07:22.943 に答える
0

ヘルパー関数に関するアイデアは、php.netの DOM に関するコメントの 1 つからのものであり、unset の使用に関するアイデアはkavoir.comからのものです。私にとって、このソリューションは最終的に機能しました:

function Myunset($node)
{
 unsetChildren($node);
 $parent = $node->parentNode;
 unset($node);
}

function unsetChildren($node)
{
 while (isset($node->firstChild))
 {
 unsetChildren($node->firstChild);
 unset($node->firstChild);
 }
}

使用: $xml は SimpleXmlElement です

Myunset($xml->channel->item[$i]);

結果は $xml に保存されるので、変数に代入する必要はありません。

于 2009-08-21T13:50:55.470 に答える
0

FluidXMLでは、XPath を使用して削除する要素を選択できます。

$doc = fluidify($doc);

$doc->remove('//*[@id="A12"]');

https://github.com/servo-php/fluidxml


XPath は次の//*[@id="A12"]ことを意味します。

  • ドキュメントの任意の場所 ( //)
  • すべてのノード ( *)
  • 属性が( ) にid等しい。A12[@id="A12"]
于 2016-01-23T23:33:03.310 に答える