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 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.
Respostas:
Muito simplesmente: use uma biblioteca XML. Dessa forma, será realmente correto em vez de exigir conhecimento detalhado de bits da especificação XML.
Como outros mencionaram, usar uma biblioteca XML é a maneira mais fácil. Se você quiser escapar-se, você pode olhar para StringEscapeUtils
a partir do Apache Commons Lang biblioteca.
StringEscapeUtils.escapeXml(str)
de commons-lang
. Eu o uso no aplicativo App Engine - funciona perfeitamente. Aqui está o Java Doc para esta função:
\t
, \n
e \r
.
\t
, \n
ou \r
precisa ser escapado?
Apenas use.
<![CDATA[ your text here ]]>
Isso permitirá qualquer caractere, exceto o final
]]>
Portanto, você pode incluir caracteres que seriam ilegais, como & e>. Por exemplo.
<element><![CDATA[ characters such as & and > are allowed ]]></element>
No entanto, os atributos precisarão ser escapados, pois os blocos CDATA não podem ser usados para eles.
Isso funcionou bem para mim fornecer uma versão com escape de uma string de texto:
public class XMLHelper {
/**
* Returns the string where all non-ascii and <, &, > are encoded as numeric entities. I.e. "<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();
}
}
Experimente 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();
}
t==null
.
Esta pergunta tem oito anos e ainda não é uma resposta totalmente correta! Não, você não deve ter que importar uma API de terceiros inteira para fazer esta tarefa simples. Mau conselho.
O seguinte método irá:
Tentei otimizar para o caso mais comum, enquanto ainda garantindo que você poderia canalizar / dev / random por meio 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<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("�"); // 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;
// Uncomment next three if you prefer, but not required
// case '\n' sb.append(" "); break;
// case '\r' sb.append(" "); break;
// case '\t' 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 há 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 outras ) falha ao codificar o conteúdo CDATA corretamente: não escapa]]> sequências no conteúdo. Uma biblioteca de terceiros, mesmo que seja parte do núcleo do Java, nem sempre é a melhor opção.
StringEscapeUtils.escapeXml()
não escapa caracteres de controle (<0x20). XML 1.1 permite caracteres de controle; XML 1.0 não. Por exemplo, XStream.toXML()
serializará alegremente os caracteres de controle de um objeto Java em XML, que um analisador XML 1.0 rejeitará.
Para escapar de caracteres de controle com Apache commons-lang, use
NumericEntityEscaper.below(0x20).translate(StringEscapeUtils.escapeXml(str))
public String escapeXml(String s) {
return s.replaceAll("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'");
}
replaceAll
chamadas é muito ineficiente, especialmente para strings grandes. Cada chamada resulta na criação de um novo objeto String, que ficará parado até que o lixo seja coletado. Além disso, cada chamada requer um loop pela string novamente. Isso poderia ser consolidado em um único loop manual com comparações contra cada caractere de destino em cada iteração.
Enquanto o idealismo diz para usar uma biblioteca XML, IMHO, se você tem uma ideia básica de XML, o bom senso e o desempenho dizem que é um modelo completo. É indiscutivelmente mais legível também. Embora usar as rotinas de escape de uma biblioteca seja provavelmente uma boa ideia.
Considere o seguinte: XML foi feito para ser escrito por humanos.
Use bibliotecas para gerar XML quando tiver seu XML como um "objeto" modelar melhor seu problema. Por exemplo, se os módulos conectáveis participarem do processo de construção deste XML.
Editar: quanto a como realmente escapar XML em modelos, o uso de CDATA ou escapeXml(string)
de JSTL são duas boas soluções, escapeXml(string)
podem ser usadas assim:
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<item>${fn:escapeXml(value)}</item>
O comportamento de StringEscapeUtils.escapeXml () foi alterado de Commons Lang 2.5 para 3.0. Agora ele não escapa mais caracteres Unicode maiores que 0x7f.
Isso é uma coisa boa, o método antigo era estar um pouco ansioso para escapar de entidades que poderiam apenas ser inseridas em um documento utf8.
Os novos escapers a serem incluídos no Google Guava 11.0 também parecem promissores: http://code.google.com/p/guava-libraries/issues/detail?id=799
Para aqueles que procuram a solução mais rápida de escrever: use os métodos do apache commons-lang :
StringEscapeUtils.escapeXml10()
para xml 1.0StringEscapeUtils.escapeXml11()
para xml 1.1StringEscapeUtils.escapeXml()
agora está obsoleto, mas era comumente usado no passadoLembre-se de incluir dependência:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version> <!--check current version! -->
</dependency>
Observação: sua pergunta é sobre como escapar , não sobre a codificação . Escaping está usando <, etc. para permitir que o analisador distinga entre "este é um comando XML" e "este é um texto". Codificação é o que você especifica no cabeçalho XML (UTF-8, ISO-8859-1, etc).
Em primeiro lugar, como todo mundo disse, use uma biblioteca XML. XML parece simples, mas o material de codificação + escape é vodu escuro (que você notará assim que encontrar tremas e japonês e outras coisas estranhas como " dígitos de largura total " (& # FF11; é 1)). Manter o XML legível por humanos é uma tarefa de Sísifo.
Eu sugiro que nunca tente ser inteligente sobre codificação de texto e escape em XML. Mas não deixe que isso o impeça de tentar; apenas lembre-se de quando ele te morde (e vai).
Dito isso, se você usar apenas UTF-8, para tornar as coisas mais legíveis, você pode considerar esta estratégia:
<![CDATA[ ... ]]>
Estou usando isso em um editor de SQL e permite que os desenvolvedores recortem e colem SQL de uma ferramenta SQL de terceiros no XML sem se preocupar em escapar. Isso funciona porque o SQL não pode conter tremas no nosso caso, então estou seguro.
Embora concorde com Jon Skeet em princípio, às vezes não tenho a opção de usar uma biblioteca XML externa. E acho peculiar que as duas funções para escapar / unescape um valor simples (atributo ou tag, não documento completo) não estão disponíveis nas bibliotecas XML padrão incluídas com 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<String> 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:
Em algum momento, escreverei a inversão dessa função, toUnescaped (). Eu simplesmente não tenho tempo para fazer isso hoje. Quando eu fizer isso, irei atualizar esta resposta com o código. :)
Para escapar de caracteres XML, a maneira mais fácil é usar o projeto Apache Commons Lang, JAR disponível para download em: http://commons.apache.org/lang/
A classe é esta: org.apache.commons.lang3.StringEscapeUtils;
Ele tem um método chamado "escapeXml", que retornará uma String com escape apropriado.
Se você está procurando uma biblioteca para fazer o trabalho, tente:
Guava 26.0 documentado aqui
return XmlEscapers.xmlContentEscaper().escape(text);
Nota: Também existe um
xmlAttributeEscaper()
Apache Commons Text 1.4 documentado aqui
StringEscapeUtils.escapeXml11(text)
Nota: Também existe um
escapeXml10()
método
Esta é uma solução fácil e ótima 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!
Você pode usar a biblioteca Enterprise Security API (ESAPI) , que fornece métodos como encodeForXML
e encodeForXMLAttribute
. Dê uma olhada na documentação da interface do codificador ; ele também contém exemplos de como criar uma instância de DefaultEncoder .
Apenas substitua
& with &
E para outros personagens:
> with >
< with <
\" with "
' with '
Use JAXP e esqueça o tratamento de texto, isso será feito para você automaticamente.
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());
Aqui está o que descobri depois de pesquisar em todos os lugares em busca de uma solução:
Obtenha a biblioteca Jsoup:
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.12.1</version>
</dependency>
Então:
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.Entities
import org.jsoup.parser.Parser
String xml = '''<?xml version = "1.0"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV = "http://www.w3.org/2001/12/soap-envelope"
SOAP-ENV:encodingStyle = "http://www.w3.org/2001/12/soap-encoding">
<SOAP-ENV:Body xmlns:m = "http://www.example.org/quotations">
<m:GetQuotation>
<m:QuotationsName> MiscroSoft@G>>gle.com </m:QuotationsName>
</m:GetQuotation>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>'''
Document doc = Jsoup.parse(new ByteArrayInputStream(xml.getBytes("UTF-8")), "UTF-8", "", Parser.xmlParser())
doc.outputSettings().charset("UTF-8")
doc.outputSettings().escapeMode(Entities.EscapeMode.base)
println doc.toString()
Espero que isso ajude alguém