web-dev-qa-db-fra.com

Générer un document XML en mémoire avec JavaScript

Je travaille sur une application Web qui doit envoyer du XML à un serveur principal. J'aimerais construire un document XML en mémoire côté client, mais en utilisant des routines de manipulation XML, au lieu d'ajouter d'innombrables chaînes ensemble. J'espère que jQuery pourra m'aider.

Disons que je dois générer ce document XML (jouet) avec JavaScript:

<report>
    <submitter>
        <name>John Doe</name>
    </submitter>
    <students>
        <student>
            <name>Alice</name>
            <grade>80</grade>
        </student>
        <student>
            <name>Bob</name>
            <grade>90</grade>
        </student>
    </students>
</report>

Pour commencer, je dois créer une sorte d'objet de document XML avec la racine "report". Je suppose que l'un d'eux devrait être proche, mais aucun ne fonctionne correctement, et/ou je ne peux pas vraiment comprendre comment utiliser l'objet correctement:

function generateDocument1()
{
    var report = $('<report></report>');
    return report;
}

function generateDocument2()
{
    var report = document.implementation.createDocument(null, "report", null);

    return new XMLSerializer().serializeToString(report);   
}

function createXmlDocument(string)
{
    var doc;
    if (window.DOMParser)
    {
        parser = new DOMParser();
        doc = parser.parseFromString(string, "application/xml");
    }
    else // Internet Explorer
    {
        doc = new ActiveXObject("Microsoft.XMLDOM");
        doc.async = "false";
        doc.loadXML(string); 
    }
    return doc;
}

function generateDocument3()
{
    var report = createXmlDocument('<report></report>');

    return report;
}

Maintenant, je veux créer et ajouter des éléments. Comment je fais ça? J'imagine que c'est quelque chose comme ça:

function generateReportXml()
{
    // Somehow generate the XML document object with root
    var report = /*???*/;

    // Somehow create the XML nodes
    var submitter = /*???*/;
    var name = /*???*/;

    // Somehow append name to submitter, and submitter to report
    submitter.append(name); /*???*/
    report.append(submitter); /*???*/

    // ... append the rest of the XML

    return report;
}

Des idées?

36
Shaggy Frog

Sans vous demander si vous devriez utiliser jQuery pour construire XML, voici quelques idées sur comment vous pourriez le faire:

// Simple helper function creates a new element from a name, so you don't have to add the brackets etc.
$.createElement = function(name)
{
    return $('<'+name+' />');
};

// JQ plugin appends a new element created from 'name' to each matched element.
$.fn.appendNewElement = function(name)
{
    this.each(function(i)
    {
        $(this).append('<'+name+' />');
    });
    return this;
}

/* xml root element - because html() does not include the root element and we want to 
 * include <report /> in the output. There may be a better way to do this.
 */
var $root = $('<XMLDocument />');

$root.append
(
    // one method of adding a basic structure
    $('<report />').append
    (
        $('<submitter />').append
        (
            $('<name />').text('John Doe')
        )
    )
    // example of our plugin
    .appendNewElement('students')
);

// get a reference to report
var $report = $root.find('report');

// get a reference to students
var $students = $report.find('students');
// or find students from the $root like this: $root.find('report>students');

// create 'Alice'
var $newStudent = $.createElement('student');
// add 'name' element using standard jQuery
$newStudent.append($('<name />').text('Alice'));
// add 'grade' element using our helper
$newStudent.append($.createElement('grade').text('80'));

// add 'Alice' to <students />
$students.append($newStudent);

// create 'Bob'
$newStudent = $.createElement('student');
$newStudent.append($('<name />').text('Bob'));
$newStudent.append($.createElement('grade').text('90'));

// add 'Bob' to <students />
$students.append($newStudent);

// display the markup as text
alert($root.html());

Sortie:

<report>
    <submitter>
        <name>John Doe</name>
    </submitter>
    <students>
        <student>
            <name>Alice</name>
            <grade>80</grade>
        </student>
        <student>
            <name>Bob</name>
            <grade>90</grade>
        </student>
    </students>
</report>
26
rath

La deuxième approche semble être une bonne voie à suivre. Il a été conçu pour fonctionner avec des documents XML. Une fois que vous avez créé l'objet document, utilisez les méthodes de manipulation DOM XML standard pour construire le document entier.

// creates a Document object with root "<report>"
var doc = document.implementation.createDocument(null, "report", null);

// create the <submitter>, <name>, and text node
var submitterElement = doc.createElement("submitter");
var nameElement = doc.createElement("name");
var name = doc.createTextNode("John Doe");

// append nodes to parents
nameElement.appendChild(name);
submitterElement.appendChild(nameElement);

// append to document
doc.documentElement.appendChild(submitterElement);

Cela peut sembler un peu bavard, mais c'est la bonne façon de créer le document XML. jQuery ne construit en fait aucun document XML, mais s'appuie simplement sur la propriété innerHTML pour analyser et reconstruire un DOM à partir d'une chaîne HTML. Le problème avec cette approche est que lorsque les noms de balises dans votre XML entrent en collision avec des noms de balises en HTML tels que <table> ou <option>, les résultats peuvent alors être imprévisibles. (EDIT: depuis 1.5 il y a jQuery.parseXML () qui fait en fait construire un document XML et évite ainsi ces problèmes - pour analyse uniquement.)

Pour réduire la verbosité, écrivez une petite bibliothèque d'aide, ou peut-être un plugin jQuery pour construire le document.

Voici une solution rapide et sale pour créer un document XML en utilisant une approche récursive.

// use this document for creating XML
var doc = document.implementation.createDocument(null, null, null);

// function that creates the XML structure
function Σ() {
    var node = doc.createElement(arguments[0]), text, child;

    for(var i = 1; i < arguments.length; i++) {
        child = arguments[i];
        if(typeof child == 'string') {
            child = doc.createTextNode(child);
        }
        node.appendChild(child);
    }

    return node;
};

// create the XML structure recursively
Σ('report',
    Σ('submitter',
        Σ('name', 'John Doe')
    ),
    Σ('students',
        Σ('student',
            Σ('name', 'Alice'),
            Σ('grade', '80')
        ),
        Σ('student',
            Σ('name', 'Bob'),
            Σ('grade', '90')
        )
    )
);

Résultats:

<report>​
    <submitter>​
        <name>​John Doe​</name>​
    </submitter>​
    <students>​
        <student>​
            <name>​Alice​</name>​
            <grade>​80​</grade>​
        </student>​
        <student>​
            <name>​Bob​</name>​
            <grade>​90​</grade>​
        </student>​
    </students>​
</report>​

Voir l'exemple

63
Anurag

J'ai trouvé que la fonction constructeur XMLWriter d'Ariel Flesler était un bon début pour créer du XML à partir de zéro (en mémoire), jetez un œil à ceci

http://flesler.blogspot.com/2008/03/xmlwriter-for-javascript.html

Exemple

function test(){    
   // XMLWriter will use DOMParser or Microsoft.XMLDOM
   var v = new  XMLWriter();
   v.writeStartDocument(true);
   v.writeElementString('test','Hello World');
   v.writeAttributeString('foo','bar');
   v.writeEndDocument();
   console.log( v.flush() );
}

Résultat

<?xml version="1.0" encoding="ISO-8859-1" standalone="true" ?>
<test foo="bar">Hello World</test>

Quelques mises en garde, cela n'échappe pas aux chaînes et la syntaxe peut rendre le coyote ++ laid.

2
Alex Nolasco

Avez-vous pensé à JSON? Vous pouvez enregistrer les données à l'aide d'objets. Ensuite, vous pouvez utiliser JSON.stringify(obj); et l'envoyer au serveur.

un exemple simple

var obj = new student('Alice',80);

function student(a,b){
  this.name=a;
  this.grade=b;
}

function sendToServer(){
  var dataString = JSON.stringify(obj);
  //the HTTP request
}
1
qw3n

Si la structure XML souhaitée peut être représentée dans un objet JavaScript ayant la même structure, vous pouvez créer un tel objet et utiliser la fonction suivante pour convertir cet objet en XML:

/*  Arguments:
      name: name of the root XML-element 
      val: the data to convert to XML
    Returns: XML string 
    Example: toXml("root", { items: { item: [1, 2] } })
      returns: "<root><items><item>1</item><item>2</item></items></root>"
*/
function toXml(name, val) {
    const map = {"<":"&lt;", ">":"&gt;", "&":"&amp;", "'":"&apos", '"':"&quot;"};
    if (Array.isArray(val)) return val.map(elem => toXml(name, elem)).join``;
    const content =  Object(val) === val
        ? Object.keys(val).map(key => toXml(key, val[key])).join``
        : String(val).replace(/[<>&'"]/g, m => map[m]);
    return `<${name}>${content}</${name}>`;
}

// Example:
const report = {
    submitter: { name: "John Doe" },
    students: {
        student: [{ name: "Alice", grade: 80 }, 
                  { name: "Bob",   grade: 90 }]
    }
};

console.log(
    '<?xml version="1.0" encoding="UTF-8" standalone="no" ?>' +
    toXml("report", report));
0
trincot