web-dev-qa-db-ja.com

JavaでXMLのテキストデータをエンコードする最良の方法は?

この質問 と非常によく似ていますが、Javaを除きます。

JavaでXML出力の文字列をエンコードする推奨方法は何ですか。文字列には、「&」、「<」などの文字が含まれる場合があります。

86
Epaga

非常に簡単:XMLライブラリを使用します。そうすれば、XML仕様の詳細な知識を必要とする代わりに、実際にはrightになります。

40
Jon Skeet

他の人が述べたように、XMLライブラリを使用するのが最も簡単な方法です。自分でエスケープしたい場合は、 StringEscapeUtilsApache Commons Lang ライブラリから見ることができます。

115
Fabian Steeg

ただ使用します。

<![CDATA[ your text here ]]>

これにより、末尾以外のすべての文字が許可されます

]]>

したがって、&や>などの不正な文字を含めることができます。例えば。

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

ただし、CDATAブロックを使用できないため、属性をエスケープする必要があります。

18
ng.

これを試して:

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

これは、テキスト文字列のエスケープバージョンを提供するのにうまく機能しました。

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

}

この質問は8歳ですが、まだ完全に正しい答えではありません!いいえ、この単純なタスクを実行するためにサードパーティAPI全体をインポートする必要はありません。悪いアドバイス。

次のメソッドは:

  • 基本的な多言語面以外の文字を正しく処理する
  • xMLに必要なエスケープ文字
  • 非ASCII文字をエスケープします。これはオプションですが一般的です
  • xML 1.0のillegal文字をUnicode置換文字に置き換えます。ここには最適なオプションはありません-それらを削除することも同様に有効です。

最も一般的なケース向けに最適化を試みましたが、/ 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();
}

編集:XMLを処理するための完全に優れたJava APIがある場合、このために独自のコードを記述するのは愚かだと主張する人にとって、StAX APIがOracleに含まれていることを知りたいと思うかもしれませんJava 8(他の人はテストしていません)CDATAコンテンツを正しくエンコードできません:コンテンツのシーケンスをエスケープしません]]>サードパーティのライブラリ、Javaコアの一部でも、常に最良のオプションではありません。

10
Mike B

StringEscapeUtils.escapeXml()は、制御文字(<0x20)をエスケープしません。 XML 1.1では、制御文字が許可されています。 XML 1.0はサポートしていません。たとえば、XStream.toXML()はJavaオブジェクトの制御文字をXMLにシリアライズしますが、XML 1.0パーサーはそれを拒否します。

Apache commons-langで制御文字をエスケープするには、使用します

NumericEntityEscaper.below(0x20).translate(StringEscapeUtils.escapeXml(str))
8
Steve Mitchell
public String escapeXml(String s) {
    return s.replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
}
6
iCrazybest

理想主義はXMLライブラリを使用すると言いますが、IMHOがXMLの基本的な考え方を持っているなら、常識とパフォーマンスはそれをすべてテンプレート化すると言います。間違いなく読みやすくなっています。ライブラリのエスケープルーチンを使用することは、おそらく良い考えです。

これを考慮してください:XML だったは人間によって書かれることを意味していました。

「オブジェクト」としてXMLを使用する場合は、ライブラリを使用してXMLを生成し、問題をより適切にモデル化します。たとえば、プラグ可能なモジュールがこのXMLの構築プロセスに参加している場合。

編集:テンプレートで実際にXMLをエスケープする方法については、JSTAからのCDATAまたはescapeXml(string)の使用が2つの優れたソリューションです。escapeXml(string)は次のように使用できます。

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

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

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

6

注:質問はエスケープであり、encodingではありません。エスケープでは、<などを使用して、パーサーが「これはXMLコマンドです」と「これはテキストです」を区別できるようにします。エンコーディングは、XMLヘッダー(UTF-8、ISO-8859-1など)で指定するものです。

まず第一に、他の皆が言ったように、XMLライブラリを使用します。 XMLはシンプルに見えますが、エンコーディング+エスケープ処理は暗いブードゥーです(ウムラウトや日本語、および「 全角数字 」(&#FF11;は1 ))。 XMLを人間が読めるようにすることは、Sisyphusの仕事です。

XMLでのテキストエンコーディングとエスケープについて賢くしようとしないことをお勧めします。しかし、それで試してみることを止めさせないでください。いつ噛むか覚えておいてください(そうするでしょう)。

とはいえ、UTF-8のみを使用する場合は、読みやすくするために次の戦略を検討できます。

  • テキストに「<」、「>」、または「&」が含まれている場合は、<![CDATA[ ... ]]>で囲みます
  • テキストにこれらの3つの文字が含まれていない場合、ワープしないでください。

これをSQLエディターで使用しているため、開発者はエスケープを心配することなく、サードパーティのSQLツールからXMLにSQLをカットアンドペーストできます。私たちの場合、SQLにはウムラウトを含めることができないため、これは機能します。したがって、私は安全です。

5
Aaron Digulla

原則としてJon Skeetに同意しますが、外部XMLライブラリを使用するオプションがない場合があります。また、単純な値(属性またはタグ、完全なドキュメントではない)をエスケープ/エスケープする2つの関数は、Javaに含まれている標準のXMLライブラリでは使用できないという特徴があります。

結果として、ここや他の場所で投稿したさまざまな回答に基づいて、最終的に作成したソリューションがあります(単純なコピー/貼り付けとして機能するものはありません):

  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. 絶対に必要になるまでcharベースのロジックを使用しない-ユニコードの互換性を改善する
  2. 確率が2番目の「if」条件である可能性が最も高いと考えられるため、可能な限り効率的であることを試みる
  3. 純粋な関数です。つまり、スレッドセーフです
  4. 何かが実際に変更された場合にのみStringBuilderのコンテンツを返すことにより、ガベージコレクターで適切に最適化されます-そうでなければ、元の文字列が返されます

ある時点で、この関数の反転toUnescaped()を作成します。今日はそれをする時間がありません。その場合、この回答をコードで更新します。 :)

5

最速の書き込みソリューションをお探しの場合: Apache commons-lang のメソッドを使用してください:

依存関係を含めることを忘れないでください:

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

XML文字をエスケープする最も簡単な方法は、Apache Commons Langプロジェクトを使用することです。JARは次の場所からダウンロードできます。 http://commons.Apache.org/lang/

クラスはこれです:org.Apache.commons.lang3.StringEscapeUtils;

「escapeXml」という名前のメソッドがあり、適切にエスケープされた文字列を返します。

3
Greg Burdett

これは簡単な解決策であり、アクセント付き文字のエンコードにも最適です!

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!
1
Mike

あなたが仕事を成し遂げるために図書館を探しているなら、試してください:

  1. グアバ26. 文書化 ここ

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

    注:xmlAttributeEscaper()もあります

  2. Apache Commons Text 1.4 文書化 ここ

    StringEscapeUtils.escapeXml11(text)

    注:escapeXml10()メソッドもあります

1
jschnasse

JAXP を使用し、自動的に行われるテキスト処理を忘れます。

0

交換するだけ

 & with &amp;

他のキャラクターの場合:

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

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());
0
K Victor Rajan

Enterprise Security API(ESAPI)ライブラリ を使用できます。これは、encodeForXMLencodeForXMLAttributeなどのメソッドを提供します。 Encoder インターフェイスのドキュメントをご覧ください。また、 DefaultEncoder のインスタンスを作成する方法の例も含まれています。

0
Vivit