在Java中编码XML文本数据的最佳方法?

时间:2009-01-13 15:15:31

标签: java xml encoding

this question非常相似,但Java除外。

在Java中为XML输出编码字符串的推荐方法是什么?字符串可能包含“&”,“<”等字符

22 个答案:

答案 0 :(得分:114)

正如其他人所说,使用XML库是最简单的方法。如果您确实想逃避自己,可以从StringEscapeUtils库中查看Apache Commons Lang

答案 1 :(得分:39)

非常简单:使用XML库。这样它实际上将正确,而不需要详细了解XML规范的位。

答案 2 :(得分:18)

只需使用。

<![CDATA[ your text here ]]>

这将允许除结尾

之外的任何字符
]]>

因此,您可以包含非法的字符,例如&amp;和&gt;。例如。

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

但是,由于无法使用CDATA块,因此需要转义属性。

答案 3 :(得分:14)

试试这个:

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

答案 4 :(得分:13)

这对我来说很有效,可以提供文本字符串的转义版本:

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

}

答案 5 :(得分:8)

StringEscapeUtils.escapeXml()不会转义控制字符(&lt; 0x20)。 XML 1.1允许控制字符; XML 1.0没有。例如,XStream.toXML()很乐意将Java对象的控制字符序列化为XML,XML 1.0解析器将拒绝这些字符。

要使用Apache commons-lang转义控制字符,请使用

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

答案 6 :(得分:8)

这个问题已经八年了,仍然不是一个完全正确的答案!不,您不必导入整个第三方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<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();
}

编辑:对于那些在有完美的Java API来处理XML的情况下继续为此编写自己的代码而愚蠢的人,您可能想知道Oracle Java 8附带的StAX API(我没有对其他人进行测试)无法正确编码CDATA内容:它没有逃脱]]&gt;内容中的序列。第三方库,即使是Java核心的一部分,并不总是最佳选择。

答案 7 :(得分:6)

虽然理想主义说使用XML库,恕我直言,如果你对XML有一个基本的想法,那么常识和性能会一直模仿它。它可以说更具可读性。虽然使用库的转义例程可能是一个好主意。

考虑一下:XML 意味着由人类编写。

当您将XML作为“对象”更好地模拟您的问题时,使用库来生成XML。例如,如果可插入模块参与构建此XML的过程。

编辑:关于如何在模板中实际转义XML,使用CDATA或JSTL中的escapeXml(string)是两个很好的解决方案,escapeXml(string)可以像这样使用:

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

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

答案 8 :(得分:6)

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

答案 9 :(得分:6)

public String escapeXml(String s) {
    return s.replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
}

答案 10 :(得分:5)

注意:您的问题是关于转义,而不是编码。转义是使用&lt;等来允许解析器区分“这是XML命令”和“这是一些文本”。编码是您在XML标头中指定的内容(UTF-8,ISO-8859-1等)。

首先,和其他人一样,使用XML库。 XML看起来很简单,但编码+转义的东西是黑暗伏都教(一旦你遇到变音符号和日语以及其他奇怪的东西,如“full width digits”(&amp;#FF11;是1),你就会注意到这一点。保持XML人类可读是西西弗斯的任务。

我建议永远不要试图聪明地在XML中进行文本编码和转义。但是,不要让那阻止你尝试;只记得它什么时候咬你(它会)。

也就是说,如果你只使用UTF-8,为了让事情更具可读性,你可以考虑这个策略:

  • 如果文字确实包含'&lt;','&gt;'或'&amp;',将其包裹在<![CDATA[ ... ]]>
  • 如果文字不包含这三个字符,请不要扭曲它。

我在SQL编辑器中使用它,它允许开发人员将SQL从第三方SQL工具剪切并粘贴到XML中,而不必担心转义。这是有效的,因为在我们的情况下SQL不能包含变音符号,所以我很安全。

答案 11 :(得分:5)

虽然我原则上同意Jon Skeet,但有时我没有选择使用外部XML库。我发现在Java中包含的标准XML库中没有提供两个函数来转义/ unescape一个简单的值(属性或标记,而不是完整文档)。

结果,基于我在这里和其他地方发布的不同答案,这里是我最终创建的解决方案(没有任何工作作为简单的复制/粘贴):

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

以上容纳了几个不同的东西:

  1. 避免使用基于字符的逻辑,直到绝对必须 - 提高unicode兼容性
  2. 尝试尽可能高效,因为概率是第二个“if”条件可能是最常用的途径
  3. 是一个纯粹的功能;即是线程安全的
  4. 通过仅返回StringBuilder的内容(如果实际发生了更改)来优化垃圾收集器 - 否则,返回原始字符串
  5. 在某些时候,我会写这个函数的反转,toUnescaped()。我今天没有时间这样做。当我这样做时,我会用代码更新这个答案。 :)

答案 12 :(得分:4)

对于那些寻求最快写入解决方案的人:使用apache commons-lang中的方法:

请记住要包含依赖项:

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

答案 13 :(得分:3)

要转义XML字符,最简单的方法是使用Apache Commons Lang项目,JAR可从以下网址下载:http://commons.apache.org/lang/

这个类是这样的:org.apache.commons.lang3.StringEscapeUtils;

它有一个名为“escapeXml”的方法,它将返回一个适当的转义字符串。

答案 14 :(得分:1)

这是一个简单的解决方案,它也非常适合编码重音字符!

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

输出

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

答案 15 :(得分:1)

如果您正在寻找图书馆来完成工作,请尝试:

  1. Guava 26.0记录在here

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

      

    注意:还有一个xmlAttributeEscaper()

  2. Apache Commons Text 1.4记录在here

    StringEscapeUtils.escapeXml11(text)

      

    注意:还有一种escapeXml10()方法

答案 16 :(得分:0)

我在这里创建了包装器,希望对您有所帮助,Click here您可以根据自己的要求进行修改

答案 17 :(得分:0)

这是我到处寻找解决方案后发现的:

获取Jsoup库:

Documentation

然后:

Open as popup

希望这对某人有帮助

答案 18 :(得分:0)

您可以使用Enterprise Security API (ESAPI) library,它提供encodeForXMLencodeForXMLAttribute等方法。看一下Encoder界面的文档;它还包含如何创建DefaultEncoder实例的示例。

答案 19 :(得分:0)

尝试使用Apache XML序列化程序对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());

答案 20 :(得分:0)

使用JAXP并忘记文字处理,它将自动为您完成。

答案 21 :(得分:-1)

只需更换

 & with &amp;

对于其他字符:

> with &gt;
< with &lt;
\" with &quot;
' with &apos;