除了Java之外,与此问题非常相似.
在Java中为XML输出编码字符串的推荐方法是什么?字符串可能包含"&","<"等字符.
正如其他人所提到的,使用XML库是最简单的方法.如果你想逃避自己,你可以StringEscapeUtils
从Apache Commons Lang库中查看.
非常简单:使用XML库.这样它实际上是正确的,而不需要详细了解XML规范的位.
只是用.
这将允许除结尾之外的任何字符
]]>
因此,您可以包含非法的字符,例如&和>.例如.
are allowed ]]>
但是,由于CDATA块不能用于它们,因此需要转义属性.
试试这个:
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(); }
这对我来说很有效,可以提供文本字符串的转义版本:
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(); } }
这个问题已经八年了,仍然不是一个完全正确的答案!不,您不必导入整个第三方API来执行此简单任务.不好的建议.
以下方法将:
正确处理基本多语言平面之外的字符
XML中所需的转义字符
转义任何非ASCII字符,这是可选的但很常见
使用Unicode替换字符替换XML 1.0中的非法字符.这里没有最好的选择 - 删除它们同样有效.
我已经尝试针对最常见的情况进行优化,同时仍然确保您可以通过此方式管道/ dev/random并获取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; // 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(""); sb.append(Integer.toHexString(c)); sb.append(';'); } } return sb.toString(); }
编辑:对于那些在有完美的Java API来处理XML时继续坚持为此编写自己的代码而愚蠢的人,您可能想知道Oracle Java 8附带的StAX API(我没有测试过其他人) )无法正确编码CDATA内容:它不会逃避]]>内容中的序列.第三方库,即使是Java核心的一部分,并不总是最佳选择.
StringEscapeUtils.escapeXml()
不会转义控制字符(<0x20).XML 1.1允许控制字符; XML 1.0没有.例如,XStream.toXML()
将愉快地将Java对象的控制字符序列化为XML,XML 1.0解析器将拒绝该XML.
要使用Apache commons-lang来转义控制字符,请使用
NumericEntityEscaper.below(0x20).translate(StringEscapeUtils.escapeXml(str))
虽然理想主义说使用XML库,恕我直言,如果你有一个基本的XML概念,那么常识和性能就是一直模仿它.它可以说更具可读性.虽然使用库的转义例程可能是个好主意.
考虑一下:XML 本来是由人类编写的.
将XML作为"对象"更好地模拟您的问题时,使用库来生成XML.例如,如果可插入模块参与构建此XML的过程.
编辑:至于如何在模板中实际转义XML,使用CDATA或escapeXml(string)
从JSTL是两个很好的解决方案,escapeXml(string)
可以像这样使用:
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>- ${fn:escapeXml(value)}
StringEscapeUtils.escapeXml()的行为已从Commons Lang 2.5更改为3.0.它现在不再转义大于0x7f的Unicode字符.
这是一件好事,旧方法有点急于逃避可以插入到utf8文档中的实体.
包含在Google Guava 11.0中的新的转发器似乎也很有希望:http://code.google.com/p/guava-libraries/issues/detail? id = 799
public String escapeXml(String s) { return s.replaceAll("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'"); }
注意:您的问题是关于转义,而不是编码.转义是使用<等来允许解析器区分"这是一个XML命令"和"这是一些文本".编码是您在XML标头(UTF-8,ISO-8859-1等)中指定的内容.
首先,像其他人一样,使用XML库.XML看起来很简单,但编码+转义的东西是黑暗伏都教(一旦你遇到变音符号和日语以及其他奇怪的东西,如" 全宽数字 "(FF11;是1),你会注意到它.保持XML人类可读是Sisyphus的任务.
我建议永远不要试图聪明地在XML中进行文本编码和转义.但是,不要让那阻止你尝试; 只记得它咬你的时候(它会).
也就是说,如果你只使用UTF-8,为了让事情更具可读性,你可以考虑这个策略:
如果文本包含"<",">"或"&",请将其换行
如果文本不包含这三个字符,请不要扭曲它.
我在SQL编辑器中使用它,它允许开发人员将SQL从第三方SQL工具剪切并粘贴到XML中,而不必担心转义.这是有效的,因为在我们的情况下SQL不能包含元音变音,所以我很安全.
尽管我在原则上同意Jon Skeet的观点,但有时我没有选择使用外部XML库的选择。而且我发现Java附带的标准XML库中没有提供两个功能来对一个简单值(属性或标记,不是完整的文档)进行转义/转义。
结果,基于我在这里和其他地方看到的不同答案,这是我最终创建的解决方案(没有任何方法可以用作简单的复制/粘贴):
public final static String ESCAPE_CHARS = "<>&\"\'"; public final static ListESCAPE_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; }
上面包含了几种不同的内容:
除非绝对必要,否则避免使用基于char的逻辑-改善unicode兼容性
假设概率是第二个“如果”条件可能是最常用的途径,则尝试尽可能地提高效率
是一个纯函数;即是线程安全的
如果实际更改了某些内容,则仅通过返回StringBuilder的内容来对垃圾回收器进行优化,否则将返回原始字符串
在某个时候,我将编写此函数的反函数toUnescaped()。我只是今天没有时间这样做。完成后,我将使用代码更新此答案。:)
对于那些寻求最快编写速度的解决方案的人:使用apache commons-lang中的方法:
StringEscapeUtils.escapeXml10()
对于xml 1.0
StringEscapeUtils.escapeXml11()
对于xml 1.1
StringEscapeUtils.escapeXml()
现在已弃用,但过去经常使用
记住要包括依赖性:
org.apache.commons commons-lang3 3.5