137

Are there any classes/functions available to be used for easy JSON escaping? I'd rather not have to write my own.

wonea
  • 4,783
  • 17
  • 86
  • 139
theringostarrs
  • 11,940
  • 14
  • 50
  • 63

16 Answers16

111

I use System.Web.HttpUtility.JavaScriptStringEncode

string quoted = HttpUtility.JavaScriptStringEncode(input);
David Walschots
  • 12,279
  • 5
  • 36
  • 59
xmedeko
  • 7,336
  • 6
  • 55
  • 85
  • 5
    I used this to avoid the missing `System.Web.Helpers.Json.Encode` in VS2015, but it needs the `(input, true)` parameter to include the actual quotes as well. – lapo Jun 22 '16 at 14:37
  • This was the missing link for me – Jawid Hassim Nov 01 '20 at 02:33
  • 2
    I notice that this will encode single quotes ' as `\u0027`. However single quotes are valid in a JSON string. – Stacey Jun 16 '21 at 08:01
77

For those using the very popular Json.Net project from Newtonsoft the task is trivial:

using Newtonsoft.Json;

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

This code prints:

"a\\b"

That is, the resulting string value contains the quotes as well as the escaped backslash.

Dunc
  • 18,404
  • 6
  • 86
  • 103
Dror Harari
  • 3,076
  • 2
  • 27
  • 25
  • 2
    I cannot reproduce this method for deserializing an encoded and escaped unc path. My path `"WatchedPath": "\\\\myserver\\output"` becomes `"\"\\\\\\\\myserver\\\\output\""` which is pretty unacceptable. – slestak Dec 29 '14 at 14:36
  • 3
    The method above is not for deserializing - rater it is used when you want to create a JSON text manually and you have a C# string and need to gets its proper representation as a text. – Dror Harari Dec 30 '14 at 22:50
  • @slestak, I think I am facing the same issue you were here. Did you find a solution? – GP24 Feb 11 '16 at 11:50
  • @GP24 IIRC, I did not. Sorry I do not have any more info. – slestak Feb 11 '16 at 15:15
  • No problem, thanks for replying. I did this if it helps you: yourAnnoyingDoubleEncodedString.Replace("\\\\", "\\").Replace("\\\"", "\""); – GP24 Feb 11 '16 at 15:24
  • This worked for me, but adding the outer quotes was a problem, since this is within code manually handling json-style text. I did this, then removed the outer quotes. – goodeye Mar 09 '19 at 01:44
40

Building on the answer by Dejan, what you can do is import System.Web.Helpers .NET Framework assembly, then use the following function:

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

The Substring call is required, since Encode automatically surrounds strings with double quotes.

Richard Ev
  • 52,939
  • 59
  • 191
  • 278
Rok Strniša
  • 6,781
  • 6
  • 41
  • 53
35

Yep, just add the following function to your Utils class or something:

    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();
    }
Clive Paterson
  • 749
  • 8
  • 9
  • 3
    Why do you need to escape `/`? – drzaus Apr 21 '16 at 15:11
  • I know this is an old answer and I'm happy to see this was given as I didn't want to rely on any external libraries, but I noticed that the default case for a control character will always return "\\u000X". I believe you need to cast the char first to an int. Consider replacing it with `string t = "000" + ((int)c).ToString("X");` – Jan Discart Dec 22 '18 at 19:18
  • 1
    The correct default case must be: `t = "000" + String.Format("{0:X}",(int) c);` – daniatic Apr 14 '20 at 13:03
  • 1
    What we actually want is " `"\\u" + ((int)c).ToString("X4")` (Although I think two Appends would be even better) – James Curran Nov 04 '20 at 04:53
15

I have used following code to escape the string value for json. You need to add your '"' to the output of the following code:

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();
}
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
Amit Bhagat
  • 4,182
  • 3
  • 23
  • 24
  • 9
    Do not use this code in production! This JSON escaping misses important special characters. See: http://stackoverflow.com/a/33799784 – vog Nov 19 '15 at 11:49
  • 2
    This code does not cover all the special cases. DO NOT use in production. – Envil Sep 08 '16 at 04:49
  • 3
    reinvent the wheel, and introduce some bug in special cases, is not a good answer – Xilmiki Oct 19 '16 at 09:47
12

The methods offered here are faulty.
Why venture that far when you could just use System.Web.HttpUtility.JavaScriptEncode ?

If you're on a lower framework, you can just copy paste it from mono

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

This can be compacted into

// 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();
    }
}
Stefan Steiger
  • 78,642
  • 66
  • 377
  • 442
12

In .Net Core 3+ and .Net 5+:

string escapedJsonString = JsonEncodedText.Encode(jsonString);
Jawad Al Shaikh
  • 2,475
  • 2
  • 28
  • 41
  • 3
    You need to mention that this does not do trivial escaping by default and the result might be much more escaped than you want. For example, quotes, triangle brackets and many letters will be converted to unicode characters and will only be usable if you unescape at the other end. – Luke Briner Mar 08 '22 at 09:43
5

I ran speed tests on some of these answers for a long string and a short string. Clive Paterson's code won by a good bit, presumably because the others are taking into account serialization options. Here are my results:

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

And here is the test code:

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();
}
Community
  • 1
  • 1
innominate227
  • 11,109
  • 1
  • 17
  • 20
4

I would also recommend using the JSON.NET library mentioned, but if you have to escape unicode characters (e.g. \uXXXX format) in the resulting JSON string, you may have to do it yourself. Take a look at Converting Unicode strings to escaped ascii string for an example.

Community
  • 1
  • 1
Kevin Hakanson
  • 41,386
  • 23
  • 126
  • 155
4

I nice one-liner, used JsonConvert as others have but added substring to remove the added quotes and backslash.

 var escapedJsonString = JsonConvert.ToString(JsonString).Substring(1, JsonString.Length - 2);
Joshua Duxbury
  • 4,892
  • 4
  • 32
  • 51
  • I think you mean var escapedJsonString = JsonConvert.ToString(JsonString).Substring(1, JsonString.Length); - otherwise you chop 2 characters off the end of your string. Remember, the \" are additional characters to the length of the original. – Bertie Jul 01 '21 at 13:37
3

What about System.Web.Helpers.Json.Encode(...) (see http://msdn.microsoft.com/en-us/library/system.web.helpers.json.encode(v=vs.111).aspx)?

Dejan
  • 9,150
  • 8
  • 69
  • 117
2
String.Format("X", c);

That just outputs: X

Try this instead:

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

sb.Append("\\u" + t.PadLeft(4, '0'));
0

There's a Json library at Codeplex

Joseph Yaduvanshi
  • 20,241
  • 5
  • 61
  • 69
0

I chose to use System.Web.Script.Serialization.JavaScriptSerializer.

I have a small static helper class defined as follows:

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

To serialize anything I just call Serialization.ToJSON(itemToSerialize)

To deserialize I just call Serialization.FromJSON<T>(jsonValueOfTypeT)

AnthonyVO
  • 3,821
  • 1
  • 36
  • 41
0

.NET 6 - System.Text.Json

var encodedText = JsonEncodedText.Encode(inputText);

0

The C# method bellow is a more advanced optimization version using Span for small string inputs, to avoid heap allocation and StringBuilder for larger inputs to avoids excessive stack usage.

public static string JsonEscape(string text) {
    if (text.Length == 0) {
        return string.Empty;
    }

    if (text.Length < 1204) {
        int count = 0;
        foreach (char c in text) {
            switch (c) {
            case '\\':
            case '\"':
            case '\b':
            case '\f':
            case '\n':
            case '\r':
            case '\t':
                count += 2;
                break;

            default:
                count++;
                break;
            }
        }

        Span<char> result = stackalloc char[count];
        count = 0;
        foreach (char c in text) {
            switch (c) {
            case '\\':
            case '\"':
            case '\b':
            case '\f':
            case '\n':
            case '\r':
            case '\t':
                result[count++] = '\\';
                result[count++] = c;
                break;

            default:
                result[count++] = c;
                break;
            }
        }
        return result.ToString();
    }

    StringBuilder sb = new StringBuilder();
    foreach (char c in text) {
        switch (c) {
        case '\\': sb.Append("\\\\"); break;
        case '\"': sb.Append("\\\""); break;
        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;
        default: sb.Append(c); break;
        }
    }
    return sb.ToString();
}
veni
  • 41
  • 4