복붙노트

PHP 용 SimpleXML에서 특정 속성을 가진 자식 제거

PHP

PHP 용 SimpleXML에서 특정 속성을 가진 자식 제거

SimpleXML을 사용하여 액세스하는 다른 속성을 가진 몇 가지 동일한 요소가 있습니다.

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

ID가 "A12"인 특정 seg 요소를 제거해야합니다. 어떻게해야합니까? 내가 seg 요소를 통해 반복하고 특정 하나를 unsetting했지만,이 작동하지 않는다, 요소가 남아있다.

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

해결법

  1. ==============================

    1.SimpleXML은 XML 노드를 제거하는 방법을 제공하지만 수정 기능은 다소 제한적입니다. 또 다른 해결책은 DOM 확장을 사용하는 것입니다. dom_import_simplexml ()은 SimpleXMLElement를 DOMElement로 변환하는 데 도움을줍니다.

    SimpleXML은 XML 노드를 제거하는 방법을 제공하지만 수정 기능은 다소 제한적입니다. 또 다른 해결책은 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
    
  2. ==============================

    2.기존의 답변에 대한 대중의 믿음과는 달리, 각 Simplexml 요소 노드는 문서 자체와 unset ()에 의해 문서에서 제거 될 수 있습니다. SimpleXML이 실제로 어떻게 작동 하는지를 이해해야한다.

    기존의 답변에 대한 대중의 믿음과는 달리, 각 Simplexml 요소 노드는 문서 자체와 unset ()에 의해 문서에서 제거 될 수 있습니다. SimpleXML이 실제로 어떻게 작동 하는지를 이해해야한다.

    먼저 제거 할 요소를 찾습니다.

    list($element) = $doc->xpath('/*/seg[@id="A12"]');
    

    그런 다음 $ element에 표시된 자체 참조를 해제 한 요소를 제거합니다.

    unset($element[0]);
    

    이는 요소의 첫 번째 요소가 Simplexml (자체 참조)의 요소 자체이기 때문에 가능합니다. 이것은 마법의 성격과 관련이 있으며 숫자 인덱스는 모든 목록의 요소 (예 : parent-> children)를 나타내며 심지어 단일 자식도 그러한 목록입니다.

    숫자가 아닌 문자열 인덱스는 속성 (배열 액세스의 경우) 또는 하위 요소 (속성 액세스의 경우)를 나타냅니다.

    따라서 속성 액세스의 숫자 인덱스는 다음과 같습니다.

    unset($element->{0});
    

    잘 작동한다.

    당연히 그 xpath 예제를 보면, 그것은 직설적입니다 (PHP 5.4에서) :

    unset($doc->xpath('/*/seg[@id="A12"]')[0][0]);
    

    전체 예제 코드 (데모) :

    <?php
    /**
     * Remove a child with a specific attribute, in SimpleXML for PHP
     * @link http://stackoverflow.com/a/16062633/367456
     */
    
    $data=<<<DATA
    <data>
        <seg id="A1"/>
        <seg id="A5"/>
        <seg id="A12"/>
        <seg id="A29"/>
        <seg id="A30"/>
    </data>
    DATA;
    
    
    $doc = new SimpleXMLElement($data);
    
    unset($doc->xpath('seg[@id="A12"]')[0]->{0});
    
    $doc->asXml('php://output');
    

    산출:

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

    3.그냥 노드를 해제 :

    그냥 노드를 해제 :

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

    이 코드는 SimpleXML에서 노드를 삭제 / 제거하는 방법에서 가져 왔습니다.

  4. ==============================

    4.나는 스테판의 대답이 맞다고 믿는다. 일치하는 모든 노드가 아닌 하나의 노드 만 제거하려는 경우 다른 예가 있습니다.

    나는 스테판의 대답이 맞다고 믿는다. 일치하는 모든 노드가 아닌 하나의 노드 만 제거하려는 경우 다른 예가 있습니다.

    //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');
    

    XML로드 ... (첫 번째) 및 Format XML ... (마지막) 섹션은 XML 데이터의 출처와 출력 작업에 따라 다른 코드로 대체 될 수 있습니다. 그 사이에 노드를 찾아서 제거하는 섹션입니다.

    또한 if 문은 대상 노드가 이동하기 전에 존재하는지 확인하기위한 것입니다. 이 경우를 처리하거나 무시할 수있는 여러 가지 방법을 선택할 수 있습니다.

  5. ==============================

    5.기본 SimpleXMLElement 클래스를 확장하는 경우이 메서드를 사용할 수 있습니다.

    기본 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>
    
  6. ==============================

    6.이 작품은 나를 위해 :

    이 작품은 나를 위해 :

    $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();
    
  7. ==============================

    7.나중에 참조 할 수 있도록 SimpleXML로 노드를 삭제하면 문서의 정확한 구조를 모르는 경우가 종종 있습니다. 그래서 SimpleXML을 작성하여 몇 가지 편의 방법을 추가하는 SimpleDOM 클래스를 작성했습니다.

    나중에 참조 할 수 있도록 SimpleXML로 노드를 삭제하면 문서의 정확한 구조를 모르는 경우가 종종 있습니다. 그래서 SimpleXML을 작성하여 몇 가지 편의 방법을 추가하는 SimpleDOM 클래스를 작성했습니다.

    예를 들어 deleteNodes ()는 XPath 표현식과 일치하는 모든 노드를 삭제합니다. "id"가 "A5"인 모든 노드를 삭제하려면 다음을 수행하면됩니다.

    // don't forget to include SimpleDOM.php
    include 'SimpleDOM.php';
    
    // use simpledom_load_string() instead of simplexml_load_string()
    $data = simpledom_load_string(
        '<data>
            <seg id="A1"/>
            <seg id="A5"/>
            <seg id="A12"/>
            <seg id="A29"/>
            <seg id="A30"/>
        </data>'
    );
    
    // and there the magic happens
    $data->deleteNodes('//seg[@id="A5"]');
    
  8. ==============================

    8.특정 애트리뷰트 값을 가진 노드를 제거 / 유지하거나 애트리뷰트 값의 배열로 떨어지려면 다음과 같이 SimpleXMLElement 클래스를 확장하면된다. (내 GitHub Gist의 최신 버전) :

    특정 애트리뷰트 값을 가진 노드를 제거 / 유지하거나 애트리뷰트 값의 배열로 떨어지려면 다음과 같이 SimpleXMLElement 클래스를 확장하면된다. (내 GitHub Gist의 최신 버전) :

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

    $ doc XML을 가지고 있으면 노드 호출을 제거 할 수 있습니다.

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

    또는 여러 개의 노드를 제거하십시오.

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

    노드 만 유지하고 나머지는 제거하려면 다음을 수행하십시오.

    $doc->seg->filterAttribute('id', ['A5', 'A30'], TRUE);
    
  9. ==============================

    9.SimpleXml을 통해 자식 요소를 제거하는 방법이 있습니다. 이 코드는  요소를 포함하고 아무것도 수행하지 않습니다. 그렇지 않으면 요소에 문자열을 추가합니다. 그런 다음 문자열을 파일에 기록합니다. 또한이 코드는 원본 파일을 덮어 쓰기 전에 백업을 저장합니다.

    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);
    
  10. ==============================

    10.새로운 아이디어 : simple_xml은 배열로 작동합니다.

    새로운 아이디어 : 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]]);
    }
    
  11. ==============================

    11.SimpleXML에는 요소를 제거하는 자세한 방법이 없지만 PHP의 unset ()을 사용하여 SimpleXML에서 요소를 제거 할 수 있습니다. 이를 수행하는 핵심은 원하는 요소를 대상으로 관리하는 것입니다. 타겟팅을 수행하는 한 가지 방법은 요소의 순서를 사용하는 것입니다. 먼저 제거하려는 요소의 순서 번호 (예 : 루프)를 찾아 요소를 제거하십시오.

    SimpleXML에는 요소를 제거하는 자세한 방법이 없지만 PHP의 unset ()을 사용하여 SimpleXML에서 요소를 제거 할 수 있습니다. 이를 수행하는 핵심은 원하는 요소를 대상으로 관리하는 것입니다. 타겟팅을 수행하는 한 가지 방법은 요소의 순서를 사용하는 것입니다. 먼저 제거하려는 요소의 순서 번호 (예 : 루프)를 찾아 요소를 제거하십시오.

    $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))를 수행하십시오.

    틀림없이 해킹이 조금 있지만 제대로 작동하는 것 같습니다.

  12. ==============================

    12.나는 또한이 문제에 어려움을 겪고 있었고 대답은 여기에 제공된 것보다 쉽습니다. 당신은 xpath를 사용하여 그것을 찾을 수 있고 그것을 다음과 같은 방법으로 설정 해제 할 수있다 :

    나는 또한이 문제에 어려움을 겪고 있었고 대답은 여기에 제공된 것보다 쉽습니다. 당신은 xpath를 사용하여 그것을 찾을 수 있고 그것을 다음과 같은 방법으로 설정 해제 할 수있다 :

    unset($XML->xpath("NODESNAME[@id='test']")[0]->{0});
    

    이 코드는 id 속성이 "test"인 "NODESNAME"노드를 찾고 첫 번째 발생을 제거합니다.

    $ XML-> saveXML (...);을 사용하여 xml을 저장하는 것을 잊지 마십시오.

  13. ==============================

    13.Gerry와 같은 치명적인 오류가 발생하여 DOM에 익숙하지 않기 때문에 다음과 같이 결정했습니다.

    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 )
    
  14. ==============================

    14.도우미 함수에 대한 아이디어는 php.net의 DOM에 대한 주석 중 하나이며, unset 사용에 대한 아이디어는 kavoir.com에서 가져온 것입니다. 나에게이 솔루션은 마침내 성공했다.

    도우미 함수에 대한 아이디어는 php.net의 DOM에 대한 주석 중 하나이며, 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에 저장되므로 변수에 할당하는 것에 대해 걱정할 필요가 없습니다.

  15. ==============================

    15.FluidXML을 사용하면 XPath를 사용하여 제거 할 요소를 선택할 수 있습니다.

    FluidXML을 사용하면 XPath를 사용하여 제거 할 요소를 선택할 수 있습니다.

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

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

    XPath // * [@ id = "A12"]는 다음을 의미합니다.

  16. ==============================

    16.유사한 (고유하지 않은) 하위 요소 목록 (예 : RSS 피드 항목)을 자르려면 다음 코드를 사용할 수 있습니다.

    유사한 (고유하지 않은) 하위 요소 목록 (예 : 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});
    }
    

    하지만 어떻게 든 무작위로 작동하고 일부 요소 만 잘라냅니다.

  17. ==============================

    17.초기 접근 방식은 옳았지 만, foreach에 대해서는 하나의 작은 점을 잊어 버렸습니다. 원본 배열 / 객체에서는 작동하지 않지만 반복 할 때마다 각 요소의 복사본이 만들어 지므로 복사본을 설정 해제했습니다. 다음과 같은 참조를 사용하십시오.

    초기 접근 방식은 옳았지 만, foreach에 대해서는 하나의 작은 점을 잊어 버렸습니다. 원본 배열 / 객체에서는 작동하지 않지만 반복 할 때마다 각 요소의 복사본이 만들어 지므로 복사본을 설정 해제했습니다. 다음과 같은 참조를 사용하십시오.

    foreach($doc->seg as &$seg) 
    {
        if($seg['id'] == 'A12')
        {
            unset($seg);
        }
    }
    
  18. from https://stackoverflow.com/questions/262351/remove-a-child-with-a-specific-attribute-in-simplexml-for-php by cc-by-sa and MIT license