当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《在Java中编码XML文本数据的最佳方法?》经验,为你挑选了13个好方法。

除了Java之外,与此问题非常相似.

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



1> Fabian Steeg..:

正如其他人所提到的,使用XML库是最简单的方法.如果你想逃避自己,你可以StringEscapeUtils从Apache Commons Lang库中查看.



2> Jon Skeet..:

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


你能推荐这样的图书馆吗?(令我惊讶的是,这不是Java版本5的标准部分......这是一项常见的任务).
XML*是标准Java框架的一部分 - 请查看org.w3c.sax和org.w3c.dom.但是,还有一些更易于使用的框架,例如JDom.请注意,可能没有"用于XML输出的编码字符串"方法 - 我更多地建议整个XML任务应该使用库来完成,而不是仅使用字符串操作一次执行.
@mice:问题是标记为Java,Java有*很多*XML库.事实上,有一些XML API被烘焙到Java中,因此没有必要添加*其他*其他...但即使你这样做,现在几百K在移动设备之外很少出现问题.即使它不是Java,我也非常警惕在没有任何XML API的平台上进行开发......

3> ng...:

只是用.


这将允许除结尾之外的任何字符

]]>

因此,您可以包含非法的字符,例如&和>.例如.

 are allowed ]]>

但是,由于CDATA块不能用于它们,因此需要转义属性.


如果你在CDATA元素中包装文本,则必须转义CDATA结束标记:"]]>"...除非你无法逃脱.因此,您必须将代码分成多个部分,其中一半数据放在一个CDATA元素中,另一半放在一秒中:> "这就是为什么它必须分开.]]> ...最后,只需要逃避'<','>'和'&'可能要简单得多.当然,许多应用程序忽略了数据中CDATA结束标记的潜在问题.我想,无知是幸福.:)
在大多数情况下,这不是你应该做的.太多人滥用CDATA标签.CDATA的目的是告诉处理器不要将其作为XML处理并将其传递给它.如果您正在尝试创建XML文件,那么您应该创建XML,而不仅仅是通过某个包装元素传递字节.
@StijndeWitt绝对正确.CDATA不是逃避特殊角色的灵丹妙药.
@Mads,使用CDATA会生成一个有效的XML文件,因此它就像"正确的方式"一样好.如果你不喜欢它,那么之后解析它,识别它并打印它.

4> Pointer Null..:

试试这个:

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


你至少有两个我能看到的bug.一个是微妙的,另一个则不是.我不会有这样的错误 - 因为我不会在第一时间重新发明轮子.
我对最终版本很满意.Java SE紧凑,快速,高效.在我的书中,做正在做的事情而不是下载另外100 MB的英国媒体报道软件总是更好.

5> Thorbjørn Ra..:

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

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

}



6> Mike B..:

这个问题已经八年了,仍然不是一个完全正确的答案!不,您不必导入整个第三方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("&#x");
            sb.append(Integer.toHexString(c));
            sb.append(';');
        }
    }
    return sb.toString();
}

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



7> 小智..:

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



8> Amr Mostafa..:

虽然理想主义说使用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)}



9> Jasper Krijg..:

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



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


链接`replaceAll`调用是非常低效的,特别是对于大字符串.每次调用都会导致创建一个新的String对象,该对象将一直存在,直到收集垃圾为止.此外,每次调用都需要再次循环遍历字符串.这可以合并到一个单独的手动循环中,并在每次迭代中与每个目标char进行比较.

11> Aaron Digull..:

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

首先,像其他人一样,使用XML库.XML看起来很简单,但编码+转义的东西是黑暗伏都教(一旦你遇到变音符号和日语以及其他奇怪的东西,如" 全宽数字 "(&#FF11;是1),你会注意到它.保持XML人类可读是Sisyphus的任务.

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

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

如果文本包含"<",">"或"&",请将其换行

如果文本不包含这三个字符,请不要扭曲它.

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



12> chaotic3quil..:

尽管我在原则上同意Jon Skeet的观点,但有时我没有选择使用外部XML库的选择。而且我发现Java附带的标准XML库中没有提供两个功能来对一个简单值(属性或标记,不是完整的文档)进行转义/转义。

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

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

上面包含了几种不同的内容:

    除非绝对必要,否则避免使用基于char的逻辑-改善unicode兼容性

    假设概率是第二个“如果”条件可能是最常用的途径,则尝试尽可能地提高效率

    是一个纯函数;即是线程安全的

    如果实际更改了某些内容,则仅通过返回StringBuilder的内容来对垃圾回收器进行优化,否则将返回原始字符串

在某个时候,我将编写此函数的反函数toUnescaped()。我只是今天没有时间这样做。完成后,我将使用代码更新此答案。:)



13> Dariusz..:

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

StringEscapeUtils.escapeXml10() 对于xml 1.0

StringEscapeUtils.escapeXml11() 对于xml 1.1

StringEscapeUtils.escapeXml() 现在已弃用,但过去经常使用

记住要包括依赖性:


  org.apache.commons
  commons-lang3
  3.5 

推荐阅读
爱唱歌的郭少文_
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有