jQuery.parseJSON zgłasza błąd „Nieprawidłowy JSON” z powodu unikania pojedynczego cudzysłowu w JSON

202

Przesyłam żądania do mojego serwera za pomocą, jQuery.post()a mój serwer zwraca obiekty JSON (jak { "var": "value", ... }). Jednakże, jeśli którakolwiek z wartości zawiera pojedynczy cudzysłów (poprawnie poprzedzony znakiem ucieczki \'), jQuery nie analizuje łańcucha JSON, który w innym przypadku byłby prawidłowy. Oto przykład tego, co mam na myśli ( zrobione w konsoli Chrome ):

data = "{ \"status\": \"success\", \"newHtml\": \"Hello \\\'x\" }";
eval("x = " + data); // { newHtml: "Hello 'x", status: "success" }

$.parseJSON(data); // Invalid JSON: { "status": "success", "newHtml": "Hello \'x" }

Czy to normalne? Czy nie ma sposobu, aby poprawnie przekazać pojedynczy cytat przez JSON?

Felix
źródło

Odpowiedzi:

325

Zgodnie ze schematem automatu stanów na stronie JSON dozwolone są tylko znaki podwójnego cudzysłowu, a nie pojedyncze cudzysłowy. Nie trzeba wstawiać znaków pojedynczego cudzysłowu:

http://www.json.org/string.gif


Aktualizacja - więcej informacji dla zainteresowanych:


Douglas Crockford nie wyjaśnia konkretnie, dlaczego specyfikacja JSON nie zezwala na unikanie pojedynczych cudzysłowów w łańcuchach. Jednak podczas dyskusji o JSON w załączniku E do JavaScript: The Good Parts pisze:

Cele projektowe JSON miały być minimalne, przenośne, tekstowe i podzbiór JavaScript. Im mniej musimy się zgodzić na współpracę, tym łatwiej możemy współpracować.

Być może więc postanowił zezwolić na definiowanie łańcuchów tylko przy użyciu podwójnych cudzysłowów, ponieważ jest to jedna mniejsza zasada, na którą muszą się zgodzić wszystkie implementacje JSON. W rezultacie pojedynczy znak cudzysłowu w ciągu nie może przypadkowo zakończyć łańcucha, ponieważ z definicji ciąg może być zakończony tylko znakiem podwójnego cudzysłowu. Dlatego nie ma potrzeby zezwalania na ucieczkę pojedynczego cudzysłowu w specyfikacji formalnej.


Kopanie trochę głębiej, Crockforda za org.json implementacja JSON Java jest bardziej dopuszczalne i nie pozwalają pojedyncze znaki cudzysłowu:

Teksty wytworzone metodami toString ściśle odpowiadają regułom składni JSON. Konstruktorzy bardziej wybaczają w tekstach, które zaakceptują:

...

  • Ciągi można cytować za pomocą „(pojedynczy cudzysłów).

Potwierdza to kod źródłowy JSONTokener . nextStringMetoda akceptuje uciekł cytatów i traktuje je tak jak cudzysłów:

public String nextString(char quote) throws JSONException {
    char c;
    StringBuffer sb = new StringBuffer();
    for (;;) {
        c = next();
        switch (c) {

        ...

        case '\\':
            c = this.next();
            switch (c) {

            ...

            case '"':
            case '\'':
            case '\\':
            case '/':
                sb.append(c);
                break;
        ...

Na górze metody znajduje się pouczający komentarz:

Formalny format JSON nie dopuszcza ciągów w pojedynczych cudzysłowach, ale implementacja może je akceptować.

Dlatego niektóre implementacje zaakceptują pojedyncze cytaty - ale nie powinieneś na tym polegać. Wiele popularnych implementacji jest pod tym względem dość restrykcyjnych i odrzuci JSON, który zawiera ciągi pojedynczego cudzysłowu i / lub uniknął pojedynczych cudzysłowów.


Na koniec, aby powiązać to z pierwotnym pytaniem, jQuery.parseJSONnajpierw próbuje się użyć natywnego parsera JSON przeglądarki lub załadowanej biblioteki, takiej jak json2.js, o ile ma to zastosowanie (która z boku jest biblioteką, na której opiera się logika jQuery, jeśli JSONnie jest zdefiniowana) . W związku z tym jQuery może być tak liberalne jak ta implementacja:

parseJSON: function( data ) {
    ...

    // Attempt to parse using the native JSON parser first
    if ( window.JSON && window.JSON.parse ) {
        return window.JSON.parse( data );
    }

    ...

    jQuery.error( "Invalid JSON: " + data );
},

O ile wiem, implementacje te są zgodne tylko z oficjalną specyfikacją JSON i nie akceptują pojedynczych cudzysłowów, dlatego też jQuery nie.

Justin Ethier
źródło
4
AKTUALIZACJA :: JQuery jest bardzo restrykcyjny przy przekazywaniu JSON. Jeśli spróbujesz alert ($. ParseJSON ("[\" Ciao \\ '\ "]")); nie działa z powodu tego, co poinformował Justin
daitangio,
2
Implementacja JSON dla Javy w Javie przez Crockforda jest bardziej dopuszczalna i pozwala na stosowanie pojedynczych cudzysłowów ” # To tylko dobra praktyka: zasada niezawodności
Duncan Jones
1
@DuncanJones - ten artykuł może dać wgląd w to, dlaczego żadna z przeglądarek nie wydaje się przestrzegać tej zasady w odniesieniu do JSON: joelonsoftware.com/items/2008/03/17.html
Justin Ethier
1
@JustinEthier, jak wskazano w odpowiedzi stackoverflow.com/a/25491642/759452 , specyfikacja JSON tools.ietf.org/html/rfc7159 mówi Any character may be escaped, że może to wyjaśniać, dlaczego niektóre implementacje umożliwiają unikanie pojedynczych cudzysłowów.
Adrien Be
1
@AdrienBe - Ciekawe ... ale czy oznaczały one, że dowolny znak może zostać ocalony, jeśli składa się z 4 cyfr szesnastkowych? Zgodnie zarówno z powyższym diagramem stanu, jak i tym w sekcji 7 RFC, ucieczka pojedynczego cytatu w formie pisemnej \'jest nadal niedozwolona. Byłoby miło, gdyby RFC było bardziej wyraźne w tej kwestii.
Justin Ethier,
15

Jeśli potrzebujesz pojedynczy cudzysłów wewnątrz łańcucha, ponieważ \”jest zdefiniowana przez spec, stosowanie \u0027 zobaczyć http://www.utf8-chartable.de/ dla nich wszystkich

edytuj: proszę wybaczyć moje niewłaściwe użycie słowa backticks w komentarzach. Miałem na myśli odwrotny ukośnik. Chodzi mi o to, że w przypadku zagnieżdżenia ciągów w innych ciągach, myślę, że bardziej użyteczne i czytelne może być użycie Unicode zamiast wielu odwrotnych ukośników, aby uniknąć pojedynczego cudzysłowu. Jeśli nie jesteś zagnieżdżony, naprawdę łatwiej jest po prostu umieścić tam zwykły stary cytat.

slf
źródło
29
Nie. Wystarczy użyć zwykłego pojedynczego cytatu.
Jeff Kaufman
Czasami użycie Unicode jest po prostu łatwiejsze niż tony tyknięć. Zwłaszcza w przypadku naprzemiennych tyknięć.
slf
3
Po co ci backtyki? Jeśli masz ciąg znaków typu „pasek foo”, po prostu pozostaw pojedyncze cudzysłowy bez zmian.
Jeff Kaufman
3
dokładnie to, czego szukałem. Próbuję napisać ciąg json na stronie jako ciąg js var i zawrzeć go w pojedynczych cudzysłowach, a kończy się on wcześnie, ilekroć wartość właściwości zawiera w sobie pojedynczy cudzysłów. Teraz po prostu robię json.Replace („”, „\ u0027”) w kodzie przed zapisaniem go na stronie.
Zack
@Zack Nie należy dołączać JSON z cytatami. Jeśli potrzebujesz łańcucha z istniejącego łańcucha JSON, po prostu ponownie go skreśl. w PHP, który byłby var jsonEncodedAsString = <?= json_encode(myEncodedJson) ?>gdzie myEncodedJsonjest wynikiem poprzedniej json_encodeTo zajmie ucieczce swoją apostrof, właściwie, to po prostu wyjść coś wielki łańcuch owinięty w cudzysłowach, więc pojedyncze cudzysłowy nie będzie uciec, ale podwójne cudzysłowy będzie.
Juan Mendes,
5

Rozumiem, na czym polega problem, a kiedy patrzę na specyfikację, jasne jest, że pojedyncze cudzysłowy bez poprawek powinny być poprawnie analizowane.

Korzystam z funkcji jQuery.parseJSON w jquery`s, aby przeanalizować ciąg JSON, ale nadal pojawia się błąd parsowania, gdy w danych przygotowanych za pomocą json_encode występuje pojedynczy cytat.

Czy to może być błąd w mojej implementacji, który wygląda tak (PHP - po stronie serwera):

$data = array();

$elem = array();
$elem['name'] = 'Erik';
$elem['position'] = 'PHP Programmer';
$data[] = json_encode($elem);

$elem = array();
$elem['name'] = 'Carl';
$elem['position'] = 'C Programmer';
$data[] = json_encode($elem);

$jsonString = "[" . implode(", ", $data) . "]";

Ostatnim krokiem jest zapisanie łańcucha zakodowanego w JSON w zmiennej JS:

<script type="text/javascript">
employees = jQuery.parseJSON('<?=$marker; ?>');
</script>

Jeśli użyję „” zamiast „”, nadal będzie generować błąd.

ROZWIĄZANIE:

Jedyną rzeczą, która działała dla mnie, było użycie maski bitowej JSON_HEX_APOS do konwersji pojedynczych cudzysłowów w następujący sposób:

json_encode($tmp, JSON_HEX_APOS);

Czy istnieje inny sposób rozwiązania tego problemu? Czy mój kod jest nieprawidłowy lub źle napisany?

Dzięki

Erik Čerpnjak
źródło
„<? = $ marker; ?> ”to nie jest prawidłowy Json. Otaczające cytaty są „zjadane” przez interpreter javascript, pozostawiając ciąg rozpoczynający się od <... to, co naprawdę chciałeś wypróbować, to jedno z poniższych: jQuery.parseJSON ('"<? = $ Marker;?>” ”); jQuery.parseJSON ("\" <? = $ marker;?> \ ""); Zgodnie ze specyfikacją, ciągi json muszą używać podwójnych cudzysłowów, ale javascript nie ma znaczenia, więc albo masz ciąg javascript z pojedynczym cudzysłowiem, albo podwójny cudzysłów, ale jeśli używasz tego ostatniego, musisz uciec od wszystkich wykorzystuje podwójne cudzysłowy w ciągu.
Chris Cogdon,
3

Gdy wysyłasz pojedynczą ofertę w zapytaniu

empid = " T'via"
empid =escape(empid)

Gdy otrzymasz wartość, w tym pojedynczy cytat

var xxx  = request.QueryString("empid")
xxx= unscape(xxx)

Jeśli chcesz wyszukać / wstawić wartość, która zawiera pojedynczy cytat w zapytaniu xxx=Replace(empid,"'","''")

BehranG BinA
źródło
1
Ale nie ma potrzeby unikania pojedynczego cytatu, gdy przekazuje się go jako część łańcucha JSON ...
Justin Ethier
2

Uderzenie podobnego problemu przy użyciu CakePHP w celu wygenerowania bloku skryptu JavaScript przy użyciu natywnego PHP json_encode. $contractorCompanieszawiera wartości, które mają pojedyncze cudzysłowy i jak wyjaśniono powyżej i json_encode($contractorCompanies)nie oczekuje się od nich ucieczki, ponieważ jest to poprawny JSON.

<?php $this->Html->scriptBlock("var contractorCompanies = jQuery.parseJSON( '".(json_encode($contractorCompanies)."' );"); ?>

Dodając addlashes () wokół łańcucha zakodowanego w JSON, możesz następnie uciec od cudzysłowów, pozwalając Cake / PHP na echo poprawnego javascript w przeglądarce. Błędy JS znikają.

<?php $this->Html->scriptBlock("var contractorCompanies = jQuery.parseJSON( '".addslashes(json_encode($contractorCompanies))."' );"); ?>
chopstik
źródło
1

Próbowałem zapisać obiekt JSON z żądania XHR w atrybucie data5 * HTML5. Wypróbowałem wiele powyższych rozwiązań bez powodzenia.

Ostatecznie skończyłem na zamianie pojedynczego cudzysłowu na 'kod &#39;za pomocą wyrażenia regularnego po wywołaniu metody stringify () w następujący sposób:

var productToString = JSON.stringify(productObject);
var quoteReplaced = productToString.replace(/'/g, "&#39;");
var anchor = '<a data-product=\'' + quoteReplaced + '\' href=\'#\'>' + productObject.name + '</a>';
// Here you can use the "anchor" variable to update your DOM element.
BoCyrill
źródło
0

Ciekawy. Jak generujesz JSON po stronie serwera? Czy używasz funkcji bibliotecznej (takiej jak json_encodePHP), czy też ręcznie budujesz ciąg JSON?

Jedyne, co przykuło moją uwagę, to apostrof ucieczki ( \'). Ponieważ używasz podwójnych cudzysłowów, tak jak powinieneś, nie ma potrzeby unikania pojedynczych cudzysłowów. Nie mogę sprawdzić, czy to jest rzeczywiście przyczyną Twojego błędu jQuery, ponieważ sam nie zaktualizowałem do wersji 1.4.1.

Aistina
źródło
Używam biblioteki w PHP do generowania obiektu JSON - trochę tego nie pamiętam. Dzięki za zwrócenie na to uwagi.
Erik Čerpnjak