web-dev-qa-db-fra.com

Le meilleur moyen de coder des données texte pour XML en Java?

Très semblable à cette question , sauf pour Java.

Quelle est la méthode recommandée pour coder des chaînes pour une sortie XML en Java? Les chaînes peuvent contenir des caractères tels que "&", "<", etc.

82
Epaga

Très simplement: utilisez une bibliothèque XML. De cette façon, il s'agira bien de right au lieu de nécessiter une connaissance détaillée des bits de la spécification XML.

39
Jon Skeet

Comme d'autres l'ont mentionné, l'utilisation d'une bibliothèque XML est le moyen le plus simple. Si vous voulez vous échapper, vous pouvez regarder dans StringEscapeUtils dans la bibliothèque Apache Commons Lang .

113
Fabian Steeg

Il suffit d'utiliser.

<![CDATA[ your text here ]]>

Cela permettra à tous les caractères sauf la fin 

]]>

Vous pouvez donc inclure des caractères illégaux tels que & et>. Par exemple.

<element><![CDATA[ characters such as & and > are allowed ]]></element>

Cependant, les attributs devront être protégés car les blocs CDATA ne peuvent pas être utilisés pour eux.

18
ng.

Essaye ça:

String xmlEscapeText(String t) {
   StringBuilder sb = new StringBuilder();
   for(int i = 0; i < t.length(); i++){
      char c = t.charAt(i);
      switch(c){
      case '<': sb.append("&lt;"); break;
      case '>': sb.append("&gt;"); break;
      case '\"': sb.append("&quot;"); break;
      case '&': sb.append("&amp;"); break;
      case '\'': sb.append("&apos;"); break;
      default:
         if(c>0x7e) {
            sb.append("&#"+((int)c)+";");
         }else
            sb.append(c);
      }
   }
   return sb.toString();
}
14
Pointer Null

Cela a bien fonctionné pour moi de fournir une version échappée d'une chaîne de texte:

public class XMLHelper {

/**
 * Returns the string where all non-ascii and <, &, > are encoded as numeric entities. I.e. "&lt;A &amp; B &gt;"
 * .... (insert result here). The result is safe to include anywhere in a text field in an XML-string. If there was
 * no characters to protect, the original string is returned.
 * 
 * @param originalUnprotectedString
 *            original string which may contain characters either reserved in XML or with different representation
 *            in different encodings (like 8859-1 and UFT-8)
 * @return
 */
public static String protectSpecialCharacters(String originalUnprotectedString) {
    if (originalUnprotectedString == null) {
        return null;
    }
    boolean anyCharactersProtected = false;

    StringBuffer stringBuffer = new StringBuffer();
    for (int i = 0; i < originalUnprotectedString.length(); i++) {
        char ch = originalUnprotectedString.charAt(i);

        boolean controlCharacter = ch < 32;
        boolean unicodeButNotAscii = ch > 126;
        boolean characterWithSpecialMeaningInXML = ch == '<' || ch == '&' || ch == '>';

        if (characterWithSpecialMeaningInXML || unicodeButNotAscii || controlCharacter) {
            stringBuffer.append("&#" + (int) ch + ";");
            anyCharactersProtected = true;
        } else {
            stringBuffer.append(ch);
        }
    }
    if (anyCharactersProtected == false) {
        return originalUnprotectedString;
    }

    return stringBuffer.toString();
}

}
13

StringEscapeUtils.escapeXml() n'échappe pas aux caractères de contrôle (<0x20). XML 1.1 autorise les caractères de contrôle; XML 1.0 ne le fait pas. Par exemple, XStream.toXML() sérialisera volontiers les caractères de contrôle d'un objet Java en XML, qu'un analyseur XML 1.0 refusera.

Pour échapper aux caractères de contrôle avec Apache commons-lang, utilisez

NumericEntityEscaper.below(0x20).translate(StringEscapeUtils.escapeXml(str))
8
Steve Mitchell
public String escapeXml(String s) {
    return s.replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
}
6
iCrazybest

Cette question a huit ans et n’est toujours pas une réponse tout à fait correcte! Non, vous ne devriez pas avoir à importer une API tierce entière pour effectuer cette tâche simple. Mauvais conseil.

La méthode suivante va:

  • gérer correctement les caractères hors du plan multilingue de base
  • caractères d'échappement requis en XML
  • échappe à tous les caractères non-ASCII, ce qui est optionnel mais courant
  • remplacez illégale caractères dans XML 1.0 par le caractère de substitution Unicode. Il n'y a pas de meilleure option ici - les supprimer est tout aussi valable.

J'ai essayé d'optimiser le cas le plus courant, tout en m'assurant que vous puissiez canaliser/dev/random à travers cela et obtenir une chaîne valide en XML.

public static String encodeXML(CharSequence s) {
    StringBuilder sb = new StringBuilder();
    int len = s.length();
    for (int i=0;i<len;i++) {
        int c = s.charAt(i);
        if (c >= 0xd800 && c <= 0xdbff && i + 1 < len) {
            c = ((c-0xd7c0)<<10) | (s.charAt(++i)&0x3ff);    // UTF16 decode
        }
        if (c < 0x80) {      // ASCII range: test most common case first
            if (c < 0x20 && (c != '\t' && c != '\r' && c != '\n')) {
                // Illegal XML character, even encoded. Skip or substitute
                sb.append("&#xfffd;");   // Unicode replacement character
            } else {
                switch(c) {
                  case '&':  sb.append("&amp;"); break;
                  case '>':  sb.append("&gt;"); break;
                  case '<':  sb.append("&lt;"); break;
                  // Uncomment next two if encoding for an XML attribute
//                  case '\''  sb.append("&apos;"); break;
//                  case '\"'  sb.append("&quot;"); break;
                  // Uncomment next three if you prefer, but not required
//                  case '\n'  sb.append("&#10;"); break;
//                  case '\r'  sb.append("&#13;"); break;
//                  case '\t'  sb.append("&#9;"); break;

                  default:   sb.append((char)c);
                }
            }
        } else if ((c >= 0xd800 && c <= 0xdfff) || c == 0xfffe || c == 0xffff) {
            // Illegal XML character, even encoded. Skip or substitute
            sb.append("&#xfffd;");   // Unicode replacement character
        } else {
            sb.append("&#x");
            sb.append(Integer.toHexString(c));
            sb.append(';');
        }
    }
    return sb.toString();
}

Edit: pour ceux qui insistent sur le fait qu'il est insensé d'écrire votre propre code pour cela alors qu'il existe d'excellentes API Java pour gérer XML, vous voudrez peut-être savoir que l'API StAX fournie avec Oracle testé les autres) ne parvient pas à coder le contenu CDATA correctement: il ne s'échappe pas]]> des séquences dans le contenu. Une bibliothèque tierce, même faisant partie du noyau Java, n’est pas toujours la meilleure option.

6
Mike B

Alors que l'idéalisme dit d'utiliser une bibliothèque XML, à mon humble avis, si vous avez une idée de base de XML, le sens commun et les performances disent qu'il le modélise parfaitement. C'est sans doute plus lisible aussi. Bien que l'utilisation des routines d'échappement d'une bibliothèque soit probablement une bonne idée.

Considérez ceci: XML était était destiné à être écrit par des humains.

Utilisez des bibliothèques pour générer du XML lorsque votre XML en tant qu '"objet" modélise mieux votre problème. Par exemple, si des modules enfichables participent au processus de construction de ce XML.

Edit: pour savoir comment échapper au XML dans les modèles, l'utilisation de CDATA ou de escapeXml(string) à partir de JSTL sont deux bonnes solutions, escapeXml(string) peut être utilisé de la manière suivante:

<%@taglib prefix="fn" uri="http://Java.Sun.com/jsp/jstl/functions"%>

<item>${fn:escapeXml(value)}</item>
6
Amr Mostafa

Le comportement de StringEscapeUtils.escapeXml () est passé de Commons Lang 2.5 à 3.0 . Il n’échappe plus les caractères Unicode supérieurs à 0x7f.

C’est une bonne chose, l’ancienne méthode était un peu trop désireuse d’échapper aux entités qui pourraient simplement être insérées dans un document utf8.

Les nouveaux évasions à intégrer à Google Guava 11.0 semblent également prometteurs: http://code.google.com/p/guava-libraries/issues/detail?id=799

6
Jasper Krijgsman

Remarque: Votre question concerne escaping, pas encoding. Echapper utilise <, etc. pour permettre à l’analyseur de faire la distinction entre "ceci est une commande XML" et "ceci est du texte". Le codage est ce que vous spécifiez dans l'en-tête XML (UTF-8, ISO-8859-1, etc.).

Tout d’abord, comme tout le monde le dit, utilisez une bibliothèque XML. XML a l'air simple mais l'encodage + les échappements sont voodoo (ce que vous remarquerez dès que vous rencontrerez des trémas, des japonais et d'autres trucs étranges comme " chiffres pleine largeur " (& # FF11; vaut 1)). Garder XML lisible par l'homme est une tâche de Sisyphe.

Je suggère de ne jamais essayer d'être intelligent sur l'encodage de texte et l'échappement en XML. Mais ne laissez pas cela vous empêcher d'essayer; Rappelez-vous juste quand il vous mord (et il le fera).

Cela dit, si vous utilisez uniquement UTF-8, vous pouvez envisager cette stratégie pour rendre les choses plus lisibles:

  • Si le texte contient '<', '>' ou '&', placez-le dans <![CDATA[ ... ]]>
  • Si le texte ne contient pas ces trois caractères, ne le déformez pas.

J'utilise cela dans un éditeur SQL et cela permet aux développeurs de couper et coller le code SQL d'un outil SQL tiers dans le XML sans se soucier de s'échapper. Cela fonctionne parce que le SQL ne peut pas contenir de tréma dans notre cas, donc je suis en sécurité.

5
Aaron Digulla

Pour ceux qui recherchent la solution la plus rapide à écrire: utilisez les méthodes de Apache commons-lang :

N'oubliez pas d'inclure la dépendance:

<dependency>
  <groupId>org.Apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>3.5</version> <!--check current version! -->
</dependency>
4
Dariusz

Bien que je sois d’accord avec le principe de Jon Skeet, je n’ai parfois pas la possibilité d’utiliser une bibliothèque XML externe. Et je trouve étrange que les deux fonctions pour échapper/décompresser une valeur simple (attribut ou balise, document non complet) ne soient pas disponibles dans les bibliothèques XML standard incluses avec Java.

En conséquence et sur la base des différentes réponses que j'ai vues publiées ici et ailleurs, voici la solution que j'ai finalement créée (rien ne fonctionnait comme un simple copier/coller):

  public final static String ESCAPE_CHARS = "<>&\"\'";
  public final static List<String> ESCAPE_STRINGS = Collections.unmodifiableList(Arrays.asList(new String[] {
      "&lt;"
    , "&gt;"
    , "&amp;"
    , "&quot;"
    , "&apos;"
  }));

  private static String UNICODE_LOW =  "" + ((char)0x20); //space
  private static String UNICODE_HIGH = "" + ((char)0x7f);

  //should only use for the content of an attribute or tag      
  public static String toEscaped(String content) {
    String result = content;

    if ((content != null) && (content.length() > 0)) {
      boolean modified = false;
      StringBuilder stringBuilder = new StringBuilder(content.length());
      for (int i = 0, count = content.length(); i < count; ++i) {
        String character = content.substring(i, i + 1);
        int pos = ESCAPE_CHARS.indexOf(character);
        if (pos > -1) {
          stringBuilder.append(ESCAPE_STRINGS.get(pos));
          modified = true;
        }
        else {
          if (    (character.compareTo(UNICODE_LOW) > -1)
               && (character.compareTo(UNICODE_HIGH) < 1)
             ) {
            stringBuilder.append(character);
          }
          else {
            stringBuilder.append("&#" + ((int)character.charAt(0)) + ";");
            modified = true;
          }
        }
      }
      if (modified) {
        result = stringBuilder.toString();
      }
    }

    return result;
  }

Ce qui précède accueille plusieurs choses différentes:

  1. évite d'utiliser la logique basée sur les caractères jusqu'à ce que cela soit absolument nécessaire - améliore la compatibilité unicode
  2. tente d'être aussi efficace que possible, étant donné que la probabilité est la deuxième condition "si" est probablement la voie la plus utilisée
  3. est une fonction pure; c'est-à-dire thread-safe
  4. optimise bien avec le ramasse-miettes en ne renvoyant le contenu de StringBuilder que si quelque chose a réellement changé - sinon, la chaîne d'origine est renvoyée

À un moment donné, j'écrirai l'inversion de cette fonction, toUnescaped (). Je n'ai tout simplement pas le temps de le faire aujourd'hui. Quand je le ferai, je viendrai mettre à jour cette réponse avec le code. :)

4
chaotic3quilibrium

Pour échapper aux caractères XML, le plus simple consiste à utiliser le projet Apache Commons Lang, JAR téléchargeable à l’adresse: http://commons.Apache.org/lang/

La classe est la suivante: org.Apache.commons.lang3.StringEscapeUtils;

Il a une méthode nommée "escapeXml", qui retournera une chaîne échappée de manière appropriée. 

3
Greg Burdett

Voici une solution simple et idéale pour encoder des caractères accentués!

String in = "Hi Lârry & Môe!";

StringBuilder out = new StringBuilder();
for(int i = 0; i < in.length(); i++) {
    char c = in.charAt(i);
    if(c < 31 || c > 126 || "<>\"'\\&".indexOf(c) >= 0) {
        out.append("&#" + (int) c + ";");
    } else {
        out.append(c);
    }
}

System.out.printf("%s%n", out);

Les sorties

Hi L&#226;rry &#38; M&#244;e!
1
Mike

Utilisez JAXP et oubliez le traitement de texte, il sera fait automatiquement pour vous.

0
Fernando Miguélez

Il suffit de remplacer

 & with &amp;

Et pour les autres personnages:

> with &gt;
< with &lt;
\" with &quot;
' with &apos;
0
raman rayat

Essayez de coder le XML à l'aide du sérialiseur XML Apache 

//Serialize DOM
OutputFormat format    = new OutputFormat (doc); 
// as a String
StringWriter stringOut = new StringWriter ();    
XMLSerializer serial   = new XMLSerializer (stringOut, 
                                          format);
serial.serialize(doc);
// Display the XML
System.out.println(stringOut.toString());
0
K Victor Rajan

Vous pouvez utiliser la bibliothèque ESAPI (Enterprise Security API) , qui fournit des méthodes telles que encodeForXML et encodeForXMLAttribute. Consultez la documentation de l'interface Encoder ; il contient également des exemples sur la création d'une instance de DefaultEncoder .

0
Vivit