JSONで文字列をエスケープするにはどうすればよいですか?


154

JSONデータを手動で作成する場合、文字列フィールドをエスケープするにはどうすればよいですか?万一Iの使用はApache CommonsのラングのようなものStringEscapeUtilities.escapeHtmlStringEscapeUtilities.escapeXmlあるいは私が使うべきjava.net.URLEncoder

問題は、を使用するSEU.escapeHtmlと引用符がエスケープされず、文字列全体をのペアでラップする'と、不正な形式のJSONが生成されることです。


20
文字列全体をのペアでラップしている場合'、最初から失敗します。JSON文字列はでのみ囲まれ"ます。ietf.org/rfc/rfc4627.txtを参照してください。
タナトス

2
StringEscapeUtilitiesアウトラインの+1 。かなり便利です。
Muhammad Gelbana

回答:


157

理想的には、適切なデータ構造をフィードできる言語のJSONライブラリを見つけ、それをエスケープする方法を心配させます。それはあなたをより正気に保つでしょう。なんらかの理由で自分の言語のライブラリがない場合、そのライブラリを使用したくない場合(私はこれをお勧めしません¹)、またはJSONライブラリを作成している場合は、以下をお読みください。

RFCに従ってエスケープします。あなたが唯一の文字:JSONはかなりリベラルである必要がありますエスケープがある\"と、制御コード(以下U + 0020以上のもの)。

このエスケープの構造はJSONに固有です。JSON固有の関数が必要です。エスケープの全ては以下のように書くことができる\uXXXX場所をXXXXその文字のUTF-16コードunit¹です。など、いくつかのショートカット\\も機能します。(そして、それらはより小さく、より明確な出力をもたらします。)

詳細については、RFCを参照してください。

¹JSONのエスケープは、それが使用する、JS上に構築されている\uXXXX場合は、XXXXUTF-16コード単位です。BMP外のコードポイントの場合、これはサロゲートペアをエンコードすることを意味します。(または、JSONのエンコードはUnicodeテキストであり、これらの特定の文字を許可するため、文字を直接出力することもできます。)


JavaScriptのように、JSONで文字列を二重引用符または単一引用符で囲むことは有効ですか?または、それらを二重引用符で囲むことだけが有効ですか?
Behrang Saeedzadeh 2010年

14
二重引用符(")のみ。
タナトス

3
@セルゲイ:文字{[]}:?は単一のバックスラッシュでエスケープしてはいけません。(\:たとえば、JSON文字列では無効です。)これらはすべて、オプションで\uXXXX構文を使用してエスケープすることができますが、数バイトを浪費します。RFCの§2.5を参照してください。
タナトス

2
それがどれほど広くサポートされているかはわかりませんが、私の経験でJSON.stringify()は、その仕事をするようにという電話がありました。
LS

2
@BitTicklerユニコード文字はまったく曖昧ではありません-それは単に、ユニコード仕様にコードポイントが含まれていることを意味します。std :: stringを使用する場合、これはUnicode文字の集まりです。あなたがそれをシリアル化する必要があるとき、それをファイルまたはネットワーク全体に言いましょう、それが「どのエンコーディング」が入ってくるかです。タナトスによれば、彼らはあなたにUTFの使用を望んでいるようですが、技術的にはあらゆるエンコーディングを使用できますユニコード文字に再構成できます。
Gerard ONeill 2017

54

以下からの抜粋投棄

 public static String quote(String string) {
         if (string == null || string.length() == 0) {
             return "\"\"";
         }

         char         c = 0;
         int          i;
         int          len = string.length();
         StringBuilder sb = new StringBuilder(len + 4);
         String       t;

         sb.append('"');
         for (i = 0; i < len; i += 1) {
             c = string.charAt(i);
             switch (c) {
             case '\\':
             case '"':
                 sb.append('\\');
                 sb.append(c);
                 break;
             case '/':
 //                if (b == '<') {
                     sb.append('\\');
 //                }
                 sb.append(c);
                 break;
             case '\b':
                 sb.append("\\b");
                 break;
             case '\t':
                 sb.append("\\t");
                 break;
             case '\n':
                 sb.append("\\n");
                 break;
             case '\f':
                 sb.append("\\f");
                 break;
             case '\r':
                sb.append("\\r");
                break;
             default:
                 if (c < ' ') {
                     t = "000" + Integer.toHexString(c);
                     sb.append("\\u" + t.substring(t.length() - 4));
                 } else {
                     sb.append(c);
                 }
             }
         }
         sb.append('"');
         return sb.toString();
     }

10
まあ、これはOPタグだった
MonoThreaded

c <''のときだけ理解しないで、\ uに変更します。私の場合は、そこに55357オーバーで文字\ uD38Dは、「」ですので、... U \に変更されません
ストーニー

1
@Stonyは新しい質問のように聞こえる
MonoThreaded

@MonoThreaded返信ありがとうございます。理由はまだわかりません。しかし最後に、(c <'' || c> 0x7f){t = "000" + Integer.toHexString(c).toUpperCase();の場合、以下のように修正するようにメソッドを変更しました。sb.append( "\\ u" + t.substring(t.length()-4)); } else {sb.append(c); }}
Stony

1
@Stony、すべて以外の文字"\ 、および制御文字(「」前とは)長い出力が一致をコードとして有効な内部のJSON文字列です。つまり、\uD38DUTFエンコードが保持されている限り、「펍」をエンコードする必要はありません。
meustrus

37

これを試してくださいorg.codehaus.jettison.json.JSONObject.quote("your string")

こちらからダウンロードしてください:http : //mvnrepository.com/artifact/org.codehaus.jettison/jettison


間違いなく最高のソリューションです!Thx
Lastnico

しかし、これは[{
Sergei

1
@Sergei JSON文字列内で中括弧をエスケープする必要はありません。
Yobert

これが実際に何を返すかを示すのに役立つかもしれません。
Trevor

2
org.json.JSONObject.quote( "your json string")も問題なく機能します
webjockey

23

org.json.simple.JSONObject.escape()は、引用符、\、/、\ r、\ n、\ b、\ f、\ tおよびその他の制御文字をエスケープします。JavaScriptコードをエスケープするために使用できます。

import org.json.simple.JSONObject;
String test =  JSONObject.escape("your string");

3
それは使用しているjsonライブラリ(JSONObject.escape、JSONObject.quote、..)に依存しますが、それは常に引用ジョブを実行する静的メソッドであり、単純に再利用する必要があります
アミン

どのライブラリがorg.jsonの一部ですか?クラスパスにはありません。
Alex Spurling


22

Apache commons langがこれをサポートするようになりました。クラスパスに最新バージョンのApache commons langがあることを確認してください。バージョン3.2以降が必要です

バージョン3.2のリリースノート

LANG-797:StringEscapeUtilsにescape / unescapeJsonを追加しました。


これが私にとって最も現実的な答えです。ほとんどのプロジェクトはすでにapache commons langを使用しているため、1つの関数に依存関係を追加する必要はありません。JSONビルダーがおそらく最良の答えです。
鍛冶屋2017年

補足として、コメントを編集する方法がわからないため、新しいコメントを追加したため、javax.json.JsonObjectBuilderとjavax.json.JsonWriterが見つかりました。非常に素晴らしいビルダー/ライターの組み合わせ。
鍛冶屋2017年

1
これはapache commons langでは非推奨です。apachecommons textを使用する必要があります。残念ながら、このライブラリは/文字をエスケープすることにより、オプション/古い仕様に従います。これにより、URLが含まれるJSONを含む多くの問題が解決されます。元の提案は/エスケープするための特別なチャーとしてありましたが、執筆時の最新の仕様で
当てはまりません

10

org.json.JSONObject quote(String data) メソッドは仕事をします

import org.json.JSONObject;
String jsonEncodedString = JSONObject.quote(data);

ドキュメントから抜粋:

データをJSON文字列としてエンコードします。これは、引用符と必要な文字のエスケープに適用されます。[...] nullは空の文字列として解釈されます


1
org.apache.sling.commons.json.JSONObjectまた、これと同じものを持っている
ヨルダンShurmer

5

StringEscapeUtils.escapeJavaScript/ StringEscapeUtils.escapeEcmaScriptもトリックを行う必要があります。


10
escapeJavaScript単一引用符をとしてエスケープしますが\'、これは誤りです。
2014年

4

fastexml jacksonを使用している場合は、以下を使用できます。 com.fasterxml.jackson.core.io.JsonStringEncoder.getInstance().quoteAsString(input)

codehaus jacksonを使用している場合は、以下を使用できます。 org.codehaus.jackson.io.JsonStringEncoder.getInstance().quoteAsString(input)


3

「jsonを手動で作成する」とはどういう意味かわかりませんが、gson(http://code.google.com/p/google-gson/)などを使用すると、HashMap、Array、Stringなどが変換されます。 、JSON値に。このためのフレームワークを使用することをお勧めします。


2
手動で言うと、単純なJSON、Gson、XStreamなどのJSONライブラリを使用することではありません。
Behrang Saeedzadeh 2010年

好奇心の問題です。なぜこれらのAPIの1つを使用したくないのですか?これは、URLEncode / Decodeを使用する代わりに、手動でURLをエスケープしようとするようなものです
Vladimir

1
実際には同じではありません。これらのライブラリには、URLEncode / Decodeに相当するものよりもはるかに多く、json形式でのJavaオブジェクトの永続化を可能にするシリアル化パッケージ全体が含まれており、場合によっては、短いテキストの束をエンコードするだけで十分です
jmd

2
少量のデータをシリアル化するためだけのライブラリを含めたくない場合は、JSONを手動で作成してください
Aditya Kumar Pandey

2
高品質のライブラリが存在する場所でJSONを手動で作成する場合は、チームメンバーにプロジェクトの削除を依頼します。
Michael Joyce

2

私は100%確実にするために時間を費やしていませんが、オンラインJSONバリデーターによって受け入れられるのに十分なほど私の入力にはうまくいきました:

org.apache.velocity.tools.generic.EscapeTool.EscapeTool().java("input")

それは何よりもよく見えませんが org.codehaus.jettison.json.JSONObject.quote("your string")

私はすでにプロジェクトでベロシティツールを使用しています-「手動JSON」ビルドはベロシティテンプレート内にありました


2

私のようにコマンドラインソリューションを探してここに来た人にとっては、cURLの--data-urlencodeはうまく機能します。

curl -G -v -s --data-urlencode 'query={"type" : "/music/artist"}' 'https://www.googleapis.com/freebase/v1/mqlread'

送る

GET /freebase/v1/mqlread?query=%7B%22type%22%20%3A%20%22%2Fmusic%2Fartist%22%7D HTTP/1.1

、 例えば。より大きなJSONデータをファイルに入れることができ、@構文を使用して、エスケープ対象のデータを取り込むファイルを指定します。たとえば、

$ cat 1.json 
{
  "type": "/music/artist",
  "name": "The Police",
  "album": []
}

あなたは使うだろう

curl -G -v -s --data-urlencode query@1.json 'https://www.googleapis.com/freebase/v1/mqlread'

そして今、これはコマンドラインからFreebaseをクエリする方法のチュートリアルでもあります:-)



1

MoshiJsonWriterクラスを考えてみましょう。それは素晴らしいAPIを備えており、コピーを最小限に抑え、すべてをファイル、OutputStreamなどにうまくストリーミングできます。

OutputStream os = ...;
JsonWriter json = new JsonWriter(Okio.buffer(Okio.sink(os)));
json.beginObject();
json.name("id").value(getId());
json.name("scores");
json.beginArray();
for (Double score : getScores()) {
  json.value(score);
}
json.endArray();
json.endObject();

文字列を手に入れたい場合:

Buffer b = new Buffer(); // okio.Buffer
JsonWriter writer = new JsonWriter(b);
//...
String jsonString = b.readUtf8();


0

JSON文字列内のJSONをエスケープする必要がある場合は、org.json.JSONObject.quote( "エスケープする必要のあるjson文字列")を使用してください。


0

\ uXXXX構文を使用すると、この問題を解決できます。GoogleUTF-16で標識の名前を使用すると、XXXXを見つけることができます。例:utf-16二重引用符


0

実際の実装を示すここのメソッドはすべて不完全です。
Javaコードはありませんが、念のため、このC#コードを簡単に変換できます。

mono-project @ https://github.com/mono/mono/blob/master/mcs/class/System.Web/System.Web/HttpUtility.cs提供

public static string JavaScriptStringEncode(string value, bool addDoubleQuotes)
{
    if (string.IsNullOrEmpty(value))
        return addDoubleQuotes ? "\"\"" : string.Empty;

    int len = value.Length;
    bool needEncode = false;
    char c;
    for (int i = 0; i < len; i++)
    {
        c = value[i];

        if (c >= 0 && c <= 31 || c == 34 || c == 39 || c == 60 || c == 62 || c == 92)
        {
            needEncode = true;
            break;
        }
    }

    if (!needEncode)
        return addDoubleQuotes ? "\"" + value + "\"" : value;

    var sb = new System.Text.StringBuilder();
    if (addDoubleQuotes)
        sb.Append('"');

    for (int i = 0; i < len; i++)
    {
        c = value[i];
        if (c >= 0 && c <= 7 || c == 11 || c >= 14 && c <= 31 || c == 39 || c == 60 || c == 62)
            sb.AppendFormat("\\u{0:x4}", (int)c);
        else switch ((int)c)
            {
                case 8:
                    sb.Append("\\b");
                    break;

                case 9:
                    sb.Append("\\t");
                    break;

                case 10:
                    sb.Append("\\n");
                    break;

                case 12:
                    sb.Append("\\f");
                    break;

                case 13:
                    sb.Append("\\r");
                    break;

                case 34:
                    sb.Append("\\\"");
                    break;

                case 92:
                    sb.Append("\\\\");
                    break;

                default:
                    sb.Append(c);
                    break;
            }
    }

    if (addDoubleQuotes)
        sb.Append('"');

    return sb.ToString();
}

これは、

    // https://github.com/mono/mono/blob/master/mcs/class/System.Json/System.Json/JsonValue.cs
public class SimpleJSON
{

    private static  bool NeedEscape(string src, int i)
    {
        char c = src[i];
        return c < 32 || c == '"' || c == '\\'
            // Broken lead surrogate
            || (c >= '\uD800' && c <= '\uDBFF' &&
                (i == src.Length - 1 || src[i + 1] < '\uDC00' || src[i + 1] > '\uDFFF'))
            // Broken tail surrogate
            || (c >= '\uDC00' && c <= '\uDFFF' &&
                (i == 0 || src[i - 1] < '\uD800' || src[i - 1] > '\uDBFF'))
            // To produce valid JavaScript
            || c == '\u2028' || c == '\u2029'
            // Escape "</" for <script> tags
            || (c == '/' && i > 0 && src[i - 1] == '<');
    }



    public static string EscapeString(string src)
    {
        System.Text.StringBuilder sb = new System.Text.StringBuilder();

        int start = 0;
        for (int i = 0; i < src.Length; i++)
            if (NeedEscape(src, i))
            {
                sb.Append(src, start, i - start);
                switch (src[i])
                {
                    case '\b': sb.Append("\\b"); break;
                    case '\f': sb.Append("\\f"); break;
                    case '\n': sb.Append("\\n"); break;
                    case '\r': sb.Append("\\r"); break;
                    case '\t': sb.Append("\\t"); break;
                    case '\"': sb.Append("\\\""); break;
                    case '\\': sb.Append("\\\\"); break;
                    case '/': sb.Append("\\/"); break;
                    default:
                        sb.Append("\\u");
                        sb.Append(((int)src[i]).ToString("x04"));
                        break;
                }
                start = i + 1;
            }
        sb.Append(src, start, src.Length - start);
        return sb.ToString();
    }
}

quote()他の回答に記載されている方法はどのように間違っていますか?
Sandy

0

2017年の最良の答えはjavax.json APIを使用することだと思います。javax.json.JsonBuilderFactoryを使用してjsonオブジェクトを作成し、javax.json.JsonWriterFactoryを使用してオブジェクトを書き出します。非常に素晴らしいビルダー/ライターの組み合わせ。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.