Как избежать строки JSON?

101

Есть ли какие-либо классы / функции, которые можно использовать для легкого экранирования JSON? Я бы предпочел не писать свои собственные.

Theringostarrs
источник
4
JsonConvert.ToString () работал у меня.
Мартин Лоттеринг
@MartinLottering Спасибо !!! Я искал способ получить json в форматированной строке. Ни один из приведенных ниже ответов не сработал, но это сработало.
GhostShaman

Ответы:

81

я использую System.Web.HttpUtility.JavaScriptStringEncode

string quoted = HttpUtility.JavaScriptStringEncode(input);
xmedeko
источник
5
Я использовал это, чтобы избежать отсутствия System.Web.Helpers.Json.Encodeв VS2015, но ему также нужен (input, true)параметр, чтобы включать фактические кавычки.
Lapo
Для меня это было недостающее звено
Джавид Хассим
50

Для тех, кто использует очень популярный проект Json.Net от Newtonsoft, задача тривиальна:

using Newtonsoft.Json;

....
var s = JsonConvert.ToString(@"a\b");
Console.WriteLine(s);
....

Этот код печатает:

"а \\ б"

То есть результирующее строковое значение содержит кавычки, а также экранированную обратную косую черту.

Дрор Харари
источник
2
Я не могу воспроизвести этот метод десериализации закодированного и экранированного пути unc. Мой путь "WatchedPath": "\\\\myserver\\output"становится "\"\\\\\\\\myserver\\\\output\""совершенно неприемлемым.
slestak
3
Вышеупомянутый метод не предназначен для десериализации - он используется, когда вы хотите создать текст JSON вручную, и у вас есть строка C #, и вам нужно получить ее правильное представление в виде текста.
Дрор Харари
@slestak, я думаю, что столкнулся с той же проблемой, что и вы. Вы нашли решение?
GP24,
@ GP24 IIRC, я этого не делал. Извините, у меня больше нет информации.
slestak
Хорошо, спасибо за ответ. Я сделал это, если вам это поможет: yourAnnoyingDoubleEncodedString.Replace ("\\\\", "\\"). Replace ("\\\" "," \ "");
GP24
40

Основываясь на ответе Деяна , вы можете импортировать System.Web.Helpersсборку .NET Framework , а затем использовать следующую функцию:

static string EscapeForJson(string s) {
  string quoted = System.Web.Helpers.Json.Encode(s);
  return quoted.Substring(1, quoted.Length - 2);
}

SubstringВызова требуется, поскольку Encodeавтоматически окружают строки в двойных кавычках.

Рок Стрниша
источник
Похоже, System.Web.Helpers недоступен до .Net 4.0
SerG
… И не более того в Visual Studio 2015.
Lapo
5
Это часть веб-страниц ASP.NET 2.0. Его можно добавить с помощью NuGet. Это не часть структуры.
Murven
31

Да, просто добавьте следующую функцию в свой класс Utils или что-то в этом роде:

    public static string cleanForJSON(string s)
    {
        if (s == null || s.Length == 0) {
            return "";
        }

        char         c = '\0';
        int          i;
        int          len = s.Length;
        StringBuilder sb = new StringBuilder(len + 4);
        String       t;

        for (i = 0; i < len; i += 1) {
            c = s[i];
            switch (c) {
                case '\\':
                case '"':
                    sb.Append('\\');
                    sb.Append(c);
                    break;
                case '/':
                    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" + String.Format("X", c);
                        sb.Append("\\u" + t.Substring(t.Length - 4));
                    } else {
                        sb.Append(c);
                    }
                    break;
            }
        }
        return sb.ToString();
    }
Клайв Патерсон
источник
3
Зачем тебе бежать /?
drzaus
Я знаю, что это старый ответ, и я рад, что это было дано, поскольку я не хотел полагаться на какие-либо внешние библиотеки, но я заметил, что случай по умолчанию для управляющего символа всегда будет возвращать «\\ u000X». Я считаю, что вам нужно сначала передать char в int. Подумайте о том, чтобы заменить его наstring t = "000" + ((int)c).ToString("X");
Jan Discart
Правильный регистр по умолчанию должен быть:t = "000" + String.Format("{0:X}",(int) c);
daniatic,
На самом деле мы хотим " "\\u" + ((int)c).ToString("X4") (хотя я думаю, что два приложения были бы еще лучше)"
Джеймс Карран,
16

Я использовал следующий код, чтобы избежать строкового значения для json. Вам нужно добавить ваш '"' к выходным данным следующего кода:

public static string EscapeStringValue(string value)
{
    const char BACK_SLASH = '\\';
    const char SLASH = '/';
    const char DBL_QUOTE = '"';

    var output = new StringBuilder(value.Length);
    foreach (char c in value)
    {
        switch (c)
        {
            case SLASH:
                output.AppendFormat("{0}{1}", BACK_SLASH, SLASH);
                break;

            case BACK_SLASH:
                output.AppendFormat("{0}{0}", BACK_SLASH);
                break;

            case DBL_QUOTE:
                output.AppendFormat("{0}{1}",BACK_SLASH,DBL_QUOTE);
                break;

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

    return output.ToString();
}
Амит Бхагат
источник
1
Это действительно спасло мне день. Большое спасибо!
casaout
8
Не используйте этот код в производстве! Это экранирование JSON пропускает важные специальные символы. См .: stackoverflow.com/a/33799784
vog
2
Этот код не охватывает все особые случаи. НЕ использовать в производстве.
Envil
2
изобретать велосипед и вводить некоторые ошибки в особых случаях - не лучший ответ
Xilmiki
6

Предлагаемые здесь методы ошибочны.
Зачем заходить так далеко, если можно просто использовать System.Web.HttpUtility.JavaScriptEncode?

Если вы используете более низкий фреймворк, вы можете просто скопировать и вставить его из моно

Предоставлено монопроектом @ 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();
    }
}
Стефан Штайгер
источник
4

Я бы также рекомендовал использовать упомянутую библиотеку JSON.NET , но если вам нужно экранировать символы Юникода (например, формат \ uXXXX) в результирующей строке JSON, вам, возможно, придется сделать это самостоятельно. Взгляните на Преобразование строк Unicode в экранированную строку ascii в качестве примера.

Кевин Хакансон
источник
4

Я провел тесты скорости на некоторых из этих ответов для длинной строки и короткой строки. Код Клайва Патерсона выиграл с большим преимуществом, по-видимому, потому, что остальные учитывают параметры сериализации. Вот мои результаты:

Apple Banana
System.Web.HttpUtility.JavaScriptStringEncode: 140ms
System.Web.Helpers.Json.Encode: 326ms
Newtonsoft.Json.JsonConvert.ToString: 230ms
Clive Paterson: 108ms

\\some\long\path\with\lots\of\things\to\escape\some\long\path\t\with\lots\of\n\things\to\escape\some\long\path\with\lots\of\"things\to\escape\some\long\path\with\lots"\of\things\to\escape
System.Web.HttpUtility.JavaScriptStringEncode: 2849ms
System.Web.Helpers.Json.Encode: 3300ms
Newtonsoft.Json.JsonConvert.ToString: 2827ms
Clive Paterson: 1173ms

А вот и тестовый код:

public static void Main(string[] args)
{
    var testStr1 = "Apple Banana";
    var testStr2 = @"\\some\long\path\with\lots\of\things\to\escape\some\long\path\t\with\lots\of\n\things\to\escape\some\long\path\with\lots\of\""things\to\escape\some\long\path\with\lots""\of\things\to\escape";

    foreach (var testStr in new[] { testStr1, testStr2 })
    {
        var results = new Dictionary<string,List<long>>();

        for (var n = 0; n < 10; n++)
        {
            var count = 1000 * 1000;

            var sw = Stopwatch.StartNew();
            for (var i = 0; i < count; i++)
            {
                var s = System.Web.HttpUtility.JavaScriptStringEncode(testStr);
            }
            var t = sw.ElapsedMilliseconds;
            results.GetOrCreate("System.Web.HttpUtility.JavaScriptStringEncode").Add(t);

            sw = Stopwatch.StartNew();
            for (var i = 0; i < count; i++)
            {
                var s = System.Web.Helpers.Json.Encode(testStr);
            }
            t = sw.ElapsedMilliseconds;
            results.GetOrCreate("System.Web.Helpers.Json.Encode").Add(t);

            sw = Stopwatch.StartNew();
            for (var i = 0; i < count; i++)
            {
                var s = Newtonsoft.Json.JsonConvert.ToString(testStr);
            }
            t = sw.ElapsedMilliseconds;
            results.GetOrCreate("Newtonsoft.Json.JsonConvert.ToString").Add(t);

            sw = Stopwatch.StartNew();
            for (var i = 0; i < count; i++)
            {
                var s = cleanForJSON(testStr);
            }
            t = sw.ElapsedMilliseconds;
            results.GetOrCreate("Clive Paterson").Add(t);
        }

        Console.WriteLine(testStr);
        foreach (var result in results)
        {
            Console.WriteLine(result.Key + ": " + Math.Round(result.Value.Skip(1).Average()) + "ms");
        }
        Console.WriteLine();
    }

    Console.ReadLine();
}
безымянный227
источник
3

В .Net Core 3+ и .Net 5+:

string escapedJsonString = JsonEncodedText.Encode(jsonString);
Джавад Аль Шейх
источник
2
String.Format("X", c);

Это просто выводит: X

Попробуйте вместо этого:

string t = ((int)c).ToString("X");

sb.Append("\\u" + t.PadLeft(4, '0'));
user2058470
источник
2

Я хороший однострочник, использовал JsonConvert, как и другие, но добавил подстроку, чтобы удалить добавленные кавычки и обратную косую черту.

 var escapedJsonString = JsonConvert.ToString(JsonString).Substring(1, JsonString.Length - 2);
Джошуа Даксбери
источник
0

Я решил использовать System.Web.Script.Serialization.JavaScriptSerializer.

У меня есть небольшой статический вспомогательный класс, определенный следующим образом:

internal static partial class Serialization
{
    static JavaScriptSerializer serializer;
    
    static Serialization()
    {
        serializer = new JavaScriptSerializer();
        serializer.MaxJsonLength = Int32.MaxValue;
    }
    public static string ToJSON<T>(T obj)
    {
        return serializer.Serialize(obj);
    }
    public static T FromJSON<T>(string data)
    {
        if (Common.IsEmpty(data))
            return default(T);
        else
            return serializer.Deserialize<T>(data);
    }
}

Чтобы сериализовать все, что я просто звоню Serialization.ToJSON(itemToSerialize)

Для десериализации я просто звоню Serialization.FromJSON<T>(jsonValueOfTypeT)

AnthonyVO
источник