Removendo caracteres XML inválidos de uma string em java
-
26-09-2019 - |
Pergunta
Olá, gostaria de remover todos os caracteres XML inválidos de uma string. Gostaria de usar uma expressão regular com o método String.Place.
Curti
line.replace(regExp,"");
Qual é o regexp certo para usar?
O personagem XML inválido é tudo o que não é isso:
[#x1-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
obrigado.
Solução
O Regex de Java suporta caracteres suplementares, para que você possa especificar esses altos intervalos com dois chars codificados UTF-16.
Aqui está o padrão para remover personagens que são ilegais em XML 1.0:
// XML 1.0
// #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
String xml10pattern = "[^"
+ "\u0009\r\n"
+ "\u0020-\uD7FF"
+ "\uE000-\uFFFD"
+ "\ud800\udc00-\udbff\udfff"
+ "]";
A maioria das pessoas deseja a versão XML 1.0.
Aqui está o padrão para remover personagens que são ilegais em XML 1.1:
// XML 1.1
// [#x1-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
String xml11pattern = "[^"
+ "\u0001-\uD7FF"
+ "\uE000-\uFFFD"
+ "\ud800\udc00-\udbff\udfff"
+ "]+";
Você precisará usar String.replaceAll(...)
e não String.replace(...)
.
String illegal = "Hello, World!\0";
String legal = illegal.replaceAll(pattern, "");
Outras dicas
Devemos considerar personagens substitutos? Caso contrário, '(atual> = 0x10000) && (atual <= 0x10ffff)' nunca será verdadeiro.
Também testou que o caminho Regex parece mais lento que o seguinte loop.
if (null == text || text.isEmpty()) {
return text;
}
final int len = text.length();
char current = 0;
int codePoint = 0;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < len; i++) {
current = text.charAt(i);
boolean surrogate = false;
if (Character.isHighSurrogate(current)
&& i + 1 < len && Character.isLowSurrogate(text.charAt(i + 1))) {
surrogate = true;
codePoint = text.codePointAt(i++);
} else {
codePoint = current;
}
if ((codePoint == 0x9) || (codePoint == 0xA) || (codePoint == 0xD)
|| ((codePoint >= 0x20) && (codePoint <= 0xD7FF))
|| ((codePoint >= 0xE000) && (codePoint <= 0xFFFD))
|| ((codePoint >= 0x10000) && (codePoint <= 0x10FFFF))) {
sb.append(current);
if (surrogate) {
sb.append(text.charAt(i));
}
}
}
Solução de Jun, simplificada. Usando StringBuffer#appendCodePoint(int)
, Eu não preciso char current
ou String#charAt(int)
. Eu posso dizer um par substituto, verificando se codePoint
é melhor que 0xFFFF
.
(Não é necessário fazer o I ++, pois um substituto baixo não passaria pelo filtro. Mas então alguém reutilizaria o código para diferentes pontos de código e falharia. Prefiro a programação para hackers.)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < text.length(); i++) {
int codePoint = text.codePointAt(i);
if (codePoint > 0xFFFF) {
i++;
}
if ((codePoint == 0x9) || (codePoint == 0xA) || (codePoint == 0xD)
|| ((codePoint >= 0x20) && (codePoint <= 0xD7FF))
|| ((codePoint >= 0xE000) && (codePoint <= 0xFFFD))
|| ((codePoint >= 0x10000) && (codePoint <= 0x10FFFF))) {
sb.appendCodePoint(codePoint);
}
}
Todas essas respostas até agora substituem apenas os próprios personagens. Mas às vezes um documento XML tem sequências inválidas de entidade XML, resultando em erros. Por exemplo, se você tem 
Em seu XML, um analisador Java XML jogará Illegal character entity: expansion character (code 0x2 at ...
.
Aqui está um programa Java simples que pode substituir essas seqüências de entidades inválidas.
public final Pattern XML_ENTITY_PATTERN = Pattern.compile("\\&\\#(?:x([0-9a-fA-F]+)|([0-9]+))\\;");
/**
* Remove problematic xml entities from the xml string so that you can parse it with java DOM / SAX libraries.
*/
String getCleanedXml(String xmlString) {
Matcher m = XML_ENTITY_PATTERN.matcher(xmlString);
Set<String> replaceSet = new HashSet<>();
while (m.find()) {
String group = m.group(1);
int val;
if (group != null) {
val = Integer.parseInt(group, 16);
if (isInvalidXmlChar(val)) {
replaceSet.add("&#x" + group + ";");
}
} else if ((group = m.group(2)) != null) {
val = Integer.parseInt(group);
if (isInvalidXmlChar(val)) {
replaceSet.add("&#" + group + ";");
}
}
}
String cleanedXmlString = xmlString;
for (String replacer : replaceSet) {
cleanedXmlString = cleanedXmlString.replaceAll(replacer, "");
}
return cleanedXmlString;
}
private boolean isInvalidXmlChar(int val) {
if (val == 0x9 || val == 0xA || val == 0xD ||
val >= 0x20 && val <= 0xD7FF ||
val >= 0x10000 && val <= 0x10FFFF) {
return false;
}
return true;
}
A partir de Weblog de Mark McLaren
/**
* This method ensures that the output String has only
* valid XML unicode characters as specified by the
* XML 1.0 standard. For reference, please see
* <a href="http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char">the
* standard</a>. This method will return an empty
* String if the input is null or empty.
*
* @param in The String whose non-valid characters we want to remove.
* @return The in String, stripped of non-valid characters.
*/
public static String stripNonValidXMLCharacters(String in) {
StringBuffer out = new StringBuffer(); // Used to hold the output.
char current; // Used to reference the current character.
if (in == null || ("".equals(in))) return ""; // vacancy test.
for (int i = 0; i < in.length(); i++) {
current = in.charAt(i); // NOTE: No IndexOutOfBoundsException caught here; it should not happen.
if ((current == 0x9) ||
(current == 0xA) ||
(current == 0xD) ||
((current >= 0x20) && (current <= 0xD7FF)) ||
((current >= 0xE000) && (current <= 0xFFFD)) ||
((current >= 0x10000) && (current <= 0x10FFFF)))
out.append(current);
}
return out.toString();
}
A partir de Melhor maneira de codificar dados de texto para XML em Java?
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();
}
Se você deseja armazenar elementos de texto com os caracteres proibidos em forma de XML, você pode usar o XPL. O dev -kit fornece processamento XPL para XML e XML simultâneo - o que significa que não há custo de tempo para a tradução de XPL para XML. Ou, se você não precisar de todo o poder do XML (namespaces), basta usar o XPL.
String xmlData = xmlData.codePoints().filter(c -> isValidXMLChar(c)).collect(StringBuilder::new,
StringBuilder::appendCodePoint, StringBuilder::append).toString();
private boolean isValidXMLChar(int c) {
if((c == 0x9) ||
(c == 0xA) ||
(c == 0xD) ||
((c >= 0x20) && (c <= 0xD7FF)) ||
((c >= 0xE000) && (c <= 0xFFFD)) ||
((c >= 0x10000) && (c <= 0x10FFFF)))
{
return true;
}
return false;
}
Eu acredito que os seguintes artigos podem ajudá -lo.
http://commons.apache.org/lang/api-2.1/org/apache/commons/lang/stringescapeutils.htmlhttp://www.javapractices.com/topic/topicaction.do?id=96
Logo, tente usar o Stringescapeutils do projeto Jacarta.