mozilla

Revision 61977 of Costruire e decostruire un documento XML

  • Slug della versione: Costruire_e_decostruire_un_documento_XML
  • Titolo della versione: Costruire e decostruire un documento XML
  • ID versione: 61977
  • Data di creazione
  • Autore: fusionchess
  • Versione corrente? No
  • Commento 102 words added, 41 words removed

Contenuto della versione

{{ T() }}

Mozilla gestisce ampiamente XML. Sono gestite diverse Raccomandazioni e bozze del World Wide Web Consortium (W3C) per la famiglia XML, così come altre tecnologie relative.

Tra le altre tecnologie, Mozilla offre queste tecnologie native per lavorare con documenti XML:

  • XPath per indirizzare parti diverse di un documento XML
  • XMLSerializer per convertire alberi DOM in stringhe o files
  • DOMParser costruire un documento XML convertendo delle stringhe in alberi DOM
  • XMLHttpRequest to parse XML from files into DOM trees. Although DOMParser does have a method named parseFromStream(), it's actually easier to use XMLHttpRequest which works for remote (not limited to HTTP) and local files.

È possibile comunque creare manualmente propri algoritmi per la conversione.

Costruire un albero DOM

Di seguito verranno elencate varie strade per costruire un albero DOM a partire da una stringa di codice sorgente, a partire da file o a partire da strutture di differente natura

Creare dinamicamente un albero DOM di un documento XML

Questo paragrafo illustra come utilizzare l'API DOM Core con JavaScript per creare e modificare oggetti DOM. It applies to all Gecko-based applications (such as Firefox) both in privileged (extensions) and unprivileged (web pages) code.

Creare un albero DOM manualmente

Si consideri il seguente documento XML:

<?xml version="1.0"?>
<people>
  <person first-name="eric" middle-initial="H" last-name="jung">
    <address street="321 south st" city="denver" state="co" country="usa"/>
    <address street="123 main st" city="arlington" state="ma" country="usa"/>
  </person>

  <person first-name="jed" last-name="brown">
    <address street="321 north st" city="atlanta" state="ga" country="usa"/>
    <address street="123 west st" city="seattle" state="wa" country="usa"/>
    <address street="321 south avenue" city="denver" state="co" country="usa"/>
  </person>
</people>

L'API W3C DOM, supportata da Mozilla, can be used to create an in-memory representation of this document like so:

var doc = document.implementation.createDocument("", "", null);
var peopleElem = doc.createElement("people");

var personElem1 = doc.createElement("person");
personElem1.setAttribute("first-name", "eric");
personElem1.setAttribute("middle-initial", "h");
personElem1.setAttribute("last-name", "jung");

var addressElem1 = doc.createElement("address");
addressElem1.setAttribute("street", "321 south st");
addressElem1.setAttribute("city", "denver");
addressElem1.setAttribute("state", "co");
addressElem1.setAttribute("country", "usa");
personElem1.appendChild(addressElem1);

var addressElem2 = doc.createElement("address");
addressElem2.setAttribute("street", "123 main st");
addressElem2.setAttribute("city", "arlington");
addressElem2.setAttribute("state", "ma");
addressElem2.setAttribute("country", "usa");
personElem1.appendChild(addressElem2);

var personElem2 = doc.createElement("person");
personElem2.setAttribute("first-name", "jed");
personElem2.setAttribute("last-name", "brown");

var addressElem3 = doc.createElement("address");
addressElem3.setAttribute("street", "321 north st");
addressElem3.setAttribute("city", "atlanta");
addressElem3.setAttribute("state", "ga");
addressElem3.setAttribute("country", "usa");
personElem2.appendChild(addressElem3);

var addressElem4 = doc.createElement("address");
addressElem4.setAttribute("street", "123 west st");
addressElem4.setAttribute("city", "seattle");
addressElem4.setAttribute("state", "wa");
addressElem4.setAttribute("country", "usa");
personElem2.appendChild(addressElem4);

var addressElem5 = doc.createElement("address");
addressElem5.setAttribute("street", "321 south avenue");
addressElem5.setAttribute("city", "denver");
addressElem5.setAttribute("state", "co");
addressElem5.setAttribute("country", "usa");
personElem2.appendChild(addressElem5);

peopleElem.appendChild(personElem1);
peopleElem.appendChild(personElem2);
doc.appendChild(peopleElem);

Si veda anche Il capitolo sul DOM del Tutorial XUL (in inglese).

So what?

DOM trees can be queried using XPath expressions, converted to strings or written to a local or remote files using XMLSerializer (without having to first convert to a string), POSTed to a web server (via XMLHttpRequest), transformed using XSLT, XLink'd to, etc.

You can use DOM trees to model data which isn't well-suited for RDF (or perhaps you just don't like RDF). Another application is that, since XUL is XML, the UI of your application can be dynamically manipulated, downloaded, uploaded, saved, loaded, converted, or transformed quite easily.

Costruire un albero DOM XML a partire da stringhe di codice sorgente

var sMyString = "<a id=\"a\"><b id=\"b\">hey!<\/b><\/a>";
var oParser = new DOMParser();
var oDOM = oParser.parseFromString(sMyString, "text\/xml");
// print the name of the root element or error message
dump(oDOM.documentElement.nodeName == "parsererror" ? "error while parsing" : oDOM.documentElement.nodeName);

Tutorial su come rendere questo codice cross browser (in inglese)

Costruire un albero DOM a partire da un file

Usando XMLHttpRequest

Come già precedentemente accennato, sebbene anche ciascuna istanza di DOMParser possegga un metodo chiamato parseFromStream(), è più facile utilizzare XMLHttpRequest per parsare documenti XML in alberi DOM (XMLHttpRequest funziona bene sia in locale che in remoto). Qui c'è un codice di esempio che legge e parsa in un albero DOM un file XML locale:

var oXHR = new XMLHttpRequest();
oXHR.open("GET", "chrome://passwdmaker/content/people.xml", false);
oXHR.send(null);
// print the name of the root element or error message
var oDOM = oXHR.responseXML;
dump(oDOM.documentElement.nodeName == "parsererror" ? "error while parsing" : oDOM.documentElement.nodeName);

req.responseXML is a Document instance.

Usando l'elemento {{ HTMLElement("object") }}.

Here is another possible way, using the {{ HTMLElement("object") }} tag. For this demo, you must create an external document called purchase_order.xml:

<!DOCTYPE html>
<html>
<head>
<title>XML Data Block Demo</title>
<script>
function runDemo() {
  var doc = document.getElementById("purchase-order").contentDocument;
  var lineItems = doc.getElementsByTagNameNS("http://example.mozilla.org/PurchaseOrderML", "lineItem");
  var firstPrice = lineItems[0].getElementsByTagNameNS("http://example.mozilla.org/PurchaseOrderML", "price")[0].textContent;
  document.getElementById("output-box").textContent = "The purchase order contains " + lineItems.length + " line items. The price of the first line item is " + firstPrice + ".";
}
</script>
</head>
<body onload="runDemo()";>
<object id="purchase-order" data="purchase_order.xml" type="text/xml" style="display: none;"></object>
<div id="output-box">Demo did not run</div>
</body>
</html>

Per ulteriori approfondimenti, si rimanda all'articolo: Usare le XML Data Islands in Mozilla.

Decostruire un albero DOM

Di seguito verranno elencate varie strade per leggere o convertire un albero DOM.

Convertire un documento XML in stringhe di codice sorgente

Per prima cosa procuriamoci un albero DOM. È possibile costruirlo manualmente (si veda: Come creare un albero DOM) o, alternativamente, ottenerlo tramite XMLHttpRequest.

Adesso proviamo a ottenere dalla variabile doc — il nostro albero DOM — una stringa di codice sorgente:

var oSerializer = new XMLSerializer();
var sXML = oSerializer.serializeToString(doc);

Non è possibile creare un istanza di XMLSerializer (ovvero lanciare: new XMLSerializer()) né dall'interno di un componente JS XPCOM, né dall'interno di un modulo. Per farlo bisogna utilizzare:

var oSerializer = Components.classes["@mozilla.org/xmlextras/xmlserializer;1"].createInstance(Components.interfaces.nsIDOMSerializer);
var sXML = oSerializer.serializeToString(doc);

Come ottenere stringhe di codice sorgente di facile lettura

You can pretty print a DOM tree using XMLSerializer and E4X. First, create a DOM tree as described in the Come creare un albero DOM article. Alternatively, use a DOM tree obtained from XMLHttpRequest. We assume it's in the doc variable.

var oSerializer = new XMLSerializer();
var sPrettyXML = XML(oSerializer.serializeToString(doc)).toXMLString();

Indents are provided with two spaces. You can, of course, use DOM:treeWalker to write your own, more performant version which also has the advantage that you can customize the indent string to be whatever you like.

Note: When using the E4X toXMLString method your CDATA elements will be lost and only the containing text remains. So using the above method might not be useful if you have CDATA elements in your XML.

<content><![CDATA[This is the content]]></content>

Will become

<content>This is the content</content>

Convertire un foglio XML in un albero di oggetti Javascript (JXON)

JXON (lossless Javascript XML Object Notation) è un nome generico col quale viene definita la rappresentazione di oggetti Javascript in linguaggio XML. Non esistono veri e propri standard per questa rappresentazione, ma da poco tempo a questa parte cominciano ad affacciarsiin rete alcune convenzioni.

Immaginiamo di aver parsato, come al solito nella nostra variabile doc, questo documento XML di esempio:

esempio.xml
<?xml version="1.0"?>
<!DOCTYPE catalog SYSTEM "catalog.dtd">
<catalog>
   <product description="Cardigan Sweater">
      <catalog_item gender="Men's">
         <item_number>QWZ5671</item_number>
         <price>39.95</price>
         <size description="Medium">
            <color_swatch image="red_cardigan.jpg">Red</color_swatch>
            <color_swatch image="burgundy_cardigan.jpg">Burgundy</color_swatch>
         </size>
         <size description="Large">
            <color_swatch image="red_cardigan.jpg">Red</color_swatch>
            <color_swatch image="burgundy_cardigan.jpg">Burgundy</color_swatch>
         </size>
      </catalog_item>
      <catalog_item gender="Women's">
         <item_number>RRX9856</item_number>
         <discount_until>Dec 25, 1995</discount_until>
         <price>42.50</price>
         <size description="Medium">
            <color_swatch image="black_cardigan.jpg">Black</color_swatch>
         </size>
      </catalog_item>
   </product>
   <script type="text/javascript"><![CDATA[function matchwo(a,b) {
    if (a < b && a < 0) { return 1; }
    else { return 0; }
}]]></script>
</catalog>

Adesso proviamo a ottenere una rappresentazione della variabile doc — l'albero DOM — attraverso un albero di oggetti Javascript (you can read more about working with Objects and how Javascript is Object-Oriented). Per far ciò possiamo utilizzare diversi algoritmi di conversione.

Per semplicità gli algoritmi qui proposti (si veda: #1, #2, #3, #4) prenderanno in considerazione solamente i seguenti tipi di nodi e i loro attributi:

  1. Document (solo come argomento della funzione),
  2. DocumentFragment (solo come argomento della funzione),
  3. Element,
  4. Text (mai come argomento della funzione),
  5. CDATASection (mai come argomento della funzione).

Si tratta di un buon compromesso per un uso Javascript, dacché la gran parte delle informazioni di un documento XML è contenuta in questo tipo di nodi. Ogni altra informazione (come processing instructions, xml schemas, commenti, etc.) andrà persa. Allo scopo di evitare conflitti, la lettura dei nomi dei nodi e dei loro attributi è case insensitive (resa sempre in minuscolo) e di conseguenza le proprietà locali dell'albero di oggetti così ottenuto aggiunte via JavaScript dovranno avere un qualche tipo di lettera maiuscola al loro interno, per evitare di sovrascrivere le proprietà ottenute dal foglio XML, come si può vedere di seguito. I seguenti algoritmi sono liberamente basati sulla Convenzione di Parker, versione 0.4, che prevede il riconoscimento del typeof del contenuto di testo di ogni singolo nodo letto.

Algoritmo #1: una via prolissa

Questo semplice costruttore ricorsivo converte un albero DOM XML in un albero di oggetti Javascript. Il contenuto di testo di ogni nodo è salvato all'interno della proprietà keyValue, mentre i nodeAttributes, se esistono, sono annidati sotto l'oggetto-figlio keyAttributes. L'argomento del costruttore può essere l'intero Document, un DocumentFragment o, più semplicemente, un nodo di tipo Element di esso.

function buildValue(sValue) {
  if (/^\s*$/.test(sValue)) { return null; }
  if (/^(true|false)$/i.test(sValue)) { return sValue.toLowerCase() === "true"; }
  if (isFinite(sValue)) { return parseFloat(sValue); }
  if (isFinite(Date.parse(sValue))) { return new Date(sValue); }
  return sValue;
}

function XMLData (oXMLParent) {
  var nAttrLen = 0, nLength = 0, sCollectedTxt = "";
  // children
  if (oXMLParent.hasChildNodes()) {
    for (var oItChild, sItKey, sItVal, nChildId = 0; nChildId < oXMLParent.childNodes.length; nChildId++) {
      oItChild = oXMLParent.childNodes.item(nChildId);
      if ((oItChild.nodeType - 1 | 1) === 3) { sCollectedTxt += oItChild.nodeType === 3 ? oItChild.nodeValue.replace(/^\s+|\s+$/g, "") : oItChild.nodeValue; } // nodeType is "Text" (3) or "CDATASection" (4)
      else if (oItChild.nodeType === 1 && !oItChild.prefix) { // nodeType is "Element" (1)
        sItKey = oItChild.nodeName.toLowerCase();
        sItVal = new XMLData(oItChild);
        if (this.hasOwnProperty(sItKey)) {
          if (this[sItKey].constructor !== Array) { this[sItKey] = [this[sItKey]]; }
          this[sItKey].push(sItVal);
        } else { this[sItKey] = sItVal; nLength++; }
      }
    }
    this.keyValue = buildValue(sCollectedTxt);
  } else { this.keyValue = null; }
  // node attributes
  if (oXMLParent.hasAttributes()) {
    var oItAttr;
    this.keyAttributes = {};
    for (nAttrLen; nAttrLen < oXMLParent.attributes.length; nAttrLen++) {
      oItAttr = oXMLParent.attributes.item(nAttrLen);
      this.keyAttributes[oItAttr.nodeName.toLowerCase()] = buildValue(oItAttr.nodeValue);
    }
  }
  // optional properties and methods; you could safely adjoust/remove them...
  this.keyLength = nLength;
  this.attributesLength = nAttrLen;
  // this.DOMNode = oXMLParent;
  this.valueOf = function() { return this.keyValue; };
  this.toString = function() { return String(this.keyValue); };
  this.getItem = function(nItem) {
    if (nLength === 0) { return null; }
    var iItem = 0;
    for (var sKeyName in this) { if (iItem === nItem) { return this[sKeyName]; } iItem++; }
    return null;
  };
  this.getAttribute = function(nAttrib) {
    if (nAttrLen === 0 || nAttrib + 1 > nAttrLen) { return null; }
    var nItAttr = 0;
    for (var sAttrName in this.keyAttributes) { if (nItAttr === nAttrib) { return this.keyAttributes[sAttrName]; } nItAttr++; }
    return null;
  };
  this.hasChildren = function() { return this.keyLength > 0; };
}

var myObject = new XMLData(doc);
// abbiamo ottenuto il nostro albero di oggetti Javascript! provare per credere: alert(JSON.stringify(myObject));

Con questo algoritmo il nostro esempio diventerà:

{
  "catalog": {
    "product": {
      "catalog_item": [{
        "item_number": {
          "keyValue": "QWZ5671",
          "keyLength": 0,
          "attributesLength": 0
        },
        "price": {
          "keyValue": 39.95,
          "keyLength": 0,
          "attributesLength": 0
        },
        "size": [{
          "color_swatch": [{
            "keyValue": "Red",
            "keyAttributes": {
              "image": "red_cardigan.jpg"
            },
            "keyLength": 0,
            "attributesLength": 1
          }, {
            "keyValue": "Burgundy",
            "keyAttributes": {
              "image": "burgundy_cardigan.jpg"
            },
            "keyLength": 0,
            "attributesLength": 1
          }],
          "keyValue": null,
          "keyAttributes": {
            "description": "Medium"
          },
          "keyLength": 1,
          "attributesLength": 1
        }, {
          "color_swatch": [{
            "keyValue": "Red",
            "keyAttributes": {
              "image": "red_cardigan.jpg"
            },
            "keyLength": 0,
            "attributesLength": 1
          }, {
            "keyValue": "Burgundy",
            "keyAttributes": {
              "image": "burgundy_cardigan.jpg"
            },
            "keyLength": 0,
            "attributesLength": 1
          }],
          "keyValue": null,
          "keyAttributes": {
            "description": "Large"
          },
          "keyLength": 1,
          "attributesLength": 1
        }],
        "keyValue": null,
        "keyAttributes": {
          "gender": "Men's"
        },
        "keyLength": 3,
        "attributesLength": 1
      }, {
        "item_number": {
          "keyValue": "RRX9856",
          "keyLength": 0,
          "attributesLength": 0
        },
        "discount_until": {
          "keyValue": new Date(1995, 11, 25),
          "keyLength": 0,
          "attributesLength": 0
        },
        "price": {
          "keyValue": 42.5,
          "keyLength": 0,
          "attributesLength": 0
        },
        "size": {
          "color_swatch": {
            "keyValue": "Black",
            "keyAttributes": {
              "image": "black_cardigan.jpg"
            },
            "keyLength": 0,
            "attributesLength": 1
          },
          "keyValue": null,
          "keyAttributes": {
            "description": "Medium"
          },
          "keyLength": 1,
          "attributesLength": 1
        },
        "keyValue": null,
        "keyAttributes": {
          "gender": "Women's"
        },
        "keyLength": 4,
        "attributesLength": 1
      }],
      "keyValue": null,
      "keyAttributes": {
        "description": "Cardigan Sweater"
      },
      "keyLength": 1,
      "attributesLength": 1
    },
    "script": {
      "keyValue": "function matchwo(a,b) {\n  if (a < b && a < 0) { return 1; }\n  else { return 0; }\n}",
      "keyAttributes": {
        "type": "text/javascript"
      },
      "keyLength": 0,
      "attributesLength": 1
    },
    "keyValue": null,
    "keyLength": 2,
    "attributesLength": 0
  },
  "keyValue": null,
  "keyLength": 1,
  "attributesLength": 0
}

È un approccio raccomandato nel caso in cui ci sia completamente ignota la struttura del documento XML che andremo a leggere.

Algoritmo #2: una via un po' meno prolissa

Quello che segue è un altro, più semplice, metodo di conversione. Dove i nodeAttributes sono annidati nello stesso oggetto dei nodi figli ma, a differenza di questi, contrassegnati dal prefisso “@”. As above, the text content is stored into the keyValue property. The constructor's argument can be the entire XML Document, a DocumentFragment or simply an Element node of it.

function buildValue(sValue) {
  if (/^\s*$/.test(sValue)) { return null; }
  if (/^(true|false)$/i.test(sValue)) { return sValue.toLowerCase() === "true"; }
  if (isFinite(sValue)) { return parseFloat(sValue); }
  if (isFinite(Date.parse(sValue))) { return new Date(sValue); }
  return sValue;
}

function XMLData (oXMLParent) {
  if (oXMLParent.hasChildNodes()) {
    var sCollectedTxt = "";
    for (var oItChild, sItKey, sItVal, nChildId = 0; nChildId < oXMLParent.childNodes.length; nChildId++) {
      oItChild = oXMLParent.childNodes.item(nChildId);
      if ((oItChild.nodeType - 1 | 1) === 3) { sCollectedTxt += oItChild.nodeType === 3 ? oItChild.nodeValue.replace(/^\s+|\s+$/g, "") : oItChild.nodeValue; }
      else if (oItChild.nodeType === 1 && !oItChild.prefix) {
        sItKey = oItChild.nodeName.toLowerCase();
        sItVal = new XMLData(oItChild);
        if (this.hasOwnProperty(sItKey)) {
          if (this[sItKey].constructor !== Array) { this[sItKey] = [this[sItKey]]; }
          this[sItKey].push(sItVal);
        } else { this[sItKey] = sItVal; }
      }
    }
    if (sCollectedTxt) { this.keyValue = buildValue(sCollectedTxt); }
  }
  if (oXMLParent.hasAttributes()) {
    var oItAttr;
    for (var iAttrId = 0; iAttrId < oXMLParent.attributes.length; iAttrId++) {
      oItAttr = oXMLParent.attributes.item(iAttrId);
      this["@" + oItAttr.nodeName.toLowerCase()] = buildValue(oItAttr.nodeValue);
    }
  }
}

var myObject = new XMLData(doc);
// abbiamo ottenuto il nostro albero di oggetti Javascript! provare per credere: alert(JSON.stringify(myObject));

Con questo algoritmo il nostro esempio diventerà:

{
  "catalog": {
    "product": {
      "catalog_item": [{
        "item_number": {
          "keyValue": "QWZ5671"
        },
        "price": {
          "keyValue": 39.95
        },
        "size": [{
          "color_swatch": [{
            "keyValue": "Red",
            "@image": "red_cardigan.jpg"
          }, {
            "keyValue": "Burgundy",
            "@image": "burgundy_cardigan.jpg"
          }],
          "@description": "Medium"
        }, {
          "color_swatch": [{
            "keyValue": "Red",
            "@image": "red_cardigan.jpg"
          }, {
            "keyValue": "Burgundy",
            "@image": "burgundy_cardigan.jpg"
          }],
          "@description": "Large"
        }],
        "@gender": "Men's"
      }, {
        "item_number": {
          "keyValue": "RRX9856"
        },
        "discount_until": {
          "keyValue": new Date(1995, 11, 25)
        },
        "price": {
          "keyValue": 42.5
        },
        "size": {
          "color_swatch": {
            "keyValue": "Black",
            "@image": "black_cardigan.jpg"
          },
          "@description": "Medium"
        },
        "@gender": "Women's"
      }],
      "@description": "Cardigan Sweater"
    },
    "script": {
      "keyValue": "function matchwo(a,b) {\n  if (a < b && a < 0) { return 1; }\n  else { return 0; }\n}",
      "@type": "text/javascript"
    }
  }
}

It is a possible way if you partially know the structure of the XML document.

Algoritmo #3: una via sintetica

Here is another method of conversion. This algorithm is the closest to the Convenzione di Parker. It is very similar to the previous one, except that nodes which do not contain other recognizable nodes than Text or CDATASection are not treated as objects, but directly as booleans, strings, numbers or Date objects (see the Convenzione di Parker). Empty nodes (i.e. which do not contain other Element nodes, Text nodes or CDATASection nodes) have the default value true. Also, this time it is not used a constructor, but a simple function. The function's argument can be the entire XML Document, a DocumentFragment or simply an Element node of it.

In many cases this one represents the most practical conversion method.

function buildValue(sValue) {
  if (/^\s*$/.test(sValue)) { return null; }
  if (/^(true|false)$/i.test(sValue)) { return sValue.toLowerCase() === "true"; }
  if (isFinite(sValue)) { return parseFloat(sValue); }
  if (isFinite(Date.parse(sValue))) { return new Date(sValue); }
  return sValue;
}

function getXMLData (oXMLParent) {
  var vResult = /* put here the default value for empty nodes! */ true, nLength = 0, sCollectedTxt = "";
  if (oXMLParent.hasAttributes()) {
    vResult = {};
    for (nLength; nLength < oXMLParent.attributes.length; nLength++) {
      oItAttr = oXMLParent.attributes.item(nLength);
      vResult["@" + oItAttr.nodeName.toLowerCase()] = buildValue(oItAttr.nodeValue.replace(/^\s+|\s+$/g, ""));
    }
  }
  if (oXMLParent.hasChildNodes()) {
    for (var oItChild, sItKey, sItVal, nChildId = 0; nChildId < oXMLParent.childNodes.length; nChildId++) {
      oItChild = oXMLParent.childNodes.item(nChildId);
      if (oItChild.nodeType === 4) { sCollectedTxt += oItChild.nodeValue; } /* nodeType is "CDATASection" (4) */
      else if (oItChild.nodeType === 3) { sCollectedTxt += oItChild.nodeValue.replace(/^\s+|\s+$/g, ""); } /* nodeType is "Text" (3) */
      else if (oItChild.nodeType === 1 && !oItChild.prefix) { /* nodeType is "Element" (1) */
         if (nLength === 0) { vResult = {}; }
        sItKey = oItChild.nodeName.toLowerCase();
        sItVal = getXMLData(oItChild);
        if (vResult.hasOwnProperty(sItKey)) {
          if (vResult[sItKey].constructor !== Array) { vResult[sItKey] = [vResult[sItKey]]; }
          vResult[sItKey].push(sItVal);
        } else { vResult[sItKey] = sItVal; nLength++; }
      }
     }
  }
  if (sCollectedTxt) { nLength > 0 ? vResult.keyValue = buildValue(sCollectedTxt) : vResult = buildValue(sCollectedTxt); }
  /* if (nLength > 0) { Object.freeze(vResult); } */
  return vResult;
}

var myObject = getXMLData(doc);
// abbiamo ottenuto il nostro albero di oggetti Javascript! provare per credere: alert(JSON.stringify(myObject));
Note: If you want to freeze the whole object tree (because of the "static" nature of a XML document), uncomment the string: /* if (nLength > 0) { Object.freeze(vResult); } */. The Object.freeze method prevents new properties from being added to it, prevents existing properties from being removed and prevents existing properties, or their enumerability, configurability, or writability, from being changed. In essence the object tree is made effectively immutable.

Con questo algoritmo il nostro esempio diventerà:

{
  "catalog": {
    "product": {
      "@description": "Cardigan Sweater",
      "catalog_item": [{
        "@gender": "Men's",
        "item_number": "QWZ5671",
        "price": 39.95,
        "size": [{
          "@description": "Medium",
          "color_swatch": [{
            "@image": "red_cardigan.jpg",
            "keyValue": "Red"
          }, {
            "@image": "burgundy_cardigan.jpg",
            "keyValue": "Burgundy"
          }]
        }, {
          "@description": "Large",
          "color_swatch": [{
            "@image": "red_cardigan.jpg",
            "keyValue": "Red"
          }, {
            "@image": "burgundy_cardigan.jpg",
            "keyValue": "Burgundy"
          }]
        }]
      }, {
        "@gender": "Women's",
        "item_number": "RRX9856",
        "discount_until": new Date(1995, 11, 25),
        "price": 42.5,
        "size": {
          "@description": "Medium",
          "color_swatch": {
            "@image": "black_cardigan.jpg",
            "keyValue": "Black"
          }
        }
      }]
    },
    "script": {
      "@type": "text/javascript",
      "keyValue": "function matchwo(a,b) {\n  if (a < b && a < 0) { return 1; }\n  else { return 0; }\n}"
    }
  }
}

It is a recommanded way if you know the structure of the XML document.

Algoritmo #4: una via davvero minimalista

The following is another possible way of conversion. It is very close to the Convenzione di Parker, too. With this algorithm all Element nodes which contain other child Element nodes and Text or CDATASection nodes in the same level are treated as instances of Boolean, Number, String, or Date Constructors. So any child Element node, if exists, will be nested in these types of objects.

For example:

<employee type="usher">John Smith</employee>
<manager>Lisa Carlucci</manager>

will become

var myObject = {
  "employee": new String("John Smith"),
  "manager": "Lisa Carlucci"
};

myObject.employee["@type"] = "usher";

// test

alert(myObject.manager); // "Lisa Carlucci"
alert(myObject.employee["@type"]); // "usher"
alert(myObject.employee); // "John Smith"

As for the third algorithm, nodes which do not contain other recognizable nodes than Text or CDATASection are not treated as objects, but directly as booleans, strings, numbers (primitive values) or Date objects; and empty nodes (i.e. which do not contain other Element nodes, Text nodes or CDATASection nodes) have the default value true. As for the third algorithm it is not used a constructor, but a simple function. The function's argument can be the entire XML Document, a DocumentFragment or simply an Element node of it.

function buildValue (sValue) {
  if (/^\s*$/.test(sValue)) { return null; }
  if (/^(true|false)$/i.test(sValue)) { return sValue.toLowerCase() === "true"; }
  if (isFinite(sValue)) { return parseFloat(sValue); }
  if (isFinite(Date.parse(sValue))) { return new Date(sValue); }
  return sValue;
}

function objectify (vValue) {
  if (vValue === null) {
    return new (function() {
      this.toString = function() { return "null"; }
      this.valueOf = function() { return null; }
    })();
  }
  return vValue instanceof Object ? vValue : new vValue.constructor(vValue);
}

var aTmpEls = []; // loaded element nodes cache

function getXMLData (oXMLParent) {
  var  sItKey, sItVal, vResult, nLength = 0, nLevelStart = aTmpEls.length,
       nChildren = oXMLParent.hasChildNodes() ? oXMLParent.childNodes.length : 0, sCollectedTxt = "";

  for (var oItChild, nChildId = 0; nChildId < nChildren; nChildId++) {
    oItChild = oXMLParent.childNodes.item(nChildId);
    if (oItChild.nodeType === 4) { sCollectedTxt += oItChild.nodeValue; } /* nodeType is "CDATASection" (4) */
    else if (oItChild.nodeType === 3) { sCollectedTxt += oItChild.nodeValue.replace(/^\s+|\s+$/g, ""); } /* nodeType is "Text" (3) */
    else if (oItChild.nodeType === 1 && !oItChild.prefix) { aTmpEls.push(oItChild); } /* nodeType is "Element" (1) */
  }

  var nLevelEnd = aTmpEls.length, vBuiltVal = buildValue(sCollectedTxt);

  if (oXMLParent.hasAttributes()) {
    vResult = objectify(vBuiltVal);
    for (nLength; nLength < oXMLParent.attributes.length; nLength++) {
      oItAttr = oXMLParent.attributes.item(nLength);
      vResult["@" + oItAttr.nodeName.toLowerCase()] = buildValue(oItAttr.nodeValue.replace(/^\s+|\s+$/g, ""));
    }
  } else if (nLevelEnd > nLevelStart) { vResult = objectify(vBuiltVal); }

  for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
    sItKey = aTmpEls[nElId].nodeName.toLowerCase();
    sItVal = getXMLData(aTmpEls[nElId]);
    if (vResult.hasOwnProperty(sItKey)) {
    if (vResult[sItKey].constructor !== Array) { vResult[sItKey] = [vResult[sItKey]]; }
      vResult[sItKey].push(sItVal);
    } else { vResult[sItKey] = sItVal; nLength++; }
  }

  aTmpEls.length = nLevelStart;

  if (nLength === 0) { vResult = sCollectedTxt ? vBuiltVal : /* put here the default value for empty nodes: */ true; }
  /* else { Object.freeze(vResult); } */

  return vResult;
}

var myObject = getXMLData(doc);
alert(myObject.catalog.product.catalog_item[1].size.color_swatch["@image"]); // "black_cardigan.jpg"
alert(myObject.catalog.product.catalog_item[1].size.color_swatch); // "Black" !
Note: If you want to freeze the whole object tree (because of the "static" nature of a XML document), uncomment the string: /* else { Object.freeze(vResult); } */. The Object.freeze method prevents new properties from being added to it, prevents existing properties from being removed and prevents existing properties, or their enumerability, configurability, or writability, from being changed. In essence the object tree is made effectively immutable.

It is a possible way if you know the structure of the XML document.

La Convenzione di Parker

The functions listed above for the conversion of an XML document to JSON (often called «JXON algorithms») are more or less freely based on the Parker Convention. It is called “Parker Convention” in opposition to “BadgerFish Convention”, after the comic Parker & Badger by Cuadrado. See also: BadgerFish Convention.

The following is a transcription of the Parker Convention paper (version 0.4), from the page “TransformingRules” of the xml2json-xslt project site.

This Convention was written in order to regulate the conversion to JSON from XSLT, so parts of it are futile for Javascript.

Conversione in JSON
  1. The root element will be absorbed, for there is only one:

    <root>test</root>

    becomes

    "test"
    
  2. Element names become object properties:

    <root><name>Xml</name><encoding>ASCII</encoding></root>

    becomes

    {
      "name": "Xml",
      "encoding": "ASCII"
    }
    
  3. Numbers are recognized (integers and decimals):

    <root><age>12</age><height>1.73</height></root>

    becomes

    {
      "age": 12,
      "height": 1.73
    }
    
  4. Booleans are recognized case insensitive:

    <root><checked>True</checked><answer>FALSE</answer></root>

    becomes

    {
      "checked": true,
      "answer": false
    }
    
  5. Strings are escaped:

    <root>Quote: &quot; New-line:
    </root>
    

    becomes

    "Quote: \" New-line:\n"
  6. Empty elements will become null:

    <root><nil/><empty></empty></root>

    becomes

    {
      "nil": null,
      "empty": null
    }
    
  7. If all sibling elements have the same name, they become an array

    <root><item>1</item><item>2</item><item>three</item></root>
    

    becomes

    [1, 2, "three"]
    
  8. Mixed mode text-nodes, comments and attributes get absorbed:

    <root version="1.0">testing<!--comment--><elementtest="true">1</element></root>
    

    becomes

    { "element": true }
    
  9. Namespaces get absorbed, and prefixes will just be part of the property name:

    <root xmlns:ding="http://zanstra.com/ding"><ding:dong>binnen</ding:dong></root>
    

    becomes

    { "ding:dong" : "binnen" }
    
Note: Our algorithms comply with the points 2, 3, 4 and 7. The third and the fourth algorithm comply also with the point 6 (but true instead of null). The point 5 is automatically managed by the Javascript method JSON.stringify.
Appendice Javascript

All the same as the JSON translation, but with these extra's:

  1. Property names are only escaped when necessary

    <root><while>true</while><wend>false</wend><only-if/></root>

    becomes

    {
      "while": true,
      wend: false,
      "only-if": null
    }
    
  2. Within a string, closing elements "</" are escaped as "<\/"

    <root><![CDATA[<script>alert("YES");</script>]]></root>

    becomes

    { script: "<script>alert(\"YES\")<\/script>" }
    
  3. Dates are created as new Date() objects

    <root>2006-12-25</root>

    becomes

    new Date(2006, 12 - 1, 25)
    
  4. Attributes and comments are shown as comments (for testing-purposes):

    <!--testing--><root><test version="1.0">123</test></root>
    

    becomes

    /* testing */ { test /* @version = "1.0" */ : 123}
    
  5. A bit of indentation is done, to keep things ledgible

Note: Our algorithms comply with the point 3 (but without month decrease). The points 1 and 2 are automatically managed by the Javascript method JSON.stringify.

In sintesi

Let's take the third algorithm as the most representative JXON parsing algorithm. A single structured XML Element might have eight different configurations:

  1. an empty element,
  2. an element with pure text content,
  3. an empty element with attributes,
  4. an element with text content and attributes,
  5. an element containing elements with different names,
  6. an element containing elements with identical names,
  7. an element containing elements and contiguous text,
  8. an element containing elements and non contiguous text.

The following table shows the corresponding conversion patterns between XML and JSON according to the third algorithm.

Case XML JSON Javascript access
1 <animal/> "animal": true myObject.animal
2 <animal>text</animal> "animal": "text" myObject.animal
3 <animal name="value" /> "animal": {"@name": "value"} myObject.animal["@name"]
4 <animal name="value">text</animal> "animal": { "@name": "value", "keyValue": "text" } myObject.animal["@name"], myObject.animal.keyValue
5 <animal> <dog>Charlie</dog> <cat>Deka</cat> </animal> "animal": { "dog": "Charlie", "cat": "Deka" } myObject.animal.dog, myObject.animal.cat
6 <animal> <dog>Charlie</dog> <dog>Mad Max</dog> </animal> "animal": { "dog": ["Charlie", "Mad Max"] } myObject.animal.dog[0], myObject.animal.dog[1]
7 <animal> in my house <dog>Charlie</dog> </animal> "animal": { "keyValue": "in my house", "dog": "Charlie" } myObject.animal.keyValue, myObject.animal.dog
8 <animal> in my ho <dog>Charlie</dog> use </animal> "animal": { "keyValue": "in my house", "dog": "Charlie" } myObject.animal.keyValue, myObject.animal.dog

Considerazioni sul codice

In these examples we chose to use a property named keyValue for the text content. The lack of standars for XML to JSON conversion leads developers to choose several property names for the text content of XML Element nodes which contain also other child nodes. Sometimes it is used a property called $. Other times it is used a property called #text. In the algorithms proposed here you can easily change this name, depending on your needs.

The choice of using a true value instead of a null value to represent empty nodes is due to the fact that when in an XML document there is an empty node the reason is often to express a Boolean content, as in this case:

<car>
  <type>Ferrari</type>
  <bought />
</car>

If the value were null it would be more cumbersome to launch a code like this:

if (myObject.car.Ferrari.bought) {
  // do something
}
According to our third algorithm and our fourth algorithm, just Text nodes or CDATASection nodes which contain nothing but white spaces (precisely: /^\s+$/) are parsed as null.

An important consideration is that, using the third or the fourth algorithm, an XML Document can be used to create any type of Javascript object. For example, If you want to create an object like the following:

{
  "bool": true,
  "array": ["Cinema", "Hot dogs", false],
  "object": {
    "nickname": "Jack",
    "registration_date": new Date(1995, 11, 25),
    "privileged_user": true
  },
  "num": 99,
  "text": "Hello World!"
}

you must just create an XML document with the following structure:

<bool>true</bool>
<array>Cinema</array>
<array>Hot dogs</array>
<array>false</array>
<object>
  <nickname>Jack</nickname>
  <registration_date>Dec 25, 1995</registration_date>
  <privileged_user />
</object>
<num>99</num>
<text>Hello World!</text>

This example also shows how the ideal JXON document is an XML document designed specifically to be converted in JSON format.

Costruire file a partire da alberi DOM

First, create a DOM tree as described in the Come creare un albero DOM article. If you have already have a DOM tree from using XMLHttpRequest, skip to the end of this section.

Now, let's serialize doc — the DOM tree — to a file (you can read more about using files in Mozilla):

var oFOStream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
var oFile = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties).get("ProfD", Components.interfaces.nsILocalFile); // get profile folder
oFile.append("extensions"); // extensions sub-directory
oFile.append("{5872365E-67D1-4AFD-9480-FD293BEBD20D}"); // GUID of your extension
oFile.append("myXMLFile.xml"); // filename
oFOStream.init(oFile, 0x02 | 0x08 | 0x20, 0664, 0); // write, create, truncate
(new XMLSerializer()).serializeToStream(doc, oFOStream, ""); // rememeber, doc is the DOM tree
oFOStream.close();

Costruire file a partire da oggetti XMLHttpRequest

If you already have a DOM tree from using XMLHttpRequest, use the same code as above but replace serializer.serializeToStream(doc, foStream, "") with serializer.serializeToStream(xmlHttpRequest.responseXML.documentElement, foStream, "") where xmlHttpRequest is an instance of XMLHttpRequest.

Note that this first parses the XML retrieved from the server, then re-serializes it into a stream. Depending on your needs, you could just save the xmlHttpRequest.responseText directly.

Resources

{{ languages( { "ja": "ja/Parsing_and_serializing_XML", "en": "en/Parsing_and_serializing_XML" } ) }}

Sorgente della versione

<p>{{ T() }}</p>
<p>Mozilla gestisce ampiamente <a href="/it/XML" title="it/XML">XML</a>. Sono gestite diverse Raccomandazioni e bozze del World Wide Web Consortium (<a class="external" href="http://w3c.org/">W3C</a>) per la famiglia XML, così come altre tecnologie relative.</p>
<p>Tra le altre tecnologie, Mozilla offre queste tecnologie native per lavorare con documenti XML:</p>
<ul> <li><a href="/it/XPath" title="it/XPath">XPath</a> per <strong>indirizzare parti diverse di un documento XM</strong>L</li> <li><a href="/it/XMLSerializer" title="it/XMLSerializer">XMLSerializer</a> per convertire <strong>alberi DOM in stringhe o files</strong></li> <li><a href="/it/DOM/DOMParser" title="it/DOMParser">DOMParser</a> costruire un documento XML <strong>convertendo delle stringhe in alberi DOM</strong></li> <li><a href="/it/XMLHttpRequest" title="it/XMLHttpRequest">XMLHttpRequest</a> to parse XML from <strong>files into DOM trees</strong>. Although <code>DOMParser</code> does have a method named <code>parseFromStream()</code>, it's actually easier to use <a href="/it/XMLHttpRequest" title="it/XMLHttpRequest">XMLHttpRequest</a> which works for remote (not limited to HTTP) <strong>and</strong> local files.</li>
</ul>
<p>È possibile comunque creare manualmente propri algoritmi per la conversione.</p>
<h2>Costruire un albero DOM</h2>
<p>Di seguito verranno elencate varie strade per costruire un albero DOM a partire da una stringa di codice sorgente, a partire da file o a partire da strutture di differente natura</p>
<h3>Creare dinamicamente un albero DOM di un documento XML</h3>
<p>Questo paragrafo illustra come utilizzare l'API <a class="external" href="http://www.w3.org/TR/DOM-Level-3-Core/core.html">DOM Core</a> con JavaScript per creare e modificare oggetti DOM. It applies to all Gecko-based applications (such as Firefox) both in privileged (extensions) and unprivileged (web pages) code.</p>
<h4 name="Dynamically_creating_a_DOM_tree">Creare un albero DOM manualmente</h4>
<p>Si consideri il seguente documento XML:</p>
<pre class="brush: xml">&lt;?xml version="1.0"?&gt;
&lt;people&gt;
  &lt;person first-name="eric" middle-initial="H" last-name="jung"&gt;
    &lt;address street="321 south st" city="denver" state="co" country="usa"/&gt;
    &lt;address street="123 main st" city="arlington" state="ma" country="usa"/&gt;
  &lt;/person&gt;

  &lt;person first-name="jed" last-name="brown"&gt;
    &lt;address street="321 north st" city="atlanta" state="ga" country="usa"/&gt;
    &lt;address street="123 west st" city="seattle" state="wa" country="usa"/&gt;
    &lt;address street="321 south avenue" city="denver" state="co" country="usa"/&gt;
  &lt;/person&gt;
&lt;/people&gt;
</pre>
<p>L'API W3C DOM, supportata da Mozilla, can be used to create an in-memory representation of this document like so:</p>
<pre class="brush: js">var doc = document.implementation.createDocument("", "", null);
var peopleElem = doc.createElement("people");

var personElem1 = doc.createElement("person");
personElem1.setAttribute("first-name", "eric");
personElem1.setAttribute("middle-initial", "h");
personElem1.setAttribute("last-name", "jung");

var addressElem1 = doc.createElement("address");
addressElem1.setAttribute("street", "321 south st");
addressElem1.setAttribute("city", "denver");
addressElem1.setAttribute("state", "co");
addressElem1.setAttribute("country", "usa");
personElem1.appendChild(addressElem1);

var addressElem2 = doc.createElement("address");
addressElem2.setAttribute("street", "123 main st");
addressElem2.setAttribute("city", "arlington");
addressElem2.setAttribute("state", "ma");
addressElem2.setAttribute("country", "usa");
personElem1.appendChild(addressElem2);

var personElem2 = doc.createElement("person");
personElem2.setAttribute("first-name", "jed");
personElem2.setAttribute("last-name", "brown");

var addressElem3 = doc.createElement("address");
addressElem3.setAttribute("street", "321 north st");
addressElem3.setAttribute("city", "atlanta");
addressElem3.setAttribute("state", "ga");
addressElem3.setAttribute("country", "usa");
personElem2.appendChild(addressElem3);

var addressElem4 = doc.createElement("address");
addressElem4.setAttribute("street", "123 west st");
addressElem4.setAttribute("city", "seattle");
addressElem4.setAttribute("state", "wa");
addressElem4.setAttribute("country", "usa");
personElem2.appendChild(addressElem4);

var addressElem5 = doc.createElement("address");
addressElem5.setAttribute("street", "321 south avenue");
addressElem5.setAttribute("city", "denver");
addressElem5.setAttribute("state", "co");
addressElem5.setAttribute("country", "usa");
personElem2.appendChild(addressElem5);

peopleElem.appendChild(personElem1);
peopleElem.appendChild(personElem2);
doc.appendChild(peopleElem);
</pre>
<p>Si veda anche <a href="/en/XUL_Tutorial/Document_Object_Model" title="en/XUL_Tutorial/Document_Object_Model">Il capitolo sul DOM del Tutorial XUL</a> (in inglese).</p>
<h4 name="So_what.3F">So what?</h4>
<p>DOM trees can be queried using <a href="/it/Usare_XPath" title="it/Usare_XPath">XPath</a> expressions, converted to strings or written to a local or remote files using <code>XMLSerializer</code> (without having to first convert to a string), POSTed to a web server (via <code><a href="/it/XMLHttpRequest" title="it/XMLHttpRequest">XMLHttpRequest</a></code>), transformed using <a href="/it/XSLT" title="it/XSLT">XSLT</a>, <a href="/it/XLink" title="it/XLink">XLink</a>'d to, etc.</p>
<p>You can use DOM trees to model data which isn't well-suited for RDF (or perhaps you just don't like RDF). Another application is that, since XUL is XML, the UI of your application can be dynamically manipulated, downloaded, uploaded, saved, loaded, converted, or transformed quite easily.</p>
<h3>Costruire un albero DOM XML a partire da stringhe di codice sorgente</h3>
<pre class="brush: js">var sMyString = "&lt;a id=\"a\"&gt;&lt;b id=\"b\"&gt;hey!&lt;\/b&gt;&lt;\/a&gt;";
var oParser = new DOMParser();
var oDOM = oParser.parseFromString(sMyString, "text\/xml");
// print the name of the root element or error message
dump(oDOM.documentElement.nodeName == "parsererror" ? "error while parsing" : oDOM.documentElement.nodeName);
</pre>
<p><a class="external" href="http://www.van-steenbeek.net/?q=explorer_domparser_parsefromstring">Tutorial su come rendere questo codice cross browser</a> (in inglese)</p>
<h3>Costruire un albero DOM a partire da un file</h3>
<h4>Usando XMLHttpRequest</h4>
<p>Come già precedentemente accennato, sebbene anche ciascuna istanza di <code>DOMParser</code> possegga un metodo chiamato <code>parseFromStream()</code>, è più facile utilizzare <a href="/it/XMLHttpRequest" title="it/XMLHttpRequest">XMLHttpRequest</a> per parsare documenti XML in alberi DOM (<code>XMLHttpRequest</code> funziona bene sia in locale che in remoto). Qui c'è un codice di esempio che legge e parsa in un albero DOM un file XML locale:</p>
<pre class="brush: js">var oXHR = new XMLHttpRequest();
oXHR.open("GET", "chrome://passwdmaker/content/people.xml", false);
oXHR.send(null);
// print the name of the root element or error message
var oDOM = oXHR.responseXML;
dump(oDOM.documentElement.nodeName == "parsererror" ? "error while parsing" : oDOM.documentElement.nodeName);
</pre>
<p><code>req.responseXML</code> is a <code><a class="external" href="http://xulplanet.com/references/objref/Document.html">Document</a></code> instance.</p>
<h4>Usando l'elemento {{ HTMLElement("object") }}.</h4>
<p>Here is another possible way, using the {{ HTMLElement("object") }} tag. For this demo, you must create an external document called <code>purchase_order.xml</code>:</p>
<pre class="brush: html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;XML Data Block Demo&lt;/title&gt;
&lt;script&gt;
function runDemo() {
  var doc = document.getElementById("purchase-order").contentDocument;
  var lineItems = doc.getElementsByTagNameNS("http://example.mozilla.org/PurchaseOrderML", "lineItem");
  var firstPrice = lineItems[0].getElementsByTagNameNS("http://example.mozilla.org/PurchaseOrderML", "price")[0].textContent;
  document.getElementById("output-box").textContent = "The purchase order contains " + lineItems.length + " line items. The price of the first line item is " + firstPrice + ".";
}
&lt;/script&gt;
&lt;/head&gt;
&lt;body onload="runDemo()";&gt;
&lt;object id="purchase-order" data="purchase_order.xml" type="text/xml" style="display: none;"&gt;&lt;/object&gt;
&lt;div id="output-box"&gt;Demo did not run&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Per ulteriori approfondimenti, si rimanda all'articolo: <a href="/it/Usare_le_XML_Data_Islands_in_Mozilla" title="it/Usare_le_XML_Data_Islands_in_Mozilla">Usare le XML Data Islands in Mozilla</a>.</p>
<h2>Decostruire un albero DOM</h2>
<p>Di seguito verranno elencate varie strade per leggere o convertire un albero DOM.</p>
<h3>Convertire un documento XML in stringhe di codice sorgente</h3>
<p>Per prima cosa procuriamoci un albero DOM. È possibile costruirlo manualmente (si veda: <a href="/it/Come_creare_un_albero_DOM" title="it/Come_creare_un_albero_DOM">Come creare un albero DOM</a>) o, alternativamente, ottenerlo tramite <a href="/it/XMLHttpRequest" title="it/XMLHttpRequest">XMLHttpRequest</a>.</p>
<p>Adesso proviamo a ottenere dalla variabile <code>doc</code> — il nostro albero DOM — una stringa di codice sorgente:</p>
<pre class="brush: js">var oSerializer = new XMLSerializer();
var sXML = oSerializer.serializeToString(doc);</pre>
<p>Non è possibile creare un istanza di <code>XMLSerializer</code> (ovvero lanciare: <code>new XMLSerializer()</code>) né dall'interno di un componente JS XPCOM, né dall'interno di un <a class="internal" href="/it/Moduli_di_codice_JavaScript" title="it/Moduli_di_codice_JavaScript">modulo</a>. Per farlo bisogna utilizzare:</p>
<pre class="brush: js">var oSerializer = Components.classes["@mozilla.org/xmlextras/xmlserializer;1"].createInstance(Components.interfaces.nsIDOMSerializer);
var sXML = oSerializer.serializeToString(doc);
</pre>
<h4>Come ottenere stringhe di codice sorgente di facile lettura</h4>
<p>You can <a class="external" href="http://en.wikipedia.org/wiki/Pretty-print">pretty print</a> a DOM tree using <code>XMLSerializer</code> and <a href="/it/E4X" title="it/E4X">E4X</a>. First, create a DOM tree as described in the <a href="/it/Come_creare_un_albero_DOM" title="it/Come_creare_un_albero_DOM">Come creare un albero DOM</a> article. Alternatively, use a DOM tree obtained from <a href="/it/XMLHttpRequest" title="it/XMLHttpRequest">XMLHttpRequest</a>. We assume it's in the <code>doc</code> variable.</p>
<pre class="brush: js">var oSerializer = new XMLSerializer();
var sPrettyXML = XML(oSerializer.serializeToString(doc)).toXMLString();</pre>
<p>Indents are provided with two spaces. You can, of course, use <a href="/it/DOM/treeWalker" title="it/DOM/treeWalker">DOM:treeWalker</a> to write your own, more performant version which also has the advantage that you can customize the indent string to be whatever you like.</p>
<p><strong>Note:</strong> When using the E4X <code>toXMLString</code> method your <strong>CDATA elements will be lost</strong> and only the containing text remains. So using the above method might not be useful if you have CDATA elements in your XML.</p>
<pre class="brush: xml">&lt;content&gt;&lt;![CDATA[This is the content]]&gt;&lt;/content&gt;</pre>
<p>Will become</p>
<pre class="brush: xml">&lt;content&gt;This is the content&lt;/content&gt;</pre>
<h3 id="JXON">Convertire un foglio XML in un albero di oggetti Javascript (JXON)</h3>
<p>JXON (lossless <strong>J</strong>avascript <strong>X</strong>ML <strong>O</strong>bject <strong>N</strong>otation) è un nome generico col quale viene definita la rappresentazione di oggetti Javascript in linguaggio XML. Non esistono veri e propri standard per questa rappresentazione, ma da poco tempo a questa parte cominciano ad affacciarsiin rete alcune convenzioni.</p>
<p>Immaginiamo di aver parsato, come al solito nella nostra variabile <code>doc</code>, questo documento XML di esempio:</p>
<h5 id="XML_di_esempio">esempio.xml</h5>
<pre class="brush: xml">&lt;?xml version="1.0"?&gt;
&lt;!DOCTYPE catalog SYSTEM "catalog.dtd"&gt;
&lt;catalog&gt;
   &lt;product description="Cardigan Sweater"&gt;
      &lt;catalog_item gender="Men's"&gt;
         &lt;item_number&gt;QWZ5671&lt;/item_number&gt;
         &lt;price&gt;39.95&lt;/price&gt;
         &lt;size description="Medium"&gt;
            &lt;color_swatch image="red_cardigan.jpg"&gt;Red&lt;/color_swatch&gt;
            &lt;color_swatch image="burgundy_cardigan.jpg"&gt;Burgundy&lt;/color_swatch&gt;
         &lt;/size&gt;
         &lt;size description="Large"&gt;
            &lt;color_swatch image="red_cardigan.jpg"&gt;Red&lt;/color_swatch&gt;
            &lt;color_swatch image="burgundy_cardigan.jpg"&gt;Burgundy&lt;/color_swatch&gt;
         &lt;/size&gt;
      &lt;/catalog_item&gt;
      &lt;catalog_item gender="Women's"&gt;
         &lt;item_number&gt;RRX9856&lt;/item_number&gt;
         &lt;discount_until&gt;Dec 25, 1995&lt;/discount_until&gt;
         &lt;price&gt;42.50&lt;/price&gt;
         &lt;size description="Medium"&gt;
            &lt;color_swatch image="black_cardigan.jpg"&gt;Black&lt;/color_swatch&gt;
         &lt;/size&gt;
      &lt;/catalog_item&gt;
   &lt;/product&gt;
   &lt;script type="text/javascript"&gt;&lt;![CDATA[function matchwo(a,b) {
    if (a &lt; b &amp;&amp; a &lt; 0) { return 1; }
    else { return 0; }
}]]&gt;&lt;/script&gt;
&lt;/catalog&gt;
</pre>
<p>Adesso proviamo a ottenere una rappresentazione della variabile <code>doc</code> — l'albero DOM — attraverso un albero di oggetti Javascript (you can read more about <a href="/en/JavaScript/Guide/Working_with_Objects" title="Working with Objects – MDC">working with Objects</a> and <a href="/en/Introduction_to_Object-Oriented_JavaScript" title="Introduction to Object-Oriented JavaScript – MDC">how Javascript is Object-Oriented</a>). Per far ciò possiamo utilizzare diversi algoritmi di conversione.</p>
<p>Per semplicità gli algoritmi qui proposti (si veda: <a href="#Algoritmo_JXON_1" title="Vai all'algoritmo JXON #1">#1</a>, <a href="#Algoritmo_JXON_2" title="Vai all'algoritmo JXON #2">#2</a>, <a href="#Algoritmo_JXON_3" title="Vai all'algoritmo JXON #3">#3</a>, <a href="#Algoritmo_JXON_4" title="Vai all'algoritmo JXON #4">#4</a>) prenderanno in considerazione solamente i seguenti tipi di nodi e i loro attributi:</p>
<ol> <li><code>Document</code> (solo come argomento della funzione),</li> <li><code>DocumentFragment</code> (solo come argomento della funzione),</li> <li><code>Element</code>,</li> <li><code>Text</code> (mai come argomento della funzione),</li> <li><code>CDATASection</code> (mai come argomento della funzione).</li>
</ol>
<p>Si tratta di un buon compromesso per un uso Javascript, dacché la gran parte delle informazioni di un documento XML è contenuta in questo tipo di nodi. Ogni altra informazione (come processing instructions, xml schemas, commenti, etc.) andrà persa. Allo scopo di evitare conflitti, la lettura dei nomi dei nodi e dei loro attributi è <em>case insensitive</em> (resa sempre in <em>minuscolo</em>) e di conseguenza le proprietà locali dell'albero di oggetti così ottenuto aggiunte via JavaScript dovranno avere un qualche tipo di lettera maiuscola al loro interno, per evitare di sovrascrivere le proprietà ottenute dal foglio XML, come si può vedere di seguito. I seguenti algoritmi sono liberamente basati sulla <a href="#Convenzione_di_Parker" title="La Convenzione di Parker">Convenzione di Parker, versione 0.4</a>, che prevede il riconoscimento del <code>typeof</code> del contenuto di testo di ogni singolo nodo letto.</p>
<h4 id="Algoritmo_JXON_1">Algoritmo #1: una via prolissa</h4>
<p>Questo semplice costruttore ricorsivo converte un albero DOM XML in un albero di oggetti Javascript. Il contenuto di testo di ogni nodo è salvato all'interno della proprietà <code>keyValue</code>, mentre i <code>nodeAttributes</code>, se esistono, sono annidati sotto l'oggetto-figlio <code>keyAttributes</code>. L'argomento del costruttore può essere l'intero <code>Document</code>, un <code>DocumentFragment</code> o, più semplicemente, un nodo di tipo <code>Element</code> di esso.</p>
<pre class="brush: js">function buildValue(sValue) {
  if (/^\s*$/.test(sValue)) { return null; }
  if (/^(true|false)$/i.test(sValue)) { return sValue.toLowerCase() === "true"; }
  if (isFinite(sValue)) { return parseFloat(sValue); }
  if (isFinite(Date.parse(sValue))) { return new Date(sValue); }
  return sValue;
}

function XMLData (oXMLParent) {
  var nAttrLen = 0, nLength = 0, sCollectedTxt = "";
  // children
  if (oXMLParent.hasChildNodes()) {
    for (var oItChild, sItKey, sItVal, nChildId = 0; nChildId &lt; oXMLParent.childNodes.length; nChildId++) {
      oItChild = oXMLParent.childNodes.item(nChildId);
      if ((oItChild.nodeType - 1 | 1) === 3) { sCollectedTxt += oItChild.nodeType === 3 ? oItChild.nodeValue.replace(/^\s+|\s+$/g, "") : oItChild.nodeValue; } // nodeType is "Text" (3) or "CDATASection" (4)
      else if (oItChild.nodeType === 1 &amp;&amp; !oItChild.prefix) { // nodeType is "Element" (1)
        sItKey = oItChild.nodeName.toLowerCase();
        sItVal = new XMLData(oItChild);
        if (this.hasOwnProperty(sItKey)) {
          if (this[sItKey].constructor !== Array) { this[sItKey] = [this[sItKey]]; }
          this[sItKey].push(sItVal);
        } else { this[sItKey] = sItVal; nLength++; }
      }
    }
    this.keyValue = buildValue(sCollectedTxt);
  } else { this.keyValue = null; }
  // node attributes
  if (oXMLParent.hasAttributes()) {
    var oItAttr;
    this.keyAttributes = {};
    for (nAttrLen; nAttrLen &lt; oXMLParent.attributes.length; nAttrLen++) {
      oItAttr = oXMLParent.attributes.item(nAttrLen);
      this.keyAttributes[oItAttr.nodeName.toLowerCase()] = buildValue(oItAttr.nodeValue);
    }
  }
  // optional properties and methods; you could safely adjoust/remove them...
  this.keyLength = nLength;
  this.attributesLength = nAttrLen;
  // this.DOMNode = oXMLParent;
  this.valueOf = function() { return this.keyValue; };
  this.toString = function() { return String(this.keyValue); };
  this.getItem = function(nItem) {
    if (nLength === 0) { return null; }
    var iItem = 0;
    for (var sKeyName in this) { if (iItem === nItem) { return this[sKeyName]; } iItem++; }
    return null;
  };
  this.getAttribute = function(nAttrib) {
    if (nAttrLen === 0 || nAttrib + 1 &gt; nAttrLen) { return null; }
    var nItAttr = 0;
    for (var sAttrName in this.keyAttributes) { if (nItAttr === nAttrib) { return this.keyAttributes[sAttrName]; } nItAttr++; }
    return null;
  };
  this.hasChildren = function() { return this.keyLength &gt; 0; };
}

var myObject = new XMLData(doc);
// abbiamo ottenuto il nostro albero di oggetti Javascript! provare per credere: alert(JSON.stringify(myObject));
</pre>
<p>Con questo algoritmo <a href="#XML_di_esempio" title="Go to the sample XML document">il nostro esempio</a> diventerà:</p>
<pre class="brush: js">{
  "catalog": {
    "product": {
      "catalog_item": [{
        "item_number": {
          "keyValue": "QWZ5671",
          "keyLength": 0,
          "attributesLength": 0
        },
        "price": {
          "keyValue": 39.95,
          "keyLength": 0,
          "attributesLength": 0
        },
        "size": [{
          "color_swatch": [{
            "keyValue": "Red",
            "keyAttributes": {
              "image": "red_cardigan.jpg"
            },
            "keyLength": 0,
            "attributesLength": 1
          }, {
            "keyValue": "Burgundy",
            "keyAttributes": {
              "image": "burgundy_cardigan.jpg"
            },
            "keyLength": 0,
            "attributesLength": 1
          }],
          "keyValue": null,
          "keyAttributes": {
            "description": "Medium"
          },
          "keyLength": 1,
          "attributesLength": 1
        }, {
          "color_swatch": [{
            "keyValue": "Red",
            "keyAttributes": {
              "image": "red_cardigan.jpg"
            },
            "keyLength": 0,
            "attributesLength": 1
          }, {
            "keyValue": "Burgundy",
            "keyAttributes": {
              "image": "burgundy_cardigan.jpg"
            },
            "keyLength": 0,
            "attributesLength": 1
          }],
          "keyValue": null,
          "keyAttributes": {
            "description": "Large"
          },
          "keyLength": 1,
          "attributesLength": 1
        }],
        "keyValue": null,
        "keyAttributes": {
          "gender": "Men's"
        },
        "keyLength": 3,
        "attributesLength": 1
      }, {
        "item_number": {
          "keyValue": "RRX9856",
          "keyLength": 0,
          "attributesLength": 0
        },
        "discount_until": {
          "keyValue": new Date(1995, 11, 25),
          "keyLength": 0,
          "attributesLength": 0
        },
        "price": {
          "keyValue": 42.5,
          "keyLength": 0,
          "attributesLength": 0
        },
        "size": {
          "color_swatch": {
            "keyValue": "Black",
            "keyAttributes": {
              "image": "black_cardigan.jpg"
            },
            "keyLength": 0,
            "attributesLength": 1
          },
          "keyValue": null,
          "keyAttributes": {
            "description": "Medium"
          },
          "keyLength": 1,
          "attributesLength": 1
        },
        "keyValue": null,
        "keyAttributes": {
          "gender": "Women's"
        },
        "keyLength": 4,
        "attributesLength": 1
      }],
      "keyValue": null,
      "keyAttributes": {
        "description": "Cardigan Sweater"
      },
      "keyLength": 1,
      "attributesLength": 1
    },
    "script": {
      "keyValue": "function matchwo(a,b) {\n  if (a &lt; b &amp;&amp; a &lt; 0) { return 1; }\n  else { return 0; }\n}",
      "keyAttributes": {
        "type": "text/javascript"
      },
      "keyLength": 0,
      "attributesLength": 1
    },
    "keyValue": null,
    "keyLength": 2,
    "attributesLength": 0
  },
  "keyValue": null,
  "keyLength": 1,
  "attributesLength": 0
}
</pre>
<p>È un approccio raccomandato nel caso in cui ci sia completamente ignota la struttura del documento XML che andremo a leggere.</p>
<h4 id="Algoritmo_JXON_2">Algoritmo #2: una via un po' meno prolissa</h4>
<p>Quello che segue è un altro, più semplice, metodo di conversione. Dove i <code>nodeAttributes</code> sono annidati nello stesso oggetto dei nodi figli ma, a differenza di questi, contrassegnati dal prefisso “@”. As above, the text content is stored into the <code>keyValue</code> property. The constructor's argument can be the entire XML <code>Document</code>, a <code>DocumentFragment</code> or simply an <code>Element</code> node of it.</p>
<pre class="brush: js">function buildValue(sValue) {
  if (/^\s*$/.test(sValue)) { return null; }
  if (/^(true|false)$/i.test(sValue)) { return sValue.toLowerCase() === "true"; }
  if (isFinite(sValue)) { return parseFloat(sValue); }
  if (isFinite(Date.parse(sValue))) { return new Date(sValue); }
  return sValue;
}

function XMLData (oXMLParent) {
  if (oXMLParent.hasChildNodes()) {
    var sCollectedTxt = "";
    for (var oItChild, sItKey, sItVal, nChildId = 0; nChildId &lt; oXMLParent.childNodes.length; nChildId++) {
      oItChild = oXMLParent.childNodes.item(nChildId);
      if ((oItChild.nodeType - 1 | 1) === 3) { sCollectedTxt += oItChild.nodeType === 3 ? oItChild.nodeValue.replace(/^\s+|\s+$/g, "") : oItChild.nodeValue; }
      else if (oItChild.nodeType === 1 &amp;&amp; !oItChild.prefix) {
        sItKey = oItChild.nodeName.toLowerCase();
        sItVal = new XMLData(oItChild);
        if (this.hasOwnProperty(sItKey)) {
          if (this[sItKey].constructor !== Array) { this[sItKey] = [this[sItKey]]; }
          this[sItKey].push(sItVal);
        } else { this[sItKey] = sItVal; }
      }
    }
    if (sCollectedTxt) { this.keyValue = buildValue(sCollectedTxt); }
  }
  if (oXMLParent.hasAttributes()) {
    var oItAttr;
    for (var iAttrId = 0; iAttrId &lt; oXMLParent.attributes.length; iAttrId++) {
      oItAttr = oXMLParent.attributes.item(iAttrId);
      this["@" + oItAttr.nodeName.toLowerCase()] = buildValue(oItAttr.nodeValue);
    }
  }
}

var myObject = new XMLData(doc);
// abbiamo ottenuto il nostro albero di oggetti Javascript! provare per credere: alert(JSON.stringify(myObject));
</pre>
<p>Con questo algoritmo <a href="#XML_di_esempio" title="Go to the sample XML document">il nostro esempio</a> diventerà:</p>
<pre class="brush: js">{
  "catalog": {
    "product": {
      "catalog_item": [{
        "item_number": {
          "keyValue": "QWZ5671"
        },
        "price": {
          "keyValue": 39.95
        },
        "size": [{
          "color_swatch": [{
            "keyValue": "Red",
            "@image": "red_cardigan.jpg"
          }, {
            "keyValue": "Burgundy",
            "@image": "burgundy_cardigan.jpg"
          }],
          "@description": "Medium"
        }, {
          "color_swatch": [{
            "keyValue": "Red",
            "@image": "red_cardigan.jpg"
          }, {
            "keyValue": "Burgundy",
            "@image": "burgundy_cardigan.jpg"
          }],
          "@description": "Large"
        }],
        "@gender": "Men's"
      }, {
        "item_number": {
          "keyValue": "RRX9856"
        },
        "discount_until": {
          "keyValue": new Date(1995, 11, 25)
        },
        "price": {
          "keyValue": 42.5
        },
        "size": {
          "color_swatch": {
            "keyValue": "Black",
            "@image": "black_cardigan.jpg"
          },
          "@description": "Medium"
        },
        "@gender": "Women's"
      }],
      "@description": "Cardigan Sweater"
    },
    "script": {
      "keyValue": "function matchwo(a,b) {\n  if (a &lt; b &amp;&amp; a &lt; 0) { return 1; }\n  else { return 0; }\n}",
      "@type": "text/javascript"
    }
  }
}
</pre>
<p>It is a possible way if you partially know the structure of the XML document.</p>
<h4 id="Algoritmo_JXON_3">Algoritmo #3: una via sintetica</h4>
<p>Here is another method of conversion. This algorithm is the closest to the <a href="#Convenzione_di_Parker" title="La Convenzione di Parker">Convenzione di Parker</a>. It is very similar to the previous one, except that nodes which do not contain other recognizable nodes than <code>Text</code> or <code>CDATASection</code> are not treated as objects, but directly as booleans, strings, numbers or <code>Date</code> objects (see the <a href="#Convenzione_di_Parker" title="La Convenzione di Parker">Convenzione di Parker</a>). Empty nodes (i.e. which do not contain other <code>Element</code> nodes, <code>Text</code> nodes or <code>CDATASection</code> nodes) have the default value <code>true</code>. Also, this time it is not used a constructor, but a simple function. The function's argument can be the entire XML <code>Document</code>, a <code>DocumentFragment</code> or simply an <code>Element</code> node of it.</p>
<p>In many cases this one represents the most practical conversion method.</p>
<pre class="brush: js">function buildValue(sValue) {
  if (/^\s*$/.test(sValue)) { return null; }
  if (/^(true|false)$/i.test(sValue)) { return sValue.toLowerCase() === "true"; }
  if (isFinite(sValue)) { return parseFloat(sValue); }
  if (isFinite(Date.parse(sValue))) { return new Date(sValue); }
  return sValue;
}

function getXMLData (oXMLParent) {
  var vResult = /* put here the default value for empty nodes! */ true, nLength = 0, sCollectedTxt = "";
  if (oXMLParent.hasAttributes()) {
    vResult = {};
    for (nLength; nLength &lt; oXMLParent.attributes.length; nLength++) {
      oItAttr = oXMLParent.attributes.item(nLength);
      vResult["@" + oItAttr.nodeName.toLowerCase()] = buildValue(oItAttr.nodeValue.replace(/^\s+|\s+$/g, ""));
    }
  }
  if (oXMLParent.hasChildNodes()) {
    for (var oItChild, sItKey, sItVal, nChildId = 0; nChildId &lt; oXMLParent.childNodes.length; nChildId++) {
      oItChild = oXMLParent.childNodes.item(nChildId);
      if (oItChild.nodeType === 4) { sCollectedTxt += oItChild.nodeValue; } /* nodeType is "CDATASection" (4) */
      else if (oItChild.nodeType === 3) { sCollectedTxt += oItChild.nodeValue.replace(/^\s+|\s+$/g, ""); } /* nodeType is "Text" (3) */
      else if (oItChild.nodeType === 1 &amp;&amp; !oItChild.prefix) { /* nodeType is "Element" (1) */
         if (nLength === 0) { vResult = {}; }
        sItKey = oItChild.nodeName.toLowerCase();
        sItVal = getXMLData(oItChild);
        if (vResult.hasOwnProperty(sItKey)) {
          if (vResult[sItKey].constructor !== Array) { vResult[sItKey] = [vResult[sItKey]]; }
          vResult[sItKey].push(sItVal);
        } else { vResult[sItKey] = sItVal; nLength++; }
      }
     }
  }
  if (sCollectedTxt) { nLength &gt; 0 ? vResult.keyValue = buildValue(sCollectedTxt) : vResult = buildValue(sCollectedTxt); }
  /* if (nLength &gt; 0) { Object.freeze(vResult); } */
  return vResult;
}

var myObject = getXMLData(doc);
// abbiamo ottenuto il nostro albero di oggetti Javascript! provare per credere: alert(JSON.stringify(myObject));
</pre>
<div class="note"><strong>Note:</strong> If you want to freeze the whole object tree (because of the "static" nature of a XML document), uncomment the string: <code>/* if (nLength &gt; 0) { Object.freeze(vResult); } */</code>. The <code><a href="/it/Javascript/Glossario/Oggetti_globali/Object/freeze" title="/it/Javascript/Glossario/Oggetti_globali/Object/freeze">Object.freeze</a></code> method prevents new properties from being added to it, prevents existing properties from being removed and prevents existing properties, or their enumerability, configurability, or writability, from being changed. In essence the object tree is made effectively immutable.</div>
<p>Con questo algoritmo <a href="#XML_di_esempio" title="Go to the sample XML document">il nostro esempio</a> diventerà:</p>
<pre class="brush: js">{
  "catalog": {
    "product": {
      "@description": "Cardigan Sweater",
      "catalog_item": [{
        "@gender": "Men's",
        "item_number": "QWZ5671",
        "price": 39.95,
        "size": [{
          "@description": "Medium",
          "color_swatch": [{
            "@image": "red_cardigan.jpg",
            "keyValue": "Red"
          }, {
            "@image": "burgundy_cardigan.jpg",
            "keyValue": "Burgundy"
          }]
        }, {
          "@description": "Large",
          "color_swatch": [{
            "@image": "red_cardigan.jpg",
            "keyValue": "Red"
          }, {
            "@image": "burgundy_cardigan.jpg",
            "keyValue": "Burgundy"
          }]
        }]
      }, {
        "@gender": "Women's",
        "item_number": "RRX9856",
        "discount_until": new Date(1995, 11, 25),
        "price": 42.5,
        "size": {
          "@description": "Medium",
          "color_swatch": {
            "@image": "black_cardigan.jpg",
            "keyValue": "Black"
          }
        }
      }]
    },
    "script": {
      "@type": "text/javascript",
      "keyValue": "function matchwo(a,b) {\n  if (a &lt; b &amp;&amp; a &lt; 0) { return 1; }\n  else { return 0; }\n}"
    }
  }
}
</pre>
<p>It is a recommanded way if you know the structure of the XML document.</p>
<h4 id="Algoritmo_JXON_4">Algoritmo #4: una via davvero minimalista</h4>
<p>The following is another possible way of conversion. It is very close to the <a href="#Convenzione_di_Parker" title="La Convenzione di Parker">Convenzione di Parker</a>, too. With this algorithm all <code>Element</code> nodes which contain other child <code>Element</code> nodes and <code>Text</code> or <code>CDATASection</code> nodes in the same level are treated as instances of <code>Boolean</code>, <code>Number</code>, <code>String</code>, or <code>Date</code> Constructors. So any child <code>Element</code> node, if exists, will be nested in these types of objects.</p>
<p>For example:</p>
<pre class="brush: xml">&lt;employee type="usher"&gt;John Smith&lt;/employee&gt;
&lt;manager&gt;Lisa Carlucci&lt;/manager&gt;
</pre>
<p>will become</p>
<pre class="brush: js">var myObject = {
  "employee": new String("John Smith"),
  "manager": "Lisa Carlucci"
};

myObject.employee["@type"] = "usher";

// test

alert(myObject.manager); // "Lisa Carlucci"
alert(myObject.employee["@type"]); // "usher"
alert(myObject.employee); // "John Smith"
</pre>
<p>As for the third algorithm, nodes which do not contain other recognizable nodes than <code>Text</code> or <code>CDATASection</code> are not treated as objects, but directly as booleans, strings, numbers (primitive values) or <code>Date</code> objects; and empty nodes (i.e. which do not contain other <code>Element</code> nodes, <code>Text</code> nodes or <code>CDATASection</code> nodes) have the default value <code>true</code>. As for the third algorithm it is not used a constructor, but a simple function. The function's argument can be the entire XML <code>Document</code>, a <code>DocumentFragment</code> or simply an <code>Element</code> node of it.</p>
<pre class="brush: js">function buildValue (sValue) {
  if (/^\s*$/.test(sValue)) { return null; }
  if (/^(true|false)$/i.test(sValue)) { return sValue.toLowerCase() === "true"; }
  if (isFinite(sValue)) { return parseFloat(sValue); }
  if (isFinite(Date.parse(sValue))) { return new Date(sValue); }
  return sValue;
}

function objectify (vValue) {
  if (vValue === null) {
    return new (function() {
      this.toString = function() { return "null"; }
      this.valueOf = function() { return null; }
    })();
  }
  return vValue instanceof Object ? vValue : new vValue.constructor(vValue);
}

var aTmpEls = []; // loaded element nodes cache

function getXMLData (oXMLParent) {
  var  sItKey, sItVal, vResult, nLength = 0, nLevelStart = aTmpEls.length,
       nChildren = oXMLParent.hasChildNodes() ? oXMLParent.childNodes.length : 0, sCollectedTxt = "";

  for (var oItChild, nChildId = 0; nChildId &lt; nChildren; nChildId++) {
    oItChild = oXMLParent.childNodes.item(nChildId);
    if (oItChild.nodeType === 4) { sCollectedTxt += oItChild.nodeValue; } /* nodeType is "CDATASection" (4) */
    else if (oItChild.nodeType === 3) { sCollectedTxt += oItChild.nodeValue.replace(/^\s+|\s+$/g, ""); } /* nodeType is "Text" (3) */
    else if (oItChild.nodeType === 1 &amp;&amp; !oItChild.prefix) { aTmpEls.push(oItChild); } /* nodeType is "Element" (1) */
  }

  var nLevelEnd = aTmpEls.length, vBuiltVal = buildValue(sCollectedTxt);

  if (oXMLParent.hasAttributes()) {
    vResult = objectify(vBuiltVal);
    for (nLength; nLength &lt; oXMLParent.attributes.length; nLength++) {
      oItAttr = oXMLParent.attributes.item(nLength);
      vResult["@" + oItAttr.nodeName.toLowerCase()] = buildValue(oItAttr.nodeValue.replace(/^\s+|\s+$/g, ""));
    }
  } else if (nLevelEnd &gt; nLevelStart) { vResult = objectify(vBuiltVal); }

  for (var nElId = nLevelStart; nElId &lt; nLevelEnd; nElId++) {
    sItKey = aTmpEls[nElId].nodeName.toLowerCase();
    sItVal = getXMLData(aTmpEls[nElId]);
    if (vResult.hasOwnProperty(sItKey)) {
    if (vResult[sItKey].constructor !== Array) { vResult[sItKey] = [vResult[sItKey]]; }
      vResult[sItKey].push(sItVal);
    } else { vResult[sItKey] = sItVal; nLength++; }
  }

  aTmpEls.length = nLevelStart;

  if (nLength === 0) { vResult = sCollectedTxt ? vBuiltVal : /* put here the default value for empty nodes: */ true; }
  /* else { Object.freeze(vResult); } */

  return vResult;
}

var myObject = getXMLData(doc);
alert(myObject.catalog.product.catalog_item[1].size.color_swatch["@image"]); // "black_cardigan.jpg"
alert(myObject.catalog.product.catalog_item[1].size.color_swatch); // "Black" !</pre>
<div class="note"><strong>Note:</strong> If you want to freeze the whole object tree (because of the "static" nature of a XML document), uncomment the string: <code>/* else { Object.freeze(vResult); } */</code>. The <code><a href="/it/Javascript/Glossario/Oggetti_globali/Object/freeze" title="/it/Javascript/Glossario/Oggetti_globali/Object/freeze">Object.freeze</a></code> method prevents new properties from being added to it, prevents existing properties from being removed and prevents existing properties, or their enumerability, configurability, or writability, from being changed. In essence the object tree is made effectively immutable.</div>
<p>It is a possible way if you know the structure of the XML document.</p>
<h4 id="Convenzione_di_Parker">La Convenzione di Parker</h4>
<p>The functions listed above for the conversion of an XML document to JSON (often called «JXON algorithms») are more or less freely based on the Parker Convention. It is called “Parker Convention” in opposition to “BadgerFish Convention”, after the comic Parker &amp; Badger by Cuadrado. See also: <a class="external" href="http://badgerfish.ning.com/" title="BadgerFish convention">BadgerFish Convention</a>.</p>
<p>The following is a transcription of the Parker Convention paper (version 0.4), from the page “<a class="external" href="http://code.google.com/p/xml2json-xslt/wiki/TransformingRules" title="TransformingRules – xml2json-xslt">TransformingRules</a>” of the <a class="external" href="http://code.google.com/p/xml2json-xslt/" title="xml2json-xslt project">xml2json-xslt project</a> site.</p>
<p>This Convention was written in order to regulate the conversion to <a href="/it/JSON" title="/it/JSON">JSON</a> from <a href="/it/XSLT" title="/it/XSLT">XSLT</a>, so parts of it are futile for Javascript.</p>
<h5>Conversione in JSON</h5>
<ol> <li> <p>The root element will be absorbed, for there is only one:</p> <pre class="brush: xml">&lt;root&gt;test&lt;/root&gt;</pre> <p>becomes</p> <pre class="brush: js">"test"
</pre> </li> <li> <p>Element names become object properties:</p> <pre class="brush: xml">&lt;root&gt;&lt;name&gt;Xml&lt;/name&gt;&lt;encoding&gt;ASCII&lt;/encoding&gt;&lt;/root&gt;</pre> <p>becomes</p> <pre class="brush: js">{
  "name": "Xml",
  "encoding": "ASCII"
}
</pre> </li> <li> <p>Numbers are recognized (integers and decimals):</p> <pre class="brush: xml">&lt;root&gt;&lt;age&gt;12&lt;/age&gt;&lt;height&gt;1.73&lt;/height&gt;&lt;/root&gt;</pre> <p>becomes</p> <pre class="brush: js">{
  "age": 12,
  "height": 1.73
}
</pre> </li> <li> <p>Booleans are recognized case insensitive:</p> <pre class="brush: xml">&lt;root&gt;&lt;checked&gt;True&lt;/checked&gt;&lt;answer&gt;FALSE&lt;/answer&gt;&lt;/root&gt;</pre> <p>becomes</p> <pre class="brush: js">{
  "checked": true,
  "answer": false
}
</pre> </li> <li> <p>Strings are escaped:</p> <pre class="brush: xml">&lt;root&gt;Quote: &amp;quot; New-line:
&lt;/root&gt;
</pre> <p>becomes</p> <pre class="brush: js">"Quote: \" New-line:\n"</pre> </li> <li> <p>Empty elements will become null:</p> <pre class="brush: xml">&lt;root&gt;&lt;nil/&gt;&lt;empty&gt;&lt;/empty&gt;&lt;/root&gt;</pre> <p>becomes</p> <pre class="brush: js">{
  "nil": null,
  "empty": null
}
</pre> </li> <li> <p>If all sibling elements have the same name, they become an array</p> <pre class="brush: xml">&lt;root&gt;&lt;item&gt;1&lt;/item&gt;&lt;item&gt;2&lt;/item&gt;&lt;item&gt;three&lt;/item&gt;&lt;/root&gt;
</pre> <p>becomes</p> <pre class="brush: js">[1, 2, "three"]
</pre> </li> <li> <p>Mixed mode text-nodes, comments and attributes get absorbed:</p> <pre class="brush: xml">&lt;root version="1.0"&gt;testing&lt;!--comment--&gt;&lt;elementtest="true"&gt;1&lt;/element&gt;&lt;/root&gt;
</pre> <p>becomes</p> <pre class="brush: js">{ "element": true }
</pre> </li> <li> <p>Namespaces get absorbed, and prefixes will just be part of the property name:</p> <pre class="brush: xml">&lt;root xmlns:ding="http://zanstra.com/ding"&gt;&lt;ding:dong&gt;binnen&lt;/ding:dong&gt;&lt;/root&gt;
</pre> <p>becomes</p> <pre class="brush: js">{ "ding:dong" : "binnen" }
</pre> </li>
</ol>
<div class="note"><strong>Note:</strong> Our algorithms comply with the points 2, 3, 4 and 7. The third and the fourth algorithm comply also with the point 6 (but <code>true</code> instead of <code>null</code>). The point 5 is automatically managed by the Javascript method <code><a href="/it/Javascript/Glossario/Oggetti_globali/JSON/stringify" title="/it/Javascript/Glossario/Oggetti_globali/JSON/stringify">JSON.stringify</a></code>.</div>
<h5>Appendice Javascript</h5>
<p>All the same as the JSON translation, but with these extra's:</p>
<ol> <li> <p>Property names are only escaped when necessary</p> <pre class="brush: xml">&lt;root&gt;&lt;while&gt;true&lt;/while&gt;&lt;wend&gt;false&lt;/wend&gt;&lt;only-if/&gt;&lt;/root&gt;</pre> <p>becomes</p> <pre class="brush: js">{
  "while": true,
  wend: false,
  "only-if": null
}
</pre> </li> <li> <p>Within a string, closing elements "&lt;/" are escaped as "&lt;\/"</p> <pre class="brush: xml">&lt;root&gt;&lt;![CDATA[&lt;script&gt;alert("YES");&lt;/script&gt;]]&gt;&lt;/root&gt;</pre> <p>becomes</p> <pre class="brush: js">{ script: "&lt;script&gt;alert(\"YES\")&lt;\/script&gt;" }
</pre> </li> <li> <p>Dates are created as <code>new Date()</code> objects</p> <pre class="brush: xml">&lt;root&gt;2006-12-25&lt;/root&gt;</pre> <p>becomes</p> <pre class="brush: js">new Date(2006, 12 - 1, 25)
</pre> </li> <li> <p>Attributes and comments are shown as comments (for testing-purposes):</p> <pre class="brush: xml">&lt;!--testing--&gt;&lt;root&gt;&lt;test version="1.0"&gt;123&lt;/test&gt;&lt;/root&gt;
</pre> <p>becomes</p> <pre class="brush: js">/* testing */ { test /* @version = "1.0" */ : 123}
</pre> </li> <li> <p>A bit of indentation is done, to keep things ledgible</p> </li>
</ol>
<div class="note"><strong>Note:</strong> Our algorithms comply with the point 3 (but without month decrease). The points 1 and 2 are automatically managed by the Javascript method <code><a href="/it/Javascript/Glossario/Oggetti_globali/JSON/stringify" title="/it/Javascript/Glossario/Oggetti_globali/JSON/stringify">JSON.stringify</a></code>.</div>
<h4>In sintesi</h4>
<p>Let's take <a href="#Algoritmo_JXON_3" title="Vai all'algoritmo JXON #3">the third algorithm</a> as the most representative JXON parsing algorithm. A single structured XML <code>Element</code> might have eight different configurations:</p>
<ol> <li>an empty element,</li> <li>an element with pure text content,</li> <li>an empty element with attributes,</li> <li>an element with text content and attributes,</li> <li>an element containing elements with different names,</li> <li>an element containing elements with identical names,</li> <li>an element containing elements and contiguous text,</li> <li>an element containing elements and non contiguous text.</li>
</ol>
<p>The following table shows the corresponding conversion patterns between XML and JSON according to the <a href="#Algoritmo_JXON_3" title="Vai all'algoritmo JXON #3">third algorithm</a>.</p>
<table> <thead> <tr> <th style="background: #faf9e2; color: #5d5636; text-align: center;"><strong>Case</strong></th> <th style="background: #faf9e2; color: #5d5636; text-align: center;"><strong>XML</strong></th> <th style="background: #faf9e2; color: #5d5636; text-align: center;"><strong>JSON</strong></th> <th style="background: #faf9e2; color: #5d5636; text-align: center;"><strong>Javascript access</strong></th> </tr> </thead> <tbody> <tr> <td style="background: #f6f6f6;">1</td> <td style="background: #f6f6f6;"><code>&lt;animal/&gt;</code></td> <td style="background: #f6f6f6;"><code>"animal": true</code></td> <td style="background: #f6f6f6;"><code>myObject.animal</code></td> </tr> <tr> <td style="background: #e7e5dc;">2</td> <td style="background: #e7e5dc;"><code>&lt;animal&gt;text&lt;/animal&gt;</code></td> <td style="background: #e7e5dc;"><code>"animal": "text"</code></td> <td style="background: #e7e5dc;"><code>myObject.animal</code></td> </tr> <tr> <td style="background: #f6f6f6;">3</td> <td style="background: #f6f6f6;"><code>&lt;animal name="value" /&gt;</code></td> <td style="background: #f6f6f6;"><code>"animal": {"@name": "value"}</code></td> <td style="background: #f6f6f6;"><code>myObject.animal["@name"]</code></td> </tr> <tr> <td style="background: #e7e5dc;">4</td> <td style="background: #e7e5dc;"><code>&lt;animal name="value"&gt;text&lt;/animal&gt;</code></td> <td style="background: #e7e5dc;"><code>"animal": { "@name": "value", "keyValue": "text" }</code></td> <td style="background: #e7e5dc;"><code>myObject.animal["@name"]</code>, <code>myObject.animal.keyValue</code></td> </tr> <tr> <td style="background: #f6f6f6;">5</td> <td style="background: #f6f6f6;"><code>&lt;animal&gt; &lt;dog&gt;Charlie&lt;/dog&gt; &lt;cat&gt;Deka&lt;/cat&gt; &lt;/animal&gt;</code></td> <td style="background: #f6f6f6;"><code>"animal": { "dog": "Charlie", "cat": "Deka" }</code></td> <td style="background: #f6f6f6;"><code>myObject.animal.dog</code>, <code>myObject.animal.cat</code></td> </tr> <tr> <td style="background: #e7e5dc;">6</td> <td style="background: #e7e5dc;"><code>&lt;animal&gt; &lt;dog&gt;Charlie&lt;/dog&gt; &lt;dog&gt;Mad Max&lt;/dog&gt; &lt;/animal&gt;</code></td> <td style="background: #e7e5dc;"><code>"animal": { "dog": ["Charlie", "Mad Max"] }</code></td> <td style="background: #e7e5dc;"><code>myObject.animal.dog[0]</code>, <code>myObject.animal.dog[1]</code></td> </tr> <tr> <td style="background: #f6f6f6;">7</td> <td style="background: #f6f6f6;"><code>&lt;animal&gt; in my house &lt;dog&gt;Charlie&lt;/dog&gt; &lt;/animal&gt;</code></td> <td style="background: #f6f6f6;"><code>"animal": { "keyValue": "in my house", "dog": "Charlie" }</code></td> <td style="background: #f6f6f6;"><code>myObject.animal.keyValue</code>, <code>myObject.animal.dog</code></td> </tr> <tr> <td style="background: #e7e5dc;">8</td> <td style="background: #e7e5dc;"><code>&lt;animal&gt; in my ho &lt;dog&gt;Charlie&lt;/dog&gt; use &lt;/animal&gt;</code></td> <td style="background: #e7e5dc;"><code>"animal": { "keyValue": "in my house", "dog": "Charlie" }</code></td> <td style="background: #e7e5dc;"><code>myObject.animal.keyValue</code>, <code>myObject.animal.dog</code></td> </tr> </tbody>
</table>
<h4>Considerazioni sul codice</h4>
<p>In these examples we chose to use a property named <code>keyValue</code> for the text content. The lack of standars for XML to JSON conversion leads developers to choose several property names for the text content of XML <code>Element</code> nodes which contain also other child nodes. Sometimes it is used a property called <code>$</code>. Other times it is used a property called <code>#text</code>. In the algorithms proposed here you can easily change this name, depending on your needs.</p>
<p>The choice of using a <code>true</code> value instead of a <code>null</code> value to represent empty nodes is due to the fact that when in an XML document there is an empty node the reason is often to express a <code>Boolean</code> content, as in this case:</p>
<pre class="brush: xml">&lt;car&gt;
  &lt;type&gt;Ferrari&lt;/type&gt;
  &lt;bought /&gt;
&lt;/car&gt;
</pre>
<p>If the value were <code>null</code> it would be more cumbersome to launch a code like this:</p>
<pre class="brush: js">if (myObject.car.Ferrari.bought) {
  // do something
}
</pre>
<div class="note">According to our <a href="#Algoritmo_JXON_3" title="Vai all'algoritmo JXON #3">third algorithm</a> and our <a href="#Algoritmo_JXON_4" title="Vai all'algoritmo JXON #4">fourth algorithm</a>, just <code>Text</code> nodes or <code>CDATASection</code> nodes which contain nothing but white spaces (precisely: <code>/^\s+$/</code>) are parsed as <code>null</code>.</div>
<p>An important consideration is that, using the third or the fourth algorithm, an XML Document can be used to create any type of Javascript object. For example, If you want to create an object like the following:</p>
<pre class="brush: js">{
  "bool": true,
  "array": ["Cinema", "Hot dogs", false],
  "object": {
    "nickname": "Jack",
    "registration_date": new Date(1995, 11, 25),
    "privileged_user": true
  },
  "num": 99,
  "text": "Hello World!"
}
</pre>
<p>you must just create an XML document with the following structure:</p>
<pre class="brush: xml">&lt;bool&gt;true&lt;/bool&gt;
&lt;array&gt;Cinema&lt;/array&gt;
&lt;array&gt;Hot dogs&lt;/array&gt;
&lt;array&gt;false&lt;/array&gt;
&lt;object&gt;
  &lt;nickname&gt;Jack&lt;/nickname&gt;
  &lt;registration_date&gt;Dec 25, 1995&lt;/registration_date&gt;
  &lt;privileged_user /&gt;
&lt;/object&gt;
&lt;num&gt;99&lt;/num&gt;
&lt;text&gt;Hello World!&lt;/text&gt;
</pre>
<p>This example also shows how the ideal JXON document is an XML document designed specifically to be converted in JSON format.</p>
<h3>Costruire file a partire da alberi DOM</h3>
<p>First, create a DOM tree as described in the <a href="/it/Come_creare_un_albero_DOM" title="it/Come_creare_un_albero_DOM">Come creare un albero DOM</a> article. If you have already have a DOM tree from using <a href="/it/XMLHttpRequest" title="it/XMLHttpRequest">XMLHttpRequest</a>, skip to the end of this section.</p>
<p>Now, let's serialize <code>doc</code> — the DOM tree — to a file (you can read more <a href="/en/Code_snippets/File_I//O" title="en/Code_snippets/File_I//O">about using files in Mozilla</a>):</p>
<pre class="brush: js">var oFOStream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
var oFile = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties).get("ProfD", Components.interfaces.nsILocalFile); // get profile folder
oFile.append("extensions"); // extensions sub-directory
oFile.append("{5872365E-67D1-4AFD-9480-FD293BEBD20D}"); // GUID of your extension
oFile.append("myXMLFile.xml"); // filename
oFOStream.init(oFile, 0x02 | 0x08 | 0x20, 0664, 0); // write, create, truncate
(new XMLSerializer()).serializeToStream(doc, oFOStream, ""); // rememeber, doc is the DOM tree
oFOStream.close();
</pre>
<h3>Costruire file a partire da oggetti XMLHttpRequest</h3>
<p>If you already have a DOM tree from using <a href="/it/XMLHttpRequest" title="it/XMLHttpRequest">XMLHttpRequest</a>, use the same code as above but replace <code>serializer.serializeToStream(doc, foStream, "")</code> with <code>serializer.serializeToStream(xmlHttpRequest.responseXML.documentElement, foStream, "")</code> where <code>xmlHttpRequest</code> is an instance of <code>XMLHttpRequest</code>.</p>
<p>Note that this first parses the XML retrieved from the server, then re-serializes it into a stream. Depending on your needs, you could just save the <code>xmlHttpRequest.responseText</code> directly.</p>
<h3>Resources</h3>
<ul> <li><a class="external" href="http://xulplanet.com/tutorials/mozsdk/xmlparse.php">Parsing and Serializing XML su XUL Planet</a></li>
</ul>
<p>{{ languages( { "ja": "ja/Parsing_and_serializing_XML", "en": "en/Parsing_and_serializing_XML" } ) }}</p>
Ripristina questa versione