Wie entkomme ich XML-Entitäten in Javascript?

78

In JavaScript (serverseitige NodeJS) schreibe ich ein Programm, das XML als Ausgabe generiert.

Ich erstelle die XML durch Verketten einer Zeichenfolge:

str += '<' + key + '>';
str += value;
str += '</' + key + '>';

Das Problem ist: Was passiert , wenn valueZeichen enthalten , wie '&', '>'oder '<'? Was ist der beste Weg, um diesen Charakteren zu entkommen?

oder gibt es eine Javascript-Bibliothek, um die sich XML-Entitäten entziehen können?

Zo72
quelle

Antworten:

119

HTML - Codierung ist einfach zu ersetzen &, ", ', <und >Zeichen mit ihrer Einheit Äquivalente. Reihenfolge ist wichtig, wenn Sie die &Zeichen nicht zuerst ersetzen , werden Sie einige der Entitäten doppelt codieren:

if (!String.prototype.encodeHTML) {
  String.prototype.encodeHTML = function () {
    return this.replace(/&/g, '&amp;')
               .replace(/</g, '&lt;')
               .replace(/>/g, '&gt;')
               .replace(/"/g, '&quot;')
               .replace(/'/g, '&apos;');
  };
}

Als @Johan BW de Vries darauf hingewiesen, das mit den Tag - Namen haben Fragen werden, möchte ich klarstellen , dass ich die Annahme gemacht , dass dies für die benutzt wurde, um value nur

Umgekehrt, wenn Sie HTML-Entitäten 1 dekodieren möchten , stellen Sie sicher, dass Sie nach allem anderen dekodieren &amp;, &damit Sie keine Entitäten doppelt dekodieren:

if (!String.prototype.decodeHTML) {
  String.prototype.decodeHTML = function () {
    return this.replace(/&apos;/g, "'")
               .replace(/&quot;/g, '"')
               .replace(/&gt;/g, '>')
               .replace(/&lt;/g, '<')
               .replace(/&amp;/g, '&');
  };
}

1 nur die Grundlagen, nicht &copy;zu ©oder andere solche Dinge


Soweit Bibliotheken betroffen sind. Underscore.js (oder Lodash, wenn Sie es vorziehen) bietet eine _.escapeMethode zum Ausführen dieser Funktionalität.

zzzzBov
quelle
2
Dies deckt fast die 5 XML-Entitäten ab. Benötigen Sie nur @apos;
Ryan
2
Dies sieht so aus, als würde immer wieder dieselbe Zeichenfolge ersetzt, was bei der Verarbeitung vieler Daten zu einer hohen Leistung führen kann. Eine schnellere Alternative?
Jonny
2
@Jonny, Der reguläre Ausdruck bietet eine schlechtere Leistung als die mehrfachen Aufrufe von .replace(). In beiden Fällen müssten Sie über eine große Datenmenge verfügen, um signifikante Probleme zu erkennen. Eine schnellere Alternative wäre, Ihre App zu vergleichen und den tatsächlichen Choke-Punkt (normalerweise verschachtelte Schleifen) zu finden, anstatt sich über etwas so Vernachlässigbares wie dieses Gedanken zu machen.
zzzzBov
1
Ich hatte 100-200 Datenzeilen in einer Google-Tabelle. Ich habe das in plists (xml) konvertiert und musste diese xml-Entitäten ersetzen. Ich habe eine benutzerdefinierte Javascript-Funktion mit dem obigen Code dafür geschrieben. Es hat funktioniert, war aber sehr langsam. Die Tabelle ist manchmal etwas verstopft, aber da es sich nur um einen "einmaligen" Schritt handelt, spielte die Geschwindigkeit am Ende keine Rolle.
Jonny
4
Ich weiß, dass diese Antwort alt ist, aber nur um JS-Neulingen klar zu machen: Das Anhängen von Zufallsfunktionen, die für einen standardisierten Vorschlag keine Polyfills sind, an globale Prototypen ist eine schlechte Idee.
austin_ce
104

Dies könnte bei gleichem Ergebnis etwas effizienter sein:

function escapeXml(unsafe) {
    return unsafe.replace(/[<>&'"]/g, function (c) {
        switch (c) {
            case '<': return '&lt;';
            case '>': return '&gt;';
            case '&': return '&amp;';
            case '\'': return '&apos;';
            case '"': return '&quot;';
        }
    });
}
hgoebl
quelle
Dies scheint eine bessere Lösung zu sein. Warum keine Upticks?
Victor Grazi
1
@ VictorGrazi: Ihr Recht, es ist in 49 von 50 Tests die schnellere Lösung. Vielleicht liegt es daran, dass es fast 5 Jahre jünger ist als die akzeptierte Antwort.
Sebastian
1
@ Sebastian ahh, das würde es erklären, danke. Guck mal Leute ^^^^^ das ist die Lösung die du willst !!!
Victor Grazi
Dies scheint mir eine bessere Lösung zu sein als die akzeptierte Antwort, bei der die gesamte Zeichenfolge fünfmal durchlaufen wird (seriell, wodurch der Spielraum für die Optimierung der JS-Engine verringert wird), um nach einer Übereinstimmung mit einem einzelnen Zeichen zu suchen. Die Lösung von hgoebl durchläuft die Eingabezeichenfolge nur einmal und versucht, jedes Zeichen einer von fünf Bedingungen zuzuordnen . Die Frage ist, was teurer ist: 1) Durchlaufen der Zeichenfolge; oder: 2) jedes Zeichen mit 5 möglichen Zeichen abgleichen. Meine Intuition ist, dass 1) teurer wäre.
Jamie Birch
1
Die @ RanLottem-Dekodierung ist viel komplizierter, wenn die Eingabe HTML ist (siehe Wikipedia) . Es ist besser, einen Parser (XML oder Dokument) zu verwenden.
Hgoebl
21

Wenn Sie jQuery haben, ist hier eine einfache Lösung:

  String.prototype.htmlEscape = function() {
    return $('<div/>').text(this.toString()).html();
  };

Verwenden Sie es so:

"<foo&bar>".htmlEscape(); -> "&lt;foo&amp;bar&gt"

Lammhaanxy
quelle
1
Ich mag diese Technik wegen ihrer Einstellung "Lass es den Browser machen". Gibt es Nachteile, möglicherweise andere als eine schlechtere Leistung, da dies über die DOM-API erfolgt?
Avernet
Einfache und doppelte Anführungszeichen werden mit dieser Technik nicht entkommen: $('<div/>').text('<&\'>"').html() -> "&lt;&amp;'&gt;""
Majgis
1
Einfache und doppelte Anführungszeichen müssen im Allgemeinen nicht maskiert werden.
Lambshaanxy
7

Sie können die folgende Methode verwenden. Ich habe dies im Prototyp hinzugefügt, um den Zugriff zu erleichtern. Ich habe auch negative Vorausschau verwendet, damit es die Dinge nicht durcheinander bringt, wenn Sie die Methode zweimal oder öfter aufrufen.

Verwendung:

 var original = "Hi&there";
 var escaped = original.EncodeXMLEscapeChars();  //Hi&amp;there

Die Dekodierung erfolgt automatisch im XML-Parser.

Methode :

//String Extenstion to format string for xml content.
//Replces xml escape chracters to their equivalent html notation.
String.prototype.EncodeXMLEscapeChars = function () {
    var OutPut = this;
    if ($.trim(OutPut) != "") {
        OutPut = OutPut.replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
        OutPut = OutPut.replace(/&(?!(amp;)|(lt;)|(gt;)|(quot;)|(#39;)|(apos;))/g, "&amp;");
        OutPut = OutPut.replace(/([^\\])((\\\\)*)\\(?![\\/{])/g, "$1\\\\$2");  //replaces odd backslash(\\) with even.
    }
    else {
        OutPut = "";
    }
    return OutPut;
};
sudhAnsu63
quelle
3
Unterschätzte ausgezeichnete Lösung. Stellen Sie sicher, dass Sie nicht mit dem berüchtigten & amp; amp; Zeichenfolge in Ihrer Ausgabe ist schön.
Steve Westbrook
Mit diesem Code haben Sie gerade alle Instanzen von String in allen Anwendungen bearbeitet , z. B. let a = 'foo'sind diese Codes betroffen. Erstellen Sie besser eine Hilfsfunktion, anstatt den Prototyp zu erweitern.
Lukas Liesis
Bitte mutieren Sie keine eingebauten Objekte, da dies zu Konflikten führt und daher eine sehr schlechte Praxis ist.
Slikts
1

Ich habe ursprünglich die akzeptierte Antwort im Produktionscode verwendet und festgestellt, dass sie bei starker Verwendung tatsächlich sehr langsam ist. Hier ist eine viel schnellere Lösung (läuft mit mehr als der doppelten Geschwindigkeit):

   var escapeXml = (function() {
        var doc = document.implementation.createDocument("", "", null)
        var el = doc.createElement("temp");
        el.textContent = "temp";
        el = el.firstChild;
        var ser =  new XMLSerializer();
        return function(text) {
            el.nodeValue = text;
            return ser.serializeToString(el);
        };
    })();

console.log(escapeXml("<>&")); //&lt;&gt;&amp;
jordancpaul
quelle
Dies setzt voraus, dass Sie ein documentObjekt haben. Ich habe es nicht
Lukas Liesis
1

Vielleicht kannst du das versuchen,

function encodeXML(s) {
  const dom = document.createElement('div')
  dom.textContent = s
  return dom.innerHTML
}

Referenz

Krone
quelle
1

Achtung, alle Regexing ist nicht gut, wenn Sie XML in XML haben.
Durchlaufen Sie stattdessen die Zeichenfolge einmal und ersetzen Sie alle Escape-Zeichen.
Auf diese Weise können Sie denselben Charakter nicht zweimal überfahren.

function _xmlAttributeEscape(inputString)
{
    var output = [];

    for (var i = 0; i < inputString.length; ++i)
    {
        switch (inputString[i])
        {
            case '&':
                output.push("&amp;");
                break;
            case '"':
                output.push("&quot;");
                break;
            case "<":
                output.push("&lt;");
                break;
            case ">":
                output.push("&gt;");
                break;
            default:
                output.push(inputString[i]);
        }


    }

    return output.join("");
}
Stefan Steiger
quelle
0

Technisch gesehen sind &, <und> keine gültigen XML-Entitätsnamenzeichen. Wenn Sie der Schlüsselvariablen nicht vertrauen können, sollten Sie sie herausfiltern.

Wenn Sie möchten, dass sie als HTML-Entitäten maskiert werden, können Sie beispielsweise http://www.strictly-software.com/htmlencode verwenden .

Johan BW de Vries
quelle
0

Wenn etwas zuvor entkommen ist, können Sie dies versuchen, da dies nicht wie bei vielen anderen doppelt entkommt

function escape(text) {
    return String(text).replace(/(['"<>&'])(\w+;)?/g, (match, char, escaped) => {
        if(escaped) 
            return match

        switch(char) {
            case '\'': return '&quot;'
            case '"': return '&apos;'
            case '<': return '&lt;'
            case '>': return '&gt;'
            case '&': return '&amp;'
        }
    })
}
Lostfields
quelle
-1

Das ist einfach:

sText = ("" + sText).split("<").join("&lt;").split(">").join("&gt;").split('"').join("&#34;").split("'").join("&#39;");
Per Ghosh
quelle
1
In welcher Welt ist dies im Vergleich zu den oben genannten Ersetzungsmethoden „einfach“?
Sam Holder
Einfach zu schreiben, ich habe nicht gesagt, dass es einfacher ist als die oben genannten. Es ist einfach anders
Per Ghosh
4
Ich bin ratlos und versuche, an eine schlechtere Lösung zu denken
Developerbmw
@developerbmw Wenn Sie keine Methode hinzufügen und keine jquery verwenden möchten, ist dies eine der besten Lösungen
Per Ghosh
1
@developerbmw Aus Gründen der Lesbarkeit ist es manchmal besser, Code so zu schreiben, dass die Lesefunktion von oben nach unten möglich ist. Dynamische Sprachen sind sehr einfach schnell unlesbar zu machen. Es hängt von der Situation ab. Verwenden Sie auch Komponenten, die in verschiedenen Szenarien verwendet werden und eine eigene Logik benötigen. Eine kleine Komponente benötigt möglicherweise keine Funktionen, wenn die Logik nur in einer Methode verwendet wird. Ich bin ein C ++ - Entwickler und habe viel von C programmiert. C ist sehr einfach zu lesen und wenn Sie wissen warum, dann kennen Sie meine Argumentation dazu
Per Ghosh