Come sfuggire alla stringa JSON?


97

Sono disponibili classi / funzioni da utilizzare per un facile escape JSON? Preferisco non dover scrivere il mio.


3
JsonConvert.ToString () ha funzionato per me.
Martin Lottering

Risposte:



47

Per coloro che utilizzano il popolare progetto Json.Net di Newtonsoft il compito è banale:

using Newtonsoft.Json;

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

Questo codice stampa:

"a \\ b"

Ovvero, il valore della stringa risultante contiene le virgolette e la barra rovesciata con escape.


2
Non è possibile riprodurre questo metodo per deserializzare un percorso unc codificato e con escape. Il mio percorso "WatchedPath": "\\\\myserver\\output"diventa "\"\\\\\\\\myserver\\\\output\""piuttosto inaccettabile.
slestak

3
Il metodo sopra non è per la deserializzazione: rater viene utilizzato quando si desidera creare manualmente un testo JSON e si dispone di una stringa C # e si deve ottenere la sua corretta rappresentazione come testo.
Dror Harari

@slestak, penso di stare affrontando lo stesso problema che eri tu qui. Hai trovato una soluzione?
GP24

@ GP24 IIRC, non l'ho fatto. Spiacente, non ho altre informazioni.
slestak

Nessun problema, grazie per aver risposto. Ho fatto questo se ti aiuta: yourAnnoyingDoubleEncodedString.Replace ("\\\\", "\\"). Sostituisci ("\\\" "," \ "");
GP24

38

Basandosi sulla risposta di Dejan , quello che puoi fare è importare l' System.Web.Helpersassembly .NET Framework , quindi utilizzare la seguente funzione:

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

La Substringchiamata è richiesta, poiché Encoderacchiude automaticamente le stringhe tra virgolette doppie.


Sembra che System.Web.Helpers non sia disponibile prima di .Net 4.0
SerG

... e non più anche in Visual Studio 2015.
giro

5
Questo fa parte di ASP.NET Web Pages 2.0. Può essere aggiunto usando NuGet. Non fa parte del framework.
Murven

30

Sì, aggiungi la seguente funzione alla tua classe Utils o qualcosa del genere:

    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
Perché hai bisogno di scappare /?
drzaus

So che questa è una vecchia risposta e sono felice di vedere che è stata data perché non volevo fare affidamento su alcuna libreria esterna, ma ho notato che il caso predefinito per un carattere di controllo restituirà sempre "\\ u000X". Credo che tu debba prima lanciare il char su un int. Considera l' string t = "000" + ((int)c).ToString("X");
idea di

Il caso predefinito corretto deve essere:t = "000" + String.Format("{0:X}",(int) c);
daniatico

16

Ho usato il seguente codice per sfuggire al valore della stringa per json. Devi aggiungere il tuo '"' all'output del codice seguente:

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
Questo mi ha davvero salvato la giornata. Molte grazie!
casaout

8
Non utilizzare questo codice in produzione! Questo codice di escape JSON manca di caratteri speciali importanti. Vedi: stackoverflow.com/a/33799784
VOG

2
Questo codice non copre tutti i casi speciali. NON utilizzare in produzione.
Envil

2
reinventare la ruota e introdurre qualche bug in casi speciali non è una buona risposta
Xilmiki

6

I metodi qui offerti sono difettosi.
Perché avventurarsi così lontano quando potresti semplicemente usare System.Web.HttpUtility.JavaScriptEncode?

Se sei su un framework inferiore, puoi semplicemente copiarlo e incollarlo da mono

Per gentile concessione del mono-progetto @ 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();
    }

Questo può essere compattato in

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

Ho eseguito test di velocità su alcune di queste risposte per una stringa lunga e una stringa corta. Il codice di Clive Paterson ha vinto un bel po ', presumibilmente perché gli altri stanno prendendo in considerazione le opzioni di serializzazione. Ecco i miei risultati:

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

Ed ecco il codice di prova:

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

Questo produce solo: X

Prova questo invece:

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

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

2

Ho una bella riga, ho usato JsonConvert come altri hanno ma aggiunto la sottostringa per rimuovere le virgolette e la barra rovesciata aggiunte.

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

2

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

string escapedJsonString = JsonEncodedText.Encode(jsonString);


0

Ho scelto di usare System.Web.Script.Serialization.JavaScriptSerializer.

Ho una piccola classe helper statica definita come segue:

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

Per serializzare tutto ciò che chiamo Serialization.ToJSON(itemToSerialize)

Per deserializzare basta chiamare Serialization.FromJSON<T>(jsonValueOfTypeT)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.