Melhor maneira de codificar dados de texto para XML em Java?

Muito semelhante a esta questão , exceto para Java.

Qual é a maneira recomendada de codificar strings para uma saída XML em Java. As strings podem conter caracteres como “&”, “<" etc.

Muito simplesmente: use uma biblioteca XML. Dessa forma, estará correto em vez de exigir conhecimento detalhado de bits da especificação XML.

Como outros já mencionaram, usar uma biblioteca XML é a maneira mais fácil. Se você quiser escaping de si mesmo, você pode procurar em StringEscapeUtils partir da biblioteca Apache Commons Lang .

Apenas use.

 < ![CDATA[ your text here ]]> 

Isso permitirá que qualquer caractere, exceto o final

 ]]> 

Então você pode include caracteres que seriam ilegais como & e>. Por exemplo.

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

No entanto, os atributos precisarão ser escapados, pois os blocos CDATA não podem ser usados ​​para eles.

Isso funcionou bem para eu fornecer uma versão com escape de uma cadeia de texto:

 public class XMLHelper { /** * Returns the string where all non-ascii and < , &, > are encoded as numeric entities. Ie "<A & B >" * .... (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(); } } 

Tente isto:

 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("<"); break; case '>': sb.append(">"); break; case '\"': sb.append("""); break; case '&': sb.append("&"); break; case '\'': sb.append("'"); break; default: if(c>0x7e) { sb.append("&#"+((int)c)+";"); }else sb.append(c); } } return sb.toString(); } 

StringEscapeUtils.escapeXml() não foge de caracteres de controle (<0x20). XML 1.1 permite caracteres de controle; XML 1.0 não. Por exemplo, XStream.toXML() alegremente serializará os caracteres de controle de um object Java em XML, que um analisador XML 1.0 rejeitará.

Para escaping de caracteres de controle com o commons-lang do Apache, use

 NumericEntityEscaper.below(0x20).translate(StringEscapeUtils.escapeXml(str)) 

Enquanto idealismo diz usar uma biblioteca XML, IMHO se você tem uma idéia básica de XML, em seguida, bom senso e desempenho diz modelo todo o caminho. É indiscutivelmente mais legível também. Embora usar as rotinas de escape de uma biblioteca seja provavelmente uma boa ideia.

Considere isto: XML foi feito para ser escrito por humanos.

Use bibliotecas para gerar XML ao ter seu XML como um “object” melhor modelando seu problema. Por exemplo, se os módulos conectáveis ​​participarem do processo de construção desse XML.

Edit: para saber como realmente escaping XML em templates, uso de CDATA ou escapeXml(string) do JSTL são duas boas soluções, escapeXml(string) pode ser usado assim:

 < %@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> ${fn:escapeXml(value)} 

O comportamento de StringEscapeUtils.escapeXml () foi alterado do Commons Lang 2.5 para 3.0. Agora não mais escapa caracteres Unicode maiores que 0x7f.

Isso é uma coisa boa, o método antigo era ser um pouco ansioso para escaping de entidades que poderiam ser inseridas em um documento utf8.

Os novos evasores a serem incluídos no Google Guava 11.0 também parecem promissores: http://code.google.com/p/guava-libraries/issues/detail?id=799

Nota: Sua pergunta é sobre como escaping , não codificar . Escaping está usando < , etc. para permitir que o analisador distinga entre "isto é um comando XML" e "isto é algum texto". Codificação é o material que você especifica no cabeçalho XML (UTF-8, ISO-8859-1, etc).

Primeiro de tudo, como todo mundo disse, use uma biblioteca XML. O XML parece simples, mas o encoding + escape é um voodoo escuro (que você notará assim que encontrar trema e japonês e outras coisas estranhas como ” dígitos de largura total ” (& # FF11; é 1)). Manter XML legível é uma tarefa de Sísifo.

Eu sugiro nunca tentar ser inteligente sobre codificação de texto e escaping em XML. Mas não deixe que isso te impeça de tentar; apenas lembre quando ele te morde (e vai).

Dito isso, se você usar apenas UTF-8, para tornar as coisas mais legíveis, considere essa estratégia:

  • Se o texto contiver ‘< ', '>‘ ou ‘&’, coloque-o em < ![CDATA[ ... ]]>
  • Se o texto não contiver esses três caracteres, não o deforme.

Eu estou usando isso em um editor de SQL e permite que os desenvolvedores copiem e colem o SQL de uma ferramenta SQL de terceiros para o XML sem se preocupar em escaping. Isso funciona porque o SQL não pode conter umlauts no nosso caso, então estou seguro.

 public String escapeXml(String s) { return s.replaceAll("&", "&").replaceAll(">", ">").replaceAll("< ", "<").replaceAll("\"", """).replaceAll("'", "'"); } 

Embora eu concorde com Jon Skeet em princípio, às vezes não tenho a opção de usar uma biblioteca XML externa. E acho peculiar as duas funções para escaping / unescape um valor simples (atributo ou tag, não documento completo) não estão disponíveis nas bibliotecas XML padrão incluídas no Java.

Como resultado e com base nas diferentes respostas que vi postadas aqui e em outros lugares, aqui está a solução que acabei criando (nada funcionou como um simples copiar / colar):

  public final static String ESCAPE_CHARS = "<>&\"\'"; public final static List ESCAPE_STRINGS = Collections.unmodifiableList(Arrays.asList(new String[] { "<" , ">" , "&" , """ , "'" })); 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; } 

O acima acomoda várias coisas diferentes:

  1. evita usar lógica baseada em caracteres até que seja absolutamente necessário - melhora a compatibilidade de unicode
  2. tenta ser tão eficiente quanto possível, dada a probabilidade é a segunda "se" condição é provavelmente a via mais utilizada
  3. é uma function pura; ou seja, é thread-safe
  4. otimiza muito bem com o coletor de lixo, retornando apenas o conteúdo do StringBuilder se algo realmente mudou - caso contrário, a string original é retornada

Em algum momento, vou escrever a inversão dessa function, toUnescaped (). Eu simplesmente não tenho tempo para fazer isso hoje. Quando o fizer, vou atualizar esta resposta com o código. 🙂

Para escaping de caracteres XML, a maneira mais fácil é usar o projeto Apache Commons Lang, o JAR pode ser baixado em: http://commons.apache.org/lang/

A class é esta: org.apache.commons.lang3.StringEscapeUtils;

Tem um método chamado “escapeXml”, que retornará uma String apropriadamente escapada.

Para aqueles que procuram a solução mais rápida para escrever: use methods do apache commons-lang :

  • StringEscapeUtils.escapeXml10() para xml 1.0
  • StringEscapeUtils.escapeXml11() para xml 1.1
  • StringEscapeUtils.escapeXml() agora está obsoleto, mas foi usado comumente no passado

Lembre-se de include dependência:

  org.apache.commons commons-lang3 3.5   

Esta questão tem oito anos e ainda não é uma resposta totalmente correta! Não, você não deve precisar importar uma API de terceiros inteira para realizar essa tarefa simples. Mau conselho.

O seguinte método irá:

  • lidar corretamente com caracteres fora do plano básico multilingue
  • caracteres de escape requeridos em XML
  • escape de qualquer caractere não-ASCII, que é opcional, mas comum
  • replace caracteres inválidos no XML 1.0 com o caractere de substituição Unicode. Não há melhor opção aqui – removê-los é tão válido.

Eu tentei otimizar para o caso mais comum, enquanto ainda garantindo que você poderia canalizar / dev / random através disso e obter uma string válida em XML.

 public static String encodeXML(CharSequence s) { StringBuilder sb = new StringBuilder(); int len = s.length(); for (int i=0;i= 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("�"); // Unicode replacement character } else { switch(c) { case '&': sb.append("&"); break; case '>': sb.append(">"); break; case '< ': sb.append("<"); break; // Uncomment next two if encoding for an XML attribute // case '\'' sb.append("'"); break; // case '\"' sb.append("""); 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("�"); // Unicode replacement character } else { sb.append("&#x"); sb.append(Integer.toHexString(c)); sb.append(';'); } } return sb.toString(); } 

Edit: para aqueles que continuam a insistir que é tolice escrever seu próprio código para isso quando existem APIs Java perfeitamente boas para lidar com XML, você pode gostar de saber que a API StAX incluída no Oracle Java 8 (eu não testei outros ) não codifica corretamente o conteúdo de CDATA: ele não escapa]]> seqüências no conteúdo. Uma biblioteca de terceiros, mesmo que seja parte do núcleo Java, nem sempre é a melhor opção.

Aqui está uma solução fácil e é ótimo para codificar caracteres acentuados também!

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

Saídas

 Hi Lârry & Môe! 

Use o JAXP e esqueça o manuseio de texto que será feito automaticamente para você.

Tente codificar o XML usando o serializador 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()); 

Você pode usar a biblioteca Enterprise Security API (ESAPI) , que fornece methods como encodeForXML e encodeForXMLAttribute . Dê uma olhada na documentação da interface do Encoder ; Ele também contém exemplos de como criar uma instância de DefaultEncoder .

Apenas replace

  & with & 

E para outros personagens:

 > with > < with < \" with " ' with '