it-swarm.dev

Nejlepší způsob, jak kódovat textová data pro XML v jazyce Java?

Velmi podobná této otázce , s výjimkou Java.

Jaký je doporučený způsob kódování řetězců pro výstup XML v jazyce Java. Řetězce mohou obsahovat znaky jako „&“, „<“ atd.

82
Epaga

Velmi jednoduše: použijte knihovnu XML. Tak to bude vlastně right namísto vyžadování detailní znalosti bitů XML spec.

39
Jon Skeet

Jak uvedli jiní, použití knihovny XML je nejjednodušší způsob. Pokud chcete uniknout, můžete se podívat do StringEscapeUtils z Knihovny Apache Commons Lang .

113
Fabian Steeg

Stačí použít.

<![CDATA[ your text here ]]>

To umožní všechny znaky kromě konce 

]]>

Takže můžete zahrnout znaky, které by byly nezákonné, například & a>. Například.

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

Atributy však bude nutné uniknout, protože pro ně nelze použít bloky CDATA.

18
ng.

Zkuste to:

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

To mi velmi pomohlo, když jsem poskytl escaped verzi textového řetězce:

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();
}

}

StringEscapeUtils.escapeXml() neunikne řídicím znakům (<0x20). XML 1.1 umožňuje ovládání znaků; XML 1.0 ne. Například XStream.toXML() šťastně serializuje řídicí znaky jazyka Java do XML, což bude analyzátor XML 1.0 odmítat.

Chcete-li uniknout kontrolním znakům pomocí Apache commons-lang, použijte

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

Tato otázka je stará osm let a stále není úplně správná odpověď! Ne, neměli byste importovat celé rozhraní API třetí strany, abyste mohli tento jednoduchý úkol provést. Špatná rada.

Následující metoda bude:

  • správně zpracovávat znaky mimo základní vícejazyčnou rovinu
  • v XML
  • uniknout žádné non-ASCII znaky, což je volitelné, ale běžné
  • nahradit nelegální znaky v XML 1.0 substitučním znakem Unicode. Není zde nejlepší volba - jejich odstranění je stejně platné.

Snažil jsem se optimalizovat pro nejběžnější případ a přitom zajistit, že byste mohli přes toto rozhraní načíst/dev/random a získat platný řetězec v 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: pro ty, kteří nadále trvají na tom, že je pošetilé psát vlastní kód pro tento případ, když existují naprosto dobré Java API pro práci s XML, možná budete chtít vědět, že StAX API, které je součástí Oracle Java 8 (nemám testovaní jiní) nezakódují obsah CDATA správně: neunikne]]> sekvence v obsahu. Knihovna třetích stran, dokonce i ta, která je součástí jádra Java, není vždy tou nejlepší volbou.

6
Mike B

Zatímco idealismus říká, že používá knihovnu XML, IMHO, pokud máte základní představu o XML, pak zdravý rozum a výkon říká, že šablona je celá. Je to pravděpodobně i čitelnější. Ačkoli používání unikající rutiny knihovny je pravděpodobně dobrý nápad.

Zvažte toto: XML bylo mělo být napsáno lidmi.

Použijte knihovny pro generování XML, když váš XML jako "objekt" lépe modeluje váš problém. Pokud se například zapojitelné moduly účastní procesu vytváření tohoto XML.

Edit: pokud jde o to, jak skutečně uniknout XML v šablonách, použití CDATA nebo escapeXml(string) z JSTL jsou dvě dobrá řešení, escapeXml(string) lze použít takto:

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

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

Chování StringEscapeUtils.escapeXml () se změnilo z Commons Lang 2.5 na 3.0. Nyní již uniká znaky Unicode větší než 0x7f.

To je dobrá věc, stará metoda měla být trochu dychtivá uniknout entitám, které by mohly být vloženy do dokumentu utf8.

Nové escapers, které mají být zahrnuty do Google Guava 11.0, také vypadají slibně: http://code.google.com/p/guava-libraries/issues/detail?id=799

6
Jasper Krijgsman

Poznámka: Vaše otázka je o escaping, ne encoding. Unikání je pomocí <, atd., Aby analyzátor rozlišil mezi "toto je XML příkaz" a "toto je nějaký text". Kódování je to, co zadáte v hlavičce XML (UTF-8, ISO-8859-1 atd.).

Za prvé, stejně jako všichni ostatní, používejte knihovnu XML. XML vypadá jednoduše, ale kódování + unikající věci jsou tmavé voodoo (které si všimnete, jakmile narazíte na přehlásky a japonské a jiné podivné věci jako " číslice plné šířky " (& # FF11; je 1)). Vedení XML lidsky čitelného je úkol Sisyphus.

Navrhuji, abych se nikdy nepokoušel být chytrý, pokud jde o kódování textu a unikání v XML. Ale nenechte se zastavit, abyste se pokusili; Jen si pamatujte, kdy vás kousne (a bude).

To znamená, že pokud používáte pouze UTF-8, aby se věci lépe čitelné, můžete zvážit tuto strategii:

  • Pokud text obsahuje znaky „<“, „>“ nebo „&“, zabalte je do souboru <![CDATA[ ... ]]>
  • Pokud text neobsahuje tyto tři znaky, nezkřivte jej.

Používám to v editoru SQL a umožňuje vývojářům vyjmout a vložit SQL z SQL nástroje třetích stran do XML bez obav z úniku. To funguje, protože SQL nemůže obsahovat přehlásky v našem případě, takže jsem v bezpečí.

5
Aaron Digulla

Pro ty, kteří hledají řešení pro nejrychlejší zápis: použijte metody z Apache commons-lang :

Nezapomeňte zahrnout závislost:

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

I když v zásadě souhlasím s Jonem Skeetem, někdy nemám možnost používat externí XML knihovnu. A považuji za zvláštní, že tyto dvě funkce uniknout/unescape jednoduchou hodnotu (atribut nebo tag, ne úplný dokument) nejsou k dispozici ve standardních knihovnách XML součástí Java.

V důsledku toho a na základě různých odpovědí, které jsem viděl (a) zde a jinde, je zde řešení, které jsem vytvořil (nic nefungovalo jako jednoduchá kopie/vložení):

  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;
  }

Výše uvedené obsahuje několik různých věcí:

  1. vyhýbá se používání logiky založené na char, dokud to absolutně nemá - zlepšuje kompatibilitu unicode
  2. pokusy být co nejefektivnější vzhledem k pravděpodobnosti je druhá "pokud" podmínka je pravděpodobně nejpoužívanější cestou
  3. je čistá funkce; tj. je bezpečný pro vlákna
  4. pěkně optimalizuje sběrač garbage pouze tím, že vrátí obsah StringBuilderu, pokud se něco skutečně změnilo - jinak se původní řetězec vrátí

V určitém okamžiku napíšu inverzi této funkce toUnescaped (). Nemám čas to udělat dnes. Když to udělám, přijdu aktualizovat tuto odpověď kódem. :)

4
chaotic3quilibrium

Chcete-li uniknout znakům XML, nejjednodušší je použít projekt Apache Commons Lang, JAR ke stažení z: http://commons.Apache.org/lang/

Třída je tato: org.Apache.commons.lang3.StringEscapeUtils;

Má metodu nazvanou "escapeXml", která vrátí řádně uniklý řetězec. 

3
Greg Burdett

Zde je snadné řešení a je to skvělé i pro kódování znaků s diakritikou!

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);

Výstupy

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

Použít JAXP a zapomenout na zpracování textu, které bude pro vás provedeno automaticky.

0

Pokuste se kódovat XML pomocí serializátoru Apache XML 

//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

Pokud hledáte knihovnu, abyste mohli provést úlohu, zkuste:

  1. Guava 26.0 zdokumentováno zde

    return XmlEscapers.xmlContentEscaper().escape(text);

    Poznámka: Existuje také xmlAttributeEscaper()

  2. Apache Commons Text 1.4 zdokumentováno zde

    StringEscapeUtils.escapeXml11(text)

    Poznámka: Existuje také metoda escapeXml10()

0
jschnasse

Můžete použít knihovnu Enterprise Security API (ESAPI) , která poskytuje metody jako encodeForXML a encodeForXMLAttribute. Prohlédněte si dokumentaci rozhraní Encoder interface; obsahuje také příklady, jak vytvořit instanci DefaultEncoder .

0
Vivit