¿Cómo escapar de la cadena JSON?

97

¿Hay clases / funciones disponibles para usar para un escape JSON fácil? Preferiría no tener que escribir el mío.

theringostarrs
fuente
3
JsonConvert.ToString () funcionó para mí.
Martin Lottering

Respuestas:

74

yo suelo System.Web.HttpUtility.JavaScriptStringEncode

string quoted = HttpUtility.JavaScriptStringEncode(input);
xmedeko
fuente
5
System.Web.Helpers.Json.EncodeUsé esto para evitar que falten en VS2015, pero también necesita que el (input, true)parámetro incluya las cotizaciones reales.
lapo
47

Para aquellos que utilizan el proyecto Json.Net muy popular de Newtonsoft, la tarea es trivial:

using Newtonsoft.Json;

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

Este código imprime:

"a \\ b"

Es decir, el valor de la cadena resultante contiene las comillas y la barra invertida de escape.

Dror Harari
fuente
2
No puedo reproducir este método para deserializar una ruta unc codificada y escapada. Mi camino se "WatchedPath": "\\\\myserver\\output"convierte en "\"\\\\\\\\myserver\\\\output\""algo bastante inaceptable.
slestak
3
El método anterior no es para deserialización: el evaluador se usa cuando desea crear un texto JSON manualmente y tiene una cadena C # y necesita obtener su representación adecuada como texto.
Dror Harari
@slestak, creo que estoy enfrentando el mismo problema que tu estabas aquí. ¿Encontraste una solución?
GP24
@ GP24 IIRC, no lo hice. Lo siento, no tengo más información.
slestak
No hay problema, gracias por responder. Hice esto si te ayuda: yourAnnoyingDoubleEncodedString.Replace ("\\\\", "\\"). Replace ("\\\" "," \ "");
GP24
38

Basándose en la respuesta de Dejan , lo que puede hacer es importar el System.Web.Helpersensamblaje de .NET Framework y luego usar la siguiente función:

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

La Substringllamada es necesaria, ya que Encoderodea automáticamente las cadenas con comillas dobles.

Rok Strniša
fuente
Parece que System.Web.Helpers no está disponible antes de .Net 4.0
SerG
… Y no más en Visual Studio 2015 también.
lapo
5
Esto es parte de ASP.NET Web Pages 2.0. Se puede agregar mediante NuGet. No es parte del marco.
Murven
30

Sí, simplemente agregue la siguiente función a su clase de Utils o algo así:

    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
fuente
3
¿Por qué necesitas escapar /?
drzaus
Sé que esta es una respuesta antigua y me alegra ver que se dio, ya que no quería depender de ninguna biblioteca externa, pero noté que el caso predeterminado para un carácter de control siempre devolverá "\\ u000X". Creo que necesitas lanzar el char primero a un int. Considere reemplazarlo constring t = "000" + ((int)c).ToString("X");
Jan Discart
El caso predeterminado correcto debe ser:t = "000" + String.Format("{0:X}",(int) c);
daniatic
16

He usado el siguiente código para escapar del valor de cadena para json. Debe agregar su '"' a la salida del siguiente código:

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();
}
Amit Bhagat
fuente
1
Esto realmente me salvó el día. ¡Muchas gracias!
casaout
8
¡No use este código en producción! Este escape JSON pierde importantes caracteres especiales. Ver: stackoverflow.com/a/33799784
vog
2
Este código no cubre todos los casos especiales. NO lo use en producción.
Envil
2
reinventar la rueda e introducir algún error en casos especiales, no es una buena respuesta
Xilmiki
6

Los métodos ofrecidos aquí son defectuosos.
¿Por qué aventurarse tan lejos cuando podría usar System.Web.HttpUtility.JavaScriptEncode?

Si está en un marco inferior, puede copiarlo y pegarlo desde mono

Cortesía del mono-proyecto @ 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();
    }

Esto se puede compactar en

// 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
fuente
4

Realicé pruebas de velocidad en algunas de estas respuestas para una cadena larga y una cadena corta. El código de Clive Paterson ganó por una buena parte, presumiblemente porque los demás están teniendo en cuenta las opciones de serialización. Aquí están mis resultados:

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

Y aquí está el código de prueba:

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();
}
innominado227
fuente
2
String.Format("X", c);

Eso solo genera: X

Prueba esto en su lugar:

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

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

Me gustó una sola línea, usé JsonConvert como lo han hecho otros, pero agregué una subcadena para eliminar las comillas y la barra invertida agregadas.

 var escapedJsonString = JsonConvert.ToString(JsonString).Substring(1, JsonString.Length - 2);
Joshua Duxbury
fuente
2

En .Net Core 3+ y .Net 5+:

string escapedJsonString = JsonEncodedText.Encode(jsonString);
Jawad Al Shaikh
fuente
0

Elegí usar System.Web.Script.Serialization.JavaScriptSerializer.

Tengo una pequeña clase auxiliar estática definida de la siguiente manera:

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

Para serializar todo lo que acabo de llamar Serialization.ToJSON(itemToSerialize)

Para deserializar solo llamo Serialization.FromJSON<T>(jsonValueOfTypeT)

AnthonyVO
fuente