web-dev-qa-db-fra.com

Erreur d'analyse XML: entité non définie

J'ai effectué des recherches sur stackoverflow sur ce problème et j'ai trouvé quelques sujets, mais j'estime qu'il n'y a pas vraiment de réponse solide à ce sujet.

J'ai un formulaire que les utilisateurs soumettent et la valeur du champ est stockée dans un fichier XML. Le XML est configuré pour être codé avec UTF-8.

De temps en temps, un utilisateur va copier/coller du texte quelque part et c'est à ce moment-là que j'obtiens l'erreur "entité non définie".

Je réalise que XML ne prend en charge que quelques entités sélectionnées et que tout ce qui est au-delà n’est pas reconnu - d’où l’erreur d’analyse.

D'après ce que je comprends, il y a quelques options que j'ai vues:

  1. Je peux trouver et remplacer tous les   et les échanger avec   ou un espace réel.
  2. Je peux placer le code en question dans une section CDATA.
  3. Je peux inclure ces entités dans le fichier XML.

Ce que je fais avec le fichier XML, c'est que l'utilisateur puisse entrer du contenu dans un formulaire, il est stocké dans un fichier XML, puis affiché sous forme de XHTML sur une page Web (analysée avec SimpleXML).

Parmi les trois options, ou toute autre option dont je n’ai pas connaissance, quel est le meilleur moyen de traiter avec ces entités?

Merci, .__ Ryan

METTRE À JOUR

Je tiens à remercier tout le monde pour les bons commentaires. J'ai effectivement déterminé la cause de mes erreurs d'entité. Toutes les suggestions m'ont fait approfondir la question!

Certaines zones de texte étaient tout simplement anciennes, mais mes zones de texte ont été améliorées avec TinyMCE. En regardant de plus près, il apparaît que les avertissements PHP ont toujours référencé les données des zones de texte améliorées de TinyMCE. Plus tard, j'ai remarqué sur un PC que tous les caractères avaient été sortis (parce qu'il ne pouvait pas les lire), mais sur un MAC, on pouvait voir de petites cases carrées faisant référence au numéro unicode de ce caractère. La raison pour laquelle il est apparu dans les carrés sur un MAC en premier lieu, c'est parce que j'ai utilisé utf8_encode pour encoder des données qui n'étaient pas au format UTF pour empêcher d'autres erreurs d'analyse (ce qui est également lié à TinyMCE).

La solution à tout cela était assez simple:

J'ai ajouté cette ligne entity_encoding : "utf-8" dans mon tinyMCE.init. Maintenant, tous les personnages apparaissent comme ils sont supposés le faire.

J'imagine que la seule chose que je ne comprends pas, c'est pourquoi les caractères apparaissent toujours lorsqu'ils sont placés dans des zones de texte, car rien ne les convertit en UTF, mais avec TinyMCE, c'était un problème.

28
NightHawk

Je conviens que c'est purement un problème d'encodage. En PHP, voici comment j'ai résolu ce problème:

  1. Avant de transmettre le fragment html au constructeur SimpleXMLElement, je l'ai décodé à l'aide de html_entity_decode.

  2. Puis l'encodé en utilisant utf8_encode().

$headerDoc = '<temp>' . utf8_encode(html_entity_decode($headerFragment)) . '</temp>'; 
$xmlHeader = new SimpleXMLElement($headerDoc);

Maintenant, le code ci-dessus ne renvoie aucune entité non définie .

22
Gaurav Arya

Vous pouvez analyser le texte en HTML et le rediffuser avec les entités numériques respectives uniquement (comme: &nbsp;&#160;). Dans tous les cas, simplement utiliser une entrée utilisateur non assainie est une mauvaise idée.

Toutes les entités numériques sont autorisées en XML, seules les entités nommées nommées en HTML ne fonctionnent pas (à l'exception de &amp;, &quot;, &lt;, &gt;, &apos;).

La plupart du temps cependant, vous pouvez simplement écrire le caractère actuel (&ouml;ö) dans le fichier XML, de sorte qu'il n'est pas nécessaire d'utiliser une référence d'entité. Si vous utilisez une API DOM pour manipuler votre XML (et vous devriez!), C'est votre pari le plus sûr.

Enfin (il s’agit de la solution de développeur paresseuse), vous pouvez créer un fichier XML défectueux (c’est-à-dire, mal formé, avec des erreurs d’entité) et simplement le passer à l’ordre pour les corrections nécessaires. Cela peut fonctionner ou échouer en fonction de comment a tout cassé. D'après mon expérience, bien rangé est plutôt intelligent, et vous permet de vous en tirer beaucoup.

15
Tomalak

1. Je peux trouver et remplacer tous les [&nbsp;?] Et les échanger avec [&#160;?] Ou un espace réel.

C'est une méthode robuste, mais elle nécessite de disposer d'un tableau de toutes les entités HTML (je suppose que l'entrée collée provient de HTML) et d'analyser le texte collé pour les références d'entité.

2. Je peux placer le code en question dans une section CDATA.

En d'autres termes, désactiver l'analyse pour toute la section? Ensuite, vous devrez analyser le problème d'une autre manière. Pourrait travailler.

3. Je peux inclure ces entités dans le fichier XML.

Vous voulez dire inclure les définitions d'entité? Je pense que c’est un moyen simple et robuste, si vous n’avez pas l’esprit de rendre le fichier XML un peu plus gros. Vous pourriez avoir un fichier "inclus" (trouvez-en un sur le Web) qui est une entité externe, que vous référencez en haut de votre fichier XML principal.

Un inconvénient est que l'analyseur XML que vous utilisez doit en être un qui traite des entités externes (ce que tous les analyseurs ne sont pas obligés de faire). Et il doit résoudre correctement l'URL (éventuellement relative) de l'entité externe en un élément accessible. Ce n'est pas si grave, mais cela peut augmenter les contraintes sur vos outils de traitement.

4. Vous pouvez interdire les non-XML dans le contenu collé. Entre autres choses, cela interdirait les références d'entités qui ne sont pas prédéfinies en XML (les 5 mentionnées par Tomalak) ou définies dans le contenu lui-même. Toutefois, cela peut enfreindre les exigences de l'application si les utilisateurs doivent pouvoir y coller du code HTML.

5. Vous pouvez analyser le contenu collé au format HTML dans une arborescence DOM en définissant someDiv.innerHTML = thePastedContent; En d’autres termes, créez un div quelque part (probablement display = none, sauf pour le débogage). Supposons que vous avez ensuite une variable javascript myDiv qui contient cet élément div et une autre variable myField qui contient l'élément qui est votre champ de texte de saisie. Puis en javascript tu fais

myDiv.innerHTML = myField.value;

qui prend le texte non analysé de myField, le analyse dans une arborescence DOM HTML et le colle dans myDiv en tant que contenu HTML.

Ensuite, vous utiliseriez une méthode basée sur un navigateur pour sérialiser (= "décomposer") l’arbre DOM en XML. Voir par exemple cette question . Ensuite, vous envoyez le résultat au serveur au format XML.

Que vous souhaitiez résoudre ce problème dans le navigateur ou sur le serveur (comme suggéré par @Hannes) dépendra de la taille des données, de la rapidité de la réponse, de la puissance de votre serveur et de l’importance que vous accordez à l'envoi de pirates XML mal formé à dessein.

4
LarsH

Si vous voulez convertir tous les caractères, cela peut vous aider (je l'ai écrit il y a quelque temps):

http://www.lautr.com/convert-all-applicable-characters-to-numeric-entities-for-use-in-xml

function _convertAlphaEntitysToNumericEntitys($entity) {
  return '&#'.ord(html_entity_decode($entity[0])).';';
}

$content = preg_replace_callback(
  '/&([\w\d]+);/i',
  '_convertAlphaEntitysToNumericEntitys',
  $content);

function _convertAsciOver127toNumericEntitys($entity) {
  if(($asciCode = ord($entity[0])) > 127)
    return '&#'.$asciCode.';';
  else
    return $entity[0];
}

$content = preg_replace_callback(
  '/[^\w\d ]/i',
  '_convertAsciOver127toNumericEntitys', $content);
1
Hannes

Cette question est un problème général pour toute langue qui analyse XML ou JSON (donc, fondamentalement, toutes les langues).

Les réponses ci-dessus sont pour PHP, mais une solution Perl serait aussi simple que ...

my $excluderegex =
    '^\n\x20-\x20' .   # Don't Encode Spaces
       '\x30-\x39' .   # Don't Encode Numbers
       '\x41-\x5a' .   # Don't Encode Capitalized Letters
       '\x61-\x7a' ;   # Don't Encode Lowercase Letters

    # in case anything is already encoded
$value = HTML::Entities::decode_entities($value);

    # encode properly to numeric
$value = HTML::Entities::encode_numeric($value, $excluderegex);
0
HoldOffHunger