Comment échapper à la chaîne JSON?


97

Existe-t-il des classes / fonctions disponibles à utiliser pour une échappement JSON facile? Je préfère ne pas avoir à écrire le mien.


3
JsonConvert.ToString () a fonctionné pour moi.
Martin Lottering

Réponses:



47

Pour ceux qui utilisent le très populaire projet Json.Net de Newtonsoft, la tâche est triviale:

using Newtonsoft.Json;

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

Ce code imprime:

"un B"

Autrement dit, la valeur de chaîne résultante contient les guillemets ainsi que la barre oblique inverse échappée.


2
Je ne peux pas reproduire cette méthode pour désérialiser un chemin unc encodé et échappé. Mon chemin "WatchedPath": "\\\\myserver\\output"devient "\"\\\\\\\\myserver\\\\output\""ce qui est assez inacceptable.
slestak

3
La méthode ci-dessus n'est pas pour la désérialisation - rater, elle est utilisée lorsque vous souhaitez créer un texte JSON manuellement et que vous avez une chaîne C # et que vous devez obtenir sa représentation correcte sous forme de texte.
Dror Harari

@slestak, je pense que je suis confronté au même problème que vous étiez ici. Avez-vous trouvé une solution?
GP24

@ GP24 IIRC, je ne l'ai pas fait. Désolé je n'ai plus d'informations.
slestak

Pas de problème, merci d'avoir répondu. Je l'ai fait si cela vous aide: yourAnnoyingDoubleEncodedString.Replace ("\\\\", "\\"). Replace ("\\\" "," \ "");
GP24 du

38

En vous basant sur la réponse de Dejan , vous pouvez importer System.Web.Helpersl'assembly .NET Framework , puis utiliser la fonction suivante:

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

L' Substringappel est obligatoire, car Encodeentoure automatiquement les chaînes de guillemets.


On dirait que System.Web.Helpers n'est pas disponible avant .Net 4.0
SerG

… Et pas plus dans Visual Studio 2015 aussi.
lapo

5
Cela fait partie des pages Web ASP.NET 2.0. Il peut être ajouté à l'aide de NuGet. Cela ne fait pas partie du cadre.
Murven

30

Oui, ajoutez simplement la fonction suivante à votre classe Utils ou quelque chose:

    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
Pourquoi avez-vous besoin de vous échapper /?
drzaus

Je sais que c'est une vieille réponse et je suis heureux de voir que cela a été donné car je ne voulais pas me fier à des bibliothèques externes, mais j'ai remarqué que la casse par défaut pour un caractère de contrôle retournera toujours "\\ u000X". Je crois que vous devez d'abord convertir le caractère en un int. Pensez à le remplacer parstring t = "000" + ((int)c).ToString("X");
Jan Discart

La casse par défaut correcte doit être:t = "000" + String.Format("{0:X}",(int) c);
daniatic le

16

J'ai utilisé le code suivant pour échapper à la valeur de chaîne pour json. Vous devez ajouter votre "" "à la sortie du code suivant:

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
Cela m'a vraiment sauvé la journée. Merci beaucoup!
casaout

8
N'utilisez pas ce code en production! Cet échappement JSON manque des caractères spéciaux importants. Voir: stackoverflow.com/a/33799784
vog

2
Ce code ne couvre pas tous les cas particuliers. NE PAS utiliser en production.
Envil

2
réinventer la roue, et introduire un bug dans des cas particuliers, n'est pas une bonne réponse
Xilmiki

6

Les méthodes proposées ici sont défectueuses.
Pourquoi s'aventurer aussi loin alors que vous pourriez simplement utiliser System.Web.HttpUtility.JavaScriptEncode?

Si vous êtes sur un framework inférieur, vous pouvez simplement le copier-coller à partir du mono

Gracieuseté du mono-projet @ 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();
    }

Cela peut être compacté 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();
    }
}


4

J'ai effectué des tests de vitesse sur certaines de ces réponses pour une longue chaîne et une courte chaîne. Le code de Clive Paterson a bien gagné, probablement parce que les autres prennent en compte les options de sérialisation. Voici mes résultats:

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

Et voici le code de test:

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


2
String.Format("X", c);

Cela sort juste: X

Essayez plutôt ceci:

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

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

2

J'ai une belle ligne, j'ai utilisé JsonConvert comme d'autres l'ont fait, mais j'ai ajouté une sous-chaîne pour supprimer les guillemets ajoutés et la barre oblique inverse.

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

2

Dans .Net Core 3+ et .Net 5+:

string escapedJsonString = JsonEncodedText.Encode(jsonString);


0

J'ai choisi d'utiliser System.Web.Script.Serialization.JavaScriptSerializer.

J'ai une petite classe d'assistance statique définie comme suit:

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

Pour sérialiser tout ce que je viens d'appeler Serialization.ToJSON(itemToSerialize)

Pour désérialiser je viens d'appeler Serialization.FromJSON<T>(jsonValueOfTypeT)

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.