Witaj, Gościu O nas | Kontakt | Mapa
Wortal Forum PHPEdia.pl Planeta Kubek IRC Przetestuj się!

Konwersja XML do tablicy w JavaScript

Konwersja responseXML do tablicy

Zanim przystąpię do przedstawienia metody obiektu ToArray konwertującej obiekt XML na tablicę zastanówmy się nad podstawowymi założeniami funkcjonalnymi.

Metoda powinna umożliwiać parsowanie responseXML:

  1. o dowolnej nazwie elementu głównego i podelementów
  2. o nieograniczonej liczbie poziomów zagnieżdżeń poszczególnych elementów
  3. w którym elementy są poprzeplatane z węzłami tekstowymi, przy czym węzły tekstowe puste (wypełnione tylko białymi znakami) powinny być pomijane
  4. dając w wyniku tablicę asocjacyjną do, której można się odwoływać posługując się nazwami znaczników XML lub wartością atrybutów id poszczególnych elementów
var ToArray = {
  version: '1.0',

  str2array: function (string, separator) {/*...*/},
  
  isOnlyWhiteString: function (value) {/*...*/},

  xml2array: function (xml, byId) {
    if (xml.hasChildNodes() == true) {
      var nrChildren = xml.childNodes.length;
      var result = new Array();
      for(var i=0; i<nrChildren; i++) {
        var node = xml.childNodes[i];
        if(node.nodeName != '#text') {
          var key = node.nodeName;
          if(key!='xml') { // in view of IE
            if (byId == true && node.getAttribute('id')) {
              key = node.getAttribute('id');
            }
            result[key] = this.xml2array(node, byId);
          }
        } else {
          if (nrChildren > 1) {
            if(this.isOnlyWhiteString(node.nodeValue) == false) {
              result.push(node.nodeValue);
            }
          } else {
            var result = node.nodeValue;
          }
        }
      }
    }
    return result;
  }
}

Sposób realizacji wyżej wypunktowanych założeń w zaprezentowanej funkcji najłatwiej omówić na konkretnych przykładach.

Na początek coś prostego. Każdy element danego obiektu XML zawiera podelementy o unikalnej nazwie. Liczba zagłębień ogranicza się do trzech poziomów. Pojawia się jednak pewien problem. Elementy nie będące liściami (elementami niezawierającymi podelementy) przeplatają się z węzłami tekstowymi. Są to ciągi składające się jedynie z białych znaków (spacje, znaki przejścia do następnej linii), które należy usunąć jako zbędne.

<?php
header("Content-type: text/xml; charset=UTF-8");
echo '<?xml version="1.0" encoding="UTF-8"?>';
echo'<table>
    <tr1>
      <td1>Val 1</td1>
      <td2>Val 2</td2>
      <td3>Val 3</td3>
      <td4>Val 4</td4>
    </tr1>
    <tr2>
      <td1>Val 5</td1>
      <td2>Val 6</td2>
      <td3>Val 7</td3>
      <td4>Val 8</td4>
    </tr2>
</table>';
?>

Po natrafieniu na jakikolwiek węzeł tekstowy w elemencie nie będącym liściem moglibyśmy go zignorować, ale założyliśmy, że tekst może się przeplatać z elementami - co zaprezentowane zostanie na kolejnym przykładzie - dlatego pozostaje nam sprawdzenie każdego węzła pod kątem jego zawartości i pozostawienie jedynie tych tekstów, które są czymś więcej niż tylko zbiorem białych znaków. Wspomne tylko, że węzeł tekstowy od elementu można rozpoznać dzięki właściwości nodeName, która dla węzła, będącego tekstem zwróci wartość #text, a dla elementu jego nazwę (dla <table /> będzie to oczywiście "table"). W czyszczeniu ze zbędnych ciągów tekstowych przydadzą się wyrażenia regularne.

Pierwsze wyrażenie sprawdza czy w dany ciąg tekstowy zaczyna się od znaku karetki, tabulatora lub spacji natomiast druga reguła sprawdza czy w danym tekście jest jakikolwiek inny znak poza wyżej wymienionymi. Na ostateczny werdykt składają się wyniki obu testów. Metoda jest tak skonstruowana, że jeżeli zwróci true to znaczy, że przekazana do niej wartość jest jedynie ciągiem białych znaków. Przekazanie do metody ciągu pustego zwróci fals, ale w naszym przypadku to nie ma znaczenia gdyż pusty string nie spowoduje utworzenia węzła tekstowego. Użycie metody isOnlyWhiteString() jest tak naprawdę uzasadnione dopiero w przypadku drugiego przykładu, który jest omówiony nieco niżej.

var ToArray = {
  version: '1.0',

  str2array: function (string, separator) {/*...*/},

  isOnlyWhiteString: function (value) {
    var re = new RegExp("^[\n\r\t ]{1,}");
    var whitespaces = re.test(value);
    re = new RegExp("[^\n\r\t ]{1,}");
    var otherMarks = re.test(value);
    result = (whitespaces && !otherMarks) ? true : false;
    return result;
  }
}

Dzięki metodzie xml2array użycie responseXML nie może być już chyba prostsze.

function updateTable() {
  advAJAX.get({
    url: "table.xml.php",
    onSuccess : function(obj) {
      $("info_table").innerHTML = "Update success";
      var result = ToArray.xml2array(obj.responseXML);
      result = result['table'];
        $("tr1td1").innerHTML = result['tr1']['td1'];
        $("tr1td2").innerHTML = result['tr1']['td2'];
        $("tr1td3").innerHTML = result['tr1']['td3'];
        $("tr1td4").innerHTML = result['tr1']['td4'];
        $("tr2td1").innerHTML = result['tr2']['td1'];
        $("tr2td2").innerHTML = result['tr2']['td2'];
        $("tr2td3").innerHTML = result['tr2']['td3'];
        $("tr2td4").innerHTML = result['tr2']['td4'];
    }
  });
}

Dane te zostaną załadowane do tabeli. Proszę zwrócić uwagę na to że atrybuty id zostały przypisane tagom <span /> , a nie komórkom tabeli. Z jakichś powodów w Operze document.getElementById(id); zwracał wartość undefined kiedy atrybut id został nadany tagom<td>

<button onclick="updateTable();">Update table</button>
<table cellspacing="3" border="1">
<tbody>
  <tr>
    <td><span id="tr1td1">[?]</span></td>
    <td><span id="tr1td2">[?]</span></td>
    <td><span id="tr1td3">[?]</span></td>
    <td><span id="tr1td4">[?]</span></td>
  </tr>
  <tr>
    <td><span id="tr2td1">[?]</span></td>
    <td><span id="tr2td2">[?]</span></td>
    <td><span id="tr2td3">[?]</span></td>
    <td><span id="tr2td4">[?]</span></td>
  </tr>
</tbody>
</table>

Kolejny dokument XML jest już bardziej rozbudowany. Pojawia nam się omówiony już wcześniej problem przeplatania się węzłów tekstowych z elementami, z tym że teraz oprócz białych stringów mamy też teksty warte zachowania. Kolejną niedogodnością są elementy o identycznych nazwach. W ramach elementu głównego <doc /> utworzone zostały cztery podelementy <part />. Nie możemy zatem użyć nazw elementów jako wartości indeksów tablicy, gdyż powstałaby tablica["doc"]["part"], a my mamy przecież cztery elementy o nazwie part.

<?php
header("Content-type: text/xml; charset=UTF-8");
echo '<?xml version="1.0" encoding="UTF-8"?>';
echo'<doc>
  <title>Tytuł</title>
  <part id="entree">Wstęp</part>
  <part id="section_1">
    Wprowadzenie do rozdziału pierwszego
    <part id="subsection_1.1">Podrozdział pierwszy rozdziału pierwszego</part>
    <part id="subsection_1.2">Podrozdział drugi rozdziału pierwszego</part>
    Podsumowanie rozdziału pierwszego
  </part>
  <part id="section_2">
    Wprowadzenie do rozdziału drugiego
    <part id="subsection_2.1">Podrozdział pierwszy rozdziału drugiego</part>
    <part id="subsection_2.2">Podrozdział drugi rozdziału drugiego</part>
    Podsumowanie rozdziału drugiego
  </part>
  <part id="summary">Zakończenie</part>
</doc>';
?>

Kwestia odróżniania białych tekstów od pozostałych została już rozpracowana tymczasem wyłonił się kolejny problem związany z ciągami tekstowymi. O ile w liściach zawsze powstaje jeden węzeł, o tyle w elementach zawierających podelementy może powstać więcej węzłów, które należy od siebie odróżnić. Dlatego też do tekstu w elemencie <title>będziemy się odwoływać tablica["doc"]["title"], a do węzłów tekstowych np. w <part id="section_1"> należy się odwołać kolejno tablica["section_1"][0] i tablica["section_1"][1]. Zadajecie sobie teraz pytanie dlaczego tablica["section_1"], a nie tablica["part"]? Ano wiąże się to ze wspomnianym brakiem unikalności nazw podelementów w ramach pojedynczego elementu.

Aby funkcja konwertująca XMLa do postaci tablicowej brała jako indeks id elementu zamiast jego nazwę należy wywołać ją z drugim parametrem równym true. W takim wypadku metoda xml2array sprawdzi czy dany element posiada atrybut id i jeśli tak jest użyje jego wartości zamiast nazwy elementu do stworzenia indeksu w tablicy.

function updateDoc() {
  advAJAX.get({
    url: "doc.xml.php",
    onSuccess : function(obj) {
      $("info_doc").innerHTML = "Update success";
      var result = ToArray.xml2array(obj.responseXML, true);
      result = result['doc'];
      $("title").innerHTML = result['title'];
      $("entree").innerHTML = result['entree'];
      $("section_1_entree").innerHTML = result['section_1'][0];
      $("subsection_1.1").innerHTML = result['section_1']['subsection_1.1'];
      $("subsection_1.2").innerHTML = result['section_1']['subsection_1.2'];
      $("section_1_summary").innerHTML = result['section_1'][1];
      $("section_2_entree").innerHTML = result['section_2'][0];
      $("subsection_2.1").innerHTML = result['section_2']['subsection_2.1'];
      $("subsection_2.2").innerHTML = result['section_2']['subsection_2.2'];
      $("section_2_summary").innerHTML = result['section_2'][1];
      $("title").innerHTML = result['title'];
      $("summary").innerHTML = result['summary'];
    }
  });
}

Nie jest to naturalnie zbyt życiowy przykład bo ja osobiście mając pobrać za jednym zamachem spis treści obudowałbym go tagami HTML po stronie serwera i wysłał jako jeden ciąg tekstowy, ale nie o to chodzi.

<button onclick="updateDoc();">Update doc</button>
<div>
  <div id="title">[?]</div>
  <ol>
    <li id="entree">[?]</li>
    <li id="section_1">
      <p id="section_1_entree">[?]</p>
      <ul>
        <li id="subsection_1.1">[?]</li>
        <li id="subsection_1.2">[?]</li>
      </ul>
      <p id="section_1_summary">[?]</p>
    </li>
    <li id="section_2">
      <p id="section_2_entree">[?]</p>
      <ul>
        <li id="subsection_2.1">[?]</li>
        <li id="subsection_2.2">[?]</li>
      </ul>
      <p id="section_2_summary">[?]</p>
    </li>
    <li id="summary">[?]</li>
  </ol>
</div>
Informacje na podobny temat:
Wasze opinie
Wszystkie opinie użytkowników: (0)
Mentax.pl    NQ.pl- serwery z dodatkiem świętego spokoju...   
O nas | Kontakt | Mapa serwisu
Copyright (c) 2003-2024 php.pl    Wszystkie prawa zastrzeżone    Powered by eZ publish Content Management System eZ publish Content Management System