dey: noch einmal: array query verfeinern

Hallo,

ich hoffe mal, das wird nicht als Doppelposting angesehen!?
Beim letzten mal gab es gar keine Antwort, was mich doch ein wenig verwirrt.
Ich poste trotz Link noch mal die komplette Frage:

ich habe schon früher einmal zum Thema miniXML gepostet.
Mein Arbeitspeicher ist jetzt deutlich früher übergelaufen, als ich erwartet hatte.
Jetzt habe ich mich bei php.net bei James bedient: herlich flott und schlank.

Nur leider wird das array nicht ganz so schön aufgebaut:

XML-to-array
Array
(
    [0] => Array
        (
            [nodename] => ALL_GAMES
            [attributes] => Array
                (
                )

[childrens] => Array
                (
                    [0] => Array
                        (
                            [nodename] => GAME
                            [attributes] => Array
                                (
                                    [GID] => 0
                                    [MATCH] => Testspiel
                                    [YY] => 00
                                    [MM] => 01
                                    [DD] => 01
                                )

[childrens] => Array
                                (
                                    [0] => Array                                        (
                                            [nodename] => RESULT
                                            [attributes] => Array
                                                (
                                                )

[nodevalue] => 1:0

Wenn ich jetzt das Ergebnis ausgeben lasse muß leider über den Schlüsselwert des entsprechenden Kindelements[0] gehen:
$xml_array['0']['childrens'][$key]['childrens'][0]['nodevalue']
Sollte die Position des Kindelements aus Unachtsamkeit mal woanders sein bekomme ich ein falschen Rückgabewert.
Lieber würde ich mir [nodevalue] von dem Kind zurückgeben lassen bei dem [nodename]= RESULT ist.
Ich habe aber ganz und gar keine Idee wie! Kann jemand helfen?

bydey

--
-- bydey ist die Signatur und Verabschiedung, nicht der Nick --
-- Navigate all your PHP web projects with  PHP Project Browser--
  1. Hi dey!

    function getNodevalue($array,$nodename) {  
        foreach($array as $node) {  
            if(isset($array['nodename']) AND $array['nodename'] == $nodename)  
                return $array['nodevalue'];  
            else return getNodevalue($node,$nodename);  
        }  
    }
    

    Die Funktion muss natürlich noch an deine Bedürfnisse angepasst werden.

    MfG H☼psel

    --
    "It's amazing I won. I was running against peace, prosperity, and incumbency."
    George W. Bush speaking to Swedish Prime Minister unaware a live television camera was still rolling, June 14, 2001
    Selfcode: ie:% fl:( br:> va:) ls:& fo:) rl:? n4:& ss:| de:] js:| ch:? sh:( mo:) zu:)
    1. Hallo,

      Hi dey!

      function getNodevalue($array,$nodename) {

      foreach($array as $node) {
              if(isset($array['nodename']) AND $array['nodename'] == $nodename)
                  return $array['nodevalue'];
              else return getNodevalue($node,$nodename);
          }
      }

      Wenn ich das richtig überblicke würden mir hier alle Nodevalue passend zum $nodename ausgegeben. Ich aber doch nur den passend zum aktuellen Elternelement.  
      Zudem kostet die Rekursiv-Funktion vermutlich erheblich Performace!?  
        
      Wie sieht es aus wenn ich aus dem Elternelement ein temporäres Array mache und die mit $array übergebe?  
        
      
      > MfG H☼psel  
      
      bydey
      
      -- 
      \-- bydey ist die Signatur und Verabschiedung, nicht der Nick --  
        
      \-- Navigate all your PHP web projects with  [PHP Project Browser](http://deynews.de/ppb/)--
      
      1. Hi dey!

        Wenn ich das richtig überblicke würden mir hier alle Nodevalue passend zum $nodename ausgegeben.

        Siehe unten.

        Ich aber doch nur den passend zum aktuellen Elternelement.
        Zudem kostet die Rekursiv-Funktion vermutlich erheblich Performace!?

        Ja. Es kommt darauf an, wie groß dein Array ist.

        Wie sieht es aus wenn ich aus dem Elternelement ein temporäres Array mache und die mit $array übergebe?

        Das brauchst du noch nicht einmal. Du kennst doch das Elternelement schon, oder?
        Du kannst also das Elternelement direkt an die Funktion übergeben oder in die Funktion eine entsprechende Abfrage mit einem dritten Argument einbauen.

        MfG H☼psel

        --
        "It's amazing I won. I was running against peace, prosperity, and incumbency."
        George W. Bush speaking to Swedish Prime Minister unaware a live television camera was still rolling, June 14, 2001
        Selfcode: ie:% fl:( br:> va:) ls:& fo:) rl:? n4:& ss:| de:] js:| ch:? sh:( mo:) zu:)
  2. echo $begrüßung;

    [childrens] => Array

    Nebenbei: children ist die Mehrzahl von child. childrens wäre Kinders.

    Wenn ich jetzt das Ergebnis ausgeben lasse muß leider über den Schlüsselwert des entsprechenden Kindelements[0] gehen:
    $xml_array['0']['childrens'][$key]['childrens'][0]['nodevalue']
    Sollte die Position des Kindelements aus Unachtsamkeit mal woanders sein bekomme ich ein falschen Rückgabewert.

    Unachtsamkeit kann viele Auswirkungen haben. Willst du die alle bedenken und abzufangen versuchen?

    Lieber würde ich mir [nodevalue] von dem Kind zurückgeben lassen bei dem [nodename]= RESULT ist.

    Rekursiv durch die Struktur hangeln wäre eine Möglichkeit.

    echo "$verabschiedung $name";

    1. Hallo,

      Nebenbei: children ist die Mehrzahl von child. childrens wäre Kinders.

      Da hst du allerdings recht. Das ist das Problem mit entliehenen Skripten.

      Unachtsamkeit kann viele Auswirkungen haben. Willst du die alle bedenken und abzufangen versuchen?

      Nein, es geht mir tatsächlich darum, daß es an der Kind-Position hängt.
      Da ich das ganze für weitere/ andere XML-Dateien verwenden möchte wäre mir ein Ansatz, allgemeingültiger ist lieber.

      Rekursiv durch die Struktur hangeln wäre eine Möglichkeit.

      Rekursiv bedeutet doch, daß ich jedesmal das komplette Array absuche. DAs kostet vermutlich deutlich Performance!?

      bydey

      --
      -- bydey ist die Signatur und Verabschiedung, nicht der Nick --
      -- Navigate all your PHP web projects with  PHP Project Browser--
      1. echo $begrüßung;

        Rekursiv durch die Struktur hangeln wäre eine Möglichkeit.
        Rekursiv bedeutet doch, daß ich jedesmal das komplette Array absuche. DAs kostet vermutlich deutlich Performance!?

        Ja, so ist das. Entweder du weißt wo du hingreifen musst oder du must das Ding suchen. Wieviel Performance das kostet, hängt natürlich von deinen Daten und von Suchalgorithmus ab. Probier es doch am besten aus. Und wenn das nichts bringt, hast du zumindest wieder etwas Erfahrung gesammelt. :-)

        echo "$verabschiedung $name";

  3. Du kannst vom unten angegebenen Skript ausgehen, welches eine XML-Datei in eine Objekthirarchie umsetzt. Durch geeignete Modifikationen solltest Du in der Lage sein, die Daten in die gewünschte Struktur zu bringen. Die Knoten werden auch unter dem Namen des Tags in m_ChildNodes abgelegt, so dass Du nicht gezwungen bist, über den Index zu gehen. Das setzt natürlich voraus, dass es nur ein Tag dieses Namens innerhalb solcher Container gibt. Andernfalls handelt es sich um ein Feld aller Kindknoten mit diesem Tagnamen, so dass das keinen Vorteil bringt.

    Allerdings kannst Du schon mit kleinen Änderungen an den XML-Daten eine Vereinfachung erreichen. Statt

    <ALL_GAMES>
      <GAME GID='0' MATCH='Testspiel' YY='00' MM='01' DD='01'>
        <RESULT>1:0</RESULT>
      </GAME>
    </ALL_GAMES>

    solltest Du erwägen

    <ALL_GAMES>
      <GAME GID='0' MATCH='Testspiel' YY='00' MM='01' DD='01' RESULT='1:0' />
    </ALL_GAMES>

    zu verwenden, denn ein Spiel hat wohl nicht mehr als 1 Ergebnis nehme ich an. In diesem Fall liefert dir der angegebene Code das Resultat:

    stdClass Object
    (
        [tagName] => ALL_GAMES
        [childNodes] => Array
            (
                [0] => stdClass Object
                    (
                        [tagName] => GAME
                        [GID] => 0
                        [MATCH] => Testspiel
                        [YY] => 00
                        [MM] => 01
                        [DD] => 01
                        [RESULT] => 1:0
                    )
                [GAME] => stdClass Object
                    (
                        [tagName] => GAME
                        [GID] => 0
                        [MATCH] => Testspiel
                        [YY] => 00
                        [MM] => 01
                        [DD] => 01
                        [RESULT] => 1:0
                    )
            )
    )

    Du kannst dann über $result->childNodes[0]->RESULT auf das Ergebnis zugreifen.

    Sollte Dir die Verwendung von Objekten nicht zusagen, können auch Felder anstelle der Objekte erzeugt werden.

    Durch das Weglassen der [0] Indizierung in der return Zeile der parse Methode können auch mehrere hintereinander stehende Wurzelknoten zurückgegeben werden.

    Eine weitere Alternative wäre eine Modifikation der XmlParser Klasse, so dass Du die Daten direkt verarbeitest und keine Notwendigkeit besteht, diese zwischenzuspeichern. Das wäre besonders dann sinnvoll, wenn Du mit recht beschränktem Speicher auskommen musst oder die Datenmenge sehr gross ist.

    MfG
    GK

    PS: Ich habe nicht alle möglichen Handler für den xml-Parser gesetzt, da das Skript nur exemplarisch sein soll. Falls nötig kannst Du mit den xml_set_xxx_handler Funktionen andere Teile einer XML-Struktur verarbeiten.

    -------------------------------

    <?php
      class XmlParser
      {
        public function __construct()
        {
          $this->m_Parser = xml_parser_create();
          xml_set_element_handler($this->m_Parser, array($this, "startElement"), array($this, "endElement"));
          xml_set_character_data_handler($this->m_Parser, array($this, "characterDataHandler"));
        }

    public function parse($data)
        {
          $this->m_Result = array();
          $this->m_Stack = array();
          $this->m_TextNode = null;
          xml_parse($this->m_Parser, $data, true);
          return($this->m_Result[0]);
        }

    private function characterDataHandler($parser, $data)
        {
          if (is_null($this->m_TextNode))
          {
            $this->m_TextNode = new StdClass;
            $this->m_TextNode->text = $data;
          }
          else
          {
            $this->m_TextNode->text .= $data;
          }
        }

    private function startElement($parser, $name, $attrib)
        {
          if (!is_null($this->m_TextNode) && !preg_match("/^\s*$/", $this->m_TextNode->text))
            $this->AddNode($this->m_TextNode);
          $this->m_TextNode = null;
          $node->tagName = $name;
          foreach ($attrib as $key => $value)
            $node->$key = $value;
          array_unshift($this->m_Stack, $node);
        }

    private function endElement($parser, $name)
        {
          if (!is_null($this->m_TextNode) && !preg_match("/^\s*$/", $this->m_TextNode->text))
            $this->AddNode($this->m_TextNode);
          $this->m_TextNode = null;
          $node = array_shift($this->m_Stack);
          if (!is_null($node))
            $this->AddNode($node);
        }

    private function addNode($node)
        {
          if (0 == count($this->m_Stack))
          {
            array_push($this->m_Result, $node);
          }
          else
          {
            $parent = $this->m_Stack[0];
            if (is_array($parent->childNodes))
              array_push($parent->childNodes, $node);
            else
              $parent->childNodes = array($node);
            if (is_array($parent->childNodes[$node->tagName]))
              array_push($parent->childNodes[$node->tagName], $node);
            else if (isset($parent->childNodes[$node->tagName]))
              $parent->childNodes[$node->tagName] = array($parent->childNodes[$node->tagName], $node);
            else
              $parent->childNodes[$node->tagName] = $node;
          }
        }

    private $m_Result;
        private $m_Parser;
        private $m_Stack;
        private $m_TextNode;
      }

    $data = "irgendwelche XML Daten";
      $parser = new XmlParser();
      $result = $parser->parse($data);
      print_r($result);
    ?>

    1. Hallo,

      Du kannst vom unten angegebenen Skript ausgehen, welches eine XML-Datei in eine Objekthirarchie umsetzt. Durch geeignete Modifikationen solltest Du in der Lage sein, die Daten in die gewünschte Struktur zu bringen. Die Knoten werden auch unter dem Namen des Tags in m_ChildNodes abgelegt, so dass Du nicht gezwungen bist, über den Index zu gehen. Das setzt natürlich voraus, dass es nur ein Tag dieses Namens innerhalb solcher Container gibt. Andernfalls handelt es sich um ein Feld aller Kindknoten mit diesem Tagnamen, so dass das keinen Vorteil bringt.

      Allerdings kannst Du schon mit kleinen Änderungen an den XML-Daten eine Vereinfachung erreichen. Statt

      Danke Gerhard, eine Datenpflege hatte ich eigentlich nicht vor, auch weil die XML-Datei dadurch unübersichtlicher würde. Aber in Bezug auf mein Array macht es sicherlich, mal schaun. Werde jetzt erst mal testen, speziell wie es mit der Performance bei der anderen Lösung aussieht.

      bydey

      --
      -- bydey ist die Signatur und Verabschiedung, nicht der Nick --
      -- Navigate all your PHP web projects with  PHP Project Browser--