Jak sprawdzić, czy ciąg jest JSON, czy nie?

207

Mam proste wywołanie AJAX, a serwer zwróci ciąg JSON z przydatnymi danymi lub ciąg komunikatu o błędzie utworzony przez funkcję PHP mysql_error(). Jak mogę sprawdzić, czy te dane są ciągiem JSON lub komunikatem o błędzie.

Byłoby miło użyć funkcji o nazwie isJSONtak samo, jak można użyć funkcji instanceofdo sprawdzenia, czy coś jest tablicą.

To jest to czego chcę:

if (isJSON(data)){
    //do some data stuff
}else{
    //report the error
    alert(data);
}
jeffery_the_wind
źródło
Możliwy duplikat AJAX: Sprawdź, czy ciąg to JSON?
Mehdi Dehghani

Odpowiedzi:

353

Użyj JSON.parse

function isJson(str) {
    try {
        JSON.parse(str);
    } catch (e) {
        return false;
    }
    return true;
}
Granica
źródło
66
Obsługa wyjątków nie powinna być używana do zrobienia czegoś, czego się oczekuje.
luisZavaleta
54
JSON.parse(1234)LUB JSON.parse(0)LUB JSON.parse(false)LUB JSON.parse(null)wszystko nie zgłosi wyjątku i zwróci wartość true !!. nie używaj tej odpowiedzi
Zalaboza
20
@Zalaboza 1234, 0, false, i nullsą wszystkie ważne wartości JSON. Jeśli potrzebujesz predykatu, który sprawdza, czy JSON reprezentuje obiekt, musisz zrobić trochę więcej.
Michael Lang
20
JSON.parsewykonuje dużo obliczeń, aby przeanalizować ciąg i przekazuje obiekt json, jeśli się powiedzie, ale odrzucasz wynik, którego niektórzy użytkownicy mogą chcieć użyć. To nie wydaje się być dobre. Zamiast tego return {value: JSON.parse(str), valid: true};w bloku catch return {value: str, valid: false};.. i zmieniłbym nazwę funkcji na tryParse().
Nawaz,
9
@luisZavaleta to co sugerujesz jako metodę
PirateApp
89

Ten kod to JSON.parse(1234)lub JSON.parse(0)lub JSON.parse(false)lub JSON.parse(null)all zwróci wartość true.

function isJson(str) {
    try {
        JSON.parse(str);
    } catch (e) {
        return false;
    }
    return true;
}

Więc przepisałem kod w ten sposób:

function isJson(item) {
    item = typeof item !== "string"
        ? JSON.stringify(item)
        : item;

    try {
        item = JSON.parse(item);
    } catch (e) {
        return false;
    }

    if (typeof item === "object" && item !== null) {
        return true;
    }

    return false;
}

Wynik testu:

Wynik testu isJson

kubosho_
źródło
4
Dobra robota! Twoje ostatnie oświadczenie if można uprościć do prostego oświadczenia zwrotnego, takiego jak:return (typeof suspect === "object" && suspect !== null);
Nebulosar
42

Podsumujmy to (dla 2019+).

Argument : Wartości takie jak true, false, nullsą ważne JSON (?)

FAKT : Te prymitywne wartości można analizować w formacie JSON, ale nie są one poprawnie sformułowanymi strukturami JSON . Specyfikacja JSON wskazuje, że JSON jest zbudowany na dwóch strukturach: zbiorze par nazwa / wartość (obiekt) lub uporządkowanej liście wartości (tablica).

Argument : obsługa wyjątków nie powinna być używana do zrobienia czegoś oczekiwanego.
(To jest komentarz, który ma ponad 25 głosów za!)

FAKT : Nie! Używanie try / catch jest zdecydowanie legalne, szczególnie w takim przypadku. W przeciwnym razie musiałbyś wykonać wiele czynności związanych z analizą ciągów, takich jak operacje tokenizacji / regex; co miałoby straszną wydajność.

hasJsonStructure()

Jest to przydatne, jeśli Twoim celem jest sprawdzenie, czy niektóre dane / tekst mają właściwy format wymiany JSON.

function hasJsonStructure(str) {
    if (typeof str !== 'string') return false;
    try {
        const result = JSON.parse(str);
        const type = Object.prototype.toString.call(result);
        return type === '[object Object]' 
            || type === '[object Array]';
    } catch (err) {
        return false;
    }
}

Stosowanie:

hasJsonStructure('true')             // —» false
hasJsonStructure('{"x":true}')       // —» true
hasJsonStructure('[1, false, null]') // —» true

safeJsonParse()

Jest to przydatne, jeśli chcesz zachować ostrożność podczas analizowania niektórych danych do wartości JavaScript.

function safeJsonParse(str) {
    try {
        return [null, JSON.parse(str)];
    } catch (err) {
        return [err];
    }
}

Stosowanie:

const [err, result] = safeJsonParse('[Invalid JSON}');
if (err) {
    console.log('Failed to parse JSON: ' + err.message);
} else {
    console.log(result);
}
Onur Yıldırım
źródło
1
Link do specyfikacji JSON zawiera następujący tekst: „Tekst JSON to sekwencja tokenów utworzona z punktów kodowych Unicode, która jest zgodna z gramatyką wartości JSON”. oraz „Wartość JSON może być obiektem, tablicą, liczbą, ciągiem znaków, wartością true, false lub null”. - Jak doszedłeś do wniosku, że JSON może być obiektem lub tablicą tylko na poziomie głównym? Nie widzę tego w specyfikacji ani nic dotyczącego „dobrze uformowanych struktur JSON”
Relequestual,
Przeczytaj drugi akapit, który zaczyna się od „JSON jest zbudowany na dwóch strukturach…” @ json.org lub 4 i 5 akapity ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
Onur Yıldırım
Serwis json.org ma wyłącznie charakter informacyjny. Czytanie specyfikacji, z którą łączysz się, nie potwierdza Twojej sugestii. Specyfikacja wymienia RFC 8259 jako najnowszy dokument RFC. Spójrz na przykłady prawidłowych tekstów JSON zawierających tylko wartości tools.ietf.org/html/rfc8259#section-13 - RFC 8259 ma na celu rozwiązanie ewentualnych niejasności i niejasności, tak po prostu.
Relequestual
Przeczytaj odpowiedź ponownie. Mówię, że wartości takie jak prymitywy (tj. Wartości tekstowe w przykładach RFC) nie są „strukturami” JSON. Nie ma żadnych niejasności. Możesz je przeanalizować jako JSON, możesz to zrobić. Ale nie są to dane strukturalne. JSON został wymyślony głównie jako format wymiany »używany do ustrukturyzowanych danych», które mogą być obiektem lub tablicą.
Onur Yıldırım
1
OK, więc myślę, że się zgadzamy. Prymaty są poprawnymi kodami JSON zgodnie ze specyfikacją, ale nie są „strukturami”. W porządku. Ale powiedziałeś: „Argument: wartości takie jak true, false, null są prawidłowe w formacie JSON (?). Fakt: tak i nie!” - Faktem jest, że ARE jest prawidłowym kodem JSON zgodnie ze specyfikacją. Opinie o tym, czy są przydatne, czy nie, nie mają znaczenia dla tego faktu.
Relequestual
20

Jeśli serwer odpowiada za pomocą JSON, to miałby application/jsontyp zawartości, jeśli odpowiada zwykłą wiadomością tekstową, powinien mieć text/plaintyp zawartości. Upewnij się, że serwer odpowiada, podając poprawny typ zawartości i przetestuj to.

Quentin
źródło
5
To jest złe, istnieje wiele innych typów nośników zgodnych z json. Ponadto overrideMimeTypemoże zastąpić nagłówek typu zawartości.
Knu
15

w przypadku użycia jQuery $.ajax()odpowiedzi będzie mieć responseJSONwłaściwość, jeśli odpowiedź była w formacie JSON, można to sprawdzić w następujący sposób:

if (xhr.hasOwnProperty('responseJSON')) {}
rémy
źródło
3
Podejrzewam, że to naprawdę odpowiedź, której szuka większość ludzi, prawdopodobnie nawet OP
Kirby
1
Jest to o wiele bardziej eleganckie niż użycie bloku try catch
Anurag Sinha
6

Podoba mi się najlepsza odpowiedź, ale jeśli jest to pusty ciąg, zwraca prawdę. Oto poprawka:

function isJSON(MyTestStr){
    try {
        var MyJSON = JSON.stringify(MyTestStr);
        var json = JSON.parse(MyJSON);
        if(typeof(MyTestStr) == 'string')
            if(MyTestStr.length == 0)
                return false;
    }
    catch(e){
        return false;
    }
    return true;
}
Lonnie Price Sr.
źródło
var json nie jest używany? czy po prostu złapać błąd?
stackdave,
5
var parsedData;

try {
    parsedData = JSON.parse(data)
} catch (e) {
    // is not a valid JSON string
}

Jednak zasugeruję, aby Twoje wywołanie / usługa http zawsze zwracały dane w tym samym formacie. Więc jeśli masz błąd, powinieneś mieć obiekt JSON, który opakowuje ten błąd:

{"error" : { "code" : 123, "message" : "Foo not supported" } } 

I może użyć oprócz statusu HTTP kodu 5xx.

ZER0
źródło
5

Cóż ... To zależy od sposobu otrzymywania danych. Myślę, że serwer odpowiada ciągiem w formacie JSON (używając np. Json_encode () w PHP). Jeśli używasz postu JQuery i ustawisz dane odpowiedzi na format JSON i jest to nieprawidłowy format JSON, spowoduje to błąd:

$.ajax({
  type: 'POST',
  url: 'test2.php',
  data: "data",
  success: function (response){

        //Supposing x is a JSON property...
        alert(response.x);

  },
  dataType: 'json',
  //Invalid JSON
  error: function (){ alert("error!"); }
});

Ale jeśli używasz odpowiedzi typu jako tekstu, musisz użyć $ .parseJSON. Według witryny jquery: „Przekazanie nieprawidłowego ciągu JSON może spowodować zgłoszenie wyjątku”. W ten sposób twój kod będzie:

$.ajax({
  type: 'POST',
  url: 'test2.php',
  data: "data",
  success: function (response){

        try {
            parsedData = JSON.parse(response);
        } catch (e) {
            // is not a valid JSON string
        }

  },
  dataType: 'text',
});
Lucas Batistussi
źródło
chyba że, oczywiście, próbujesz przeanalizować tekst błędu w funkcji błędu w powyższym przykładzie i nie jesteś pewien, czy jest to JSON ...
Kirby
Świetna odpowiedź, chociaż jeśli responsejest pusta, trafi do success: '(
Henrik Petterson
5

Używam tylko 2 linii, aby to zrobić:

var isValidJSON = true;
try { JSON.parse(jsonString) } catch { isValidJSON = false }

To wszystko!

Pamiętaj jednak, że istnieją 2 pułapki:
1. JSON.parse(null)zwraca null
2. Dowolną liczbę lub ciąg można przeanalizować JSON.parse()metodą.
   JSON.parse("5")zwraca 5
   JSON.parse(5)zwraca5

Pobawmy się kodem:

// TEST 1
var data = '{ "a": 1 }'

// Avoiding 'null' trap! Null is confirmed as JSON.
var isValidJSON = data ? true : false
try { JSON.parse(data) } catch(e) { isValidJSON = false }

console.log("data isValidJSON: ", isValidJSON);
console.log("data isJSONArray: ", isValidJSON && JSON.parse(data).length ? true : false);

Console outputs:
data isValidJSON:  true
data isJSONArray:  false


// TEST 2
var data2 = '[{ "b": 2 }]'

var isValidJSON = data ? true : false
try { JSON.parse(data2) } catch(e) { isValidJSON = false }

console.log("data2 isValidJSON: ", isValidJSON);
console.log("data2 isJSONArray: ", isValidJSON && JSON.parse(data2).length ? true : false);

Console outputs:
data2 isValidJSON:  true
data2 isJSONArray:  true


// TEST 3
var data3 = '[{ 2 }]'

var isValidJSON = data ? true : false
try { JSON.parse(data3) } catch(e) { isValidJSON = false }

console.log("data3 isValidJSON: ", isValidJSON);
console.log("data3 isJSONArray: ", isValidJSON && JSON.parse(data3).length ? true : false);

Console outputs:
data3 isValidJSON:  false
data3 isJSONArray:  false


// TEST 4
var data4 = '2'

var isValidJSON = data ? true : false
try { JSON.parse(data4) } catch(e) { isValidJSON = false }

console.log("data4 isValidJSON: ", isValidJSON);
console.log("data4 isJSONArray: ", isValidJSON && JSON.parse(data4).length ? true : false);


Console outputs:
data4 isValidJSON:  true
data4 isJSONArray:  false


// TEST 5
var data5 = ''

var isValidJSON = data ? true : false
try { JSON.parse(data5) } catch(e) { isValidJSON = false }

console.log("data5 isValidJSON: ", isValidJSON);
console.log("data5 isJSONArray: ", isValidJSON && JSON.parse(data5).length ? true : false);


Console outputs:
data5 isValidJSON:  false
data5 isJSONArray:  false

// TEST 6
var data6; // undefined

var isValidJSON = data ? true : false
try { JSON.parse(data6) } catch(e) { isValidJSON = false }

console.log("data6 isValidJSON: ", isValidJSON);
console.log("data6 isJSONArray: ", isValidJSON && JSON.parse(data6).length ? true : false);

Console outputs:
data6 isValidJSON:  false
data6 isJSONArray:  false
efkan
źródło
Stworzyłem skrzypce dla tej odpowiedzi na jsfiddle.net/fatmonk/gpn4eyav, które zawierają również opcję dodania własnych danych testowych użytkownika. Wydaje mi się, że to podstawa dobrej funkcji bibliotecznej, ale chciałbym dowiedzieć się więcej o tym, dlaczego Test 1 nie jest prawidłową tablicą JSON.
Fat Monk,
Ponieważ tablica musi być określona przy użyciu [i ]. Na przykład [1, 2, 3]jest tablicą liczb. ["a", "b", "c"]jest tablicą łańcuchową. I [{"a":1}, {"b":2}]jest tablicą JSON. Twoja praca jsfiddle wydaje się naprawdę przydatna!
efkan
Tak proste jak to?! Zatem Test 1 to obiekt JSON, a Test 2 to tablica JSON składająca się z pojedynczego elementu obiektu JSON. Czy dobrze to zrozumiałem?
Fat Monk
Pytanie oznaczone jako możliwy duplikat tego ( stackoverflow.com/questions/3710204/ ) dotyczy osiągnięcia tego bez użycia try / catch, więc rozwidliłem moje skrzypce, aby spróbować osiągnąć ten cel. Widelec znajduje się pod adresem jsfiddle.net/fatmonk/827jsuvr i działa ze wszystkimi powyższymi testami, z wyjątkiem testu 3, w którym błędy w plikuJSON.parse . Czy ktoś może doradzić, jak uniknąć tego błędu bez użycia try?
Fat Monk
Twoja jsfiddleaplikacja zgłasza błąd, ponieważ Test 3 nie ma prawidłowego wyrażenia JSON. Dlatego try-catchnależy użyć a, aby wychwycić ten błąd i ocenić każdy błąd, ponieważ wyrażenie nie jest JSON podczas analizowania, jak w powyższym teście 3:try { JSON.parse(data3) } catch(e) { isValidJSON = false }
efkan
4

Prawdopodobnie istnieją testy, które możesz wykonać, na przykład, jeśli wiesz, że zwracany JSON zawsze będzie otoczony, {a }następnie możesz przetestować te znaki lub inną hacką metodę. Lub możesz użyć biblioteki JS json.org, aby spróbować ją przeanalizować i sprawdzić, czy się powiedzie.

Sugerowałbym jednak inne podejście. Twój skrypt PHP obecnie zwraca kod JSON, jeśli wywołanie się powiedzie, ale coś innego, jeśli nie. Dlaczego nie zawsze zwracać JSON?

Na przykład

Udane połączenie:

{ "status": "success", "data": [ <your data here> ] }

Błędne wezwanie:

{ "status": "error", "error": "Database not found" }

To znacznie ułatwiłoby pisanie kodu JS po stronie klienta - wystarczy tylko sprawdzić status członka i odpowiednio zareagować.

mdm
źródło
2

Możesz spróbować go zdekodować i złapać wyjątek (natywny lub json2.js ):

try {
  newObj = JSON.parse(myJsonString);
} catch (e) {
  console.log('Not JSON');
}

Jednak sugerowałbym, aby odpowiedź była zawsze poprawna w formacie JSON. Jeśli otrzymasz błąd z zapytania MySQL, po prostu odeślij JSON z błędem:

{"error":"The MySQL error string."}

I wtedy:

if (myParsedJSON.error) {
  console.log('An error occurred: ' + myParsedJSON.error);
}
James Sumners
źródło
2

Ostrzeżenie: w przypadku metod, na których opierają się JSON.parse- tablice i łańcuchy otoczone cudzysłowami również przejdą (tj.console.log(JSON.parse('[3]'), JSON.parse('"\uD800"')) )

Aby uniknąć wszystkich prymitywów JSON niebędących obiektami (boolean, null, array, number, string), sugeruję użycie następującego:

/* Validate a possible object ie. o = { "a": 2 } */
const isJSONObject = (o) => 
  !!o && (typeof o === 'object') && !Array.isArray(o) && 
  (() => { try { return Boolean(JSON.stringify(o)); } catch { return false } })()

/* Validate a possible JSON object represented as string ie. s = '{ "a": 3 }' */
function isJSONObjectString(s) {
    try {
        const o = JSON.parse(s);
        return !!o && (typeof o === 'object') && !Array.isArray(o)
    } catch {
        return false
    }
}

Objaśnienie kodu

  • !! o - Not falsy (wyklucza wartość null, która rejestruje się jako typ 'obiektu')
  • (typeof o === 'obiekt') - wyklucza wartość logiczną, liczbę i ciąg
  • ! Array.isArray (o) - wyklucza tablice (które rejestrują się jako typ 'obiektu')
  • try ... JSON.stringify / JSON.parse - prosi silnik JavaScript o określenie, czy poprawny format JSON

Dlaczego nie skorzystać z odpowiedzi hasJsonStructure ()?

Poleganie na tym toString()nie jest dobrym pomysłem. Dzieje się tak, ponieważ różne silniki JavaScript mogą zwracać inną reprezentację ciągu. Ogólnie rzecz biorąc, metody, które na tym polegają, mogą zawieść w różnych środowiskach lub mogą ulec awarii później, jeśli silnik kiedykolwiek zmieni wynik ciągu

Dlaczego przechwytywanie wyjątku nie jest hakowaniem?

Wychowywano, że złapanie wyjątku w celu określenia ważności czegoś nigdy nie jest właściwą drogą. To generalnie dobra rada, ale nie zawsze. W tym przypadku wychwytywanie wyjątków jest prawdopodobnie najlepszą drogą, ponieważ opiera się na implementacji walidacji danych JSON przez silnik JavaScript.

Poleganie na silniku JS zapewnia następujące korzyści:

  1. Bardziej szczegółowe i stale aktualizowane wraz ze zmianami specyfikacji JSON
  2. Prawdopodobnie będzie działać szybciej (ponieważ jest to kod niższego poziomu)

Gdybym miał możliwość oparcia się na silniku JavaScript, radziłbym to zrobić. Szczególnie w tym przypadku. Chociaż wychwycenie wyjątku może wydawać się hackem, tak naprawdę obsługujesz tylko dwa możliwe stany powrotu z metody zewnętrznej.

Ron S.
źródło
1

Użyłem tego (rodzaj mieszanki różnych odpowiedzi, ale tak czy inaczej):

const isJSON = str => {
  if (typeof str === 'string'){
    try {
      JSON.parse(str)
      return true
    } catch(e){
    }
  }
  return false
}



[null, undefined, false, true, [], {}, 
 '', 'asdf', '{}', '[]', "{\"abc\": 2}","{\"abc\": \"2\"}"]
  .map(el => {
      console.log(`[>${el}<] - ${isJSON(el)}`)
})

console.log('-----------------')

elbik
źródło
1

Oto kod z niewielkimi modyfikacjami w odpowiedzi Bourne'a. Ponieważ JSON.parse (liczba) działa dobrze bez żadnych wyjątków, dodano isNaN.

function isJson(str) {
    try {
        JSON.parse(str);
    } catch (e) {
        return false;
    }
    return isNaN(str);
}
Devashish Mamgain
źródło
0

Wszystkie ciągi json zaczynają się od „{” lub „[” i kończą odpowiednim „}” lub „]”, więc po prostu to sprawdź.

Oto, jak robi to Angular.js:

var JSON_START = /^\[|^\{(?!\{)/;
var JSON_ENDS = {
  '[': /]$/,
  '{': /}$/
};

function isJsonLike(str) {
    var jsonStart = str.match(JSON_START);
    return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
}

https://github.com/angular/angular.js/blob/v1.6.x/src/ng/http.js

carlin.scott
źródło
@DukeDougal chcesz wyjaśnić? Czasami ludzie rozpoczynają swój plik json od „[”, ale nie jest to zbyt powszechne.
carlin.scott
1
Musisz go przeanalizować, aby wydobyć z niego prawidłowy kod JSON. Jeśli jest to nieprawidłowy JSON, to nie jest to JSON. Pytanie brzmi „jak sprawdzić, czy ciąg znaków jest w formacie JSON, czy nie?”. Według twojego podejścia byłby to JSON {fibble - i to naprawdę nie jest JSON. Rozważ również przypadki, takie jak sama cyfra 1 - to jest prawidłowy JSON.
Duke Dougal

1
„Jeśli jest to nieprawidłowy JSON, to nie jest JSON”. Fakt, że musisz użyć słowa „ważny”, pokazuje, że dodajesz kwalifikację do faktu, że jest to coś więcej niż tylko json. Pytanie brzmiało po prostu „czy to json”, a mój przykład kodu doskonale odpowiada na to pytanie bez zakładania dodatkowych wymagań.
carlin.scott

zły pomysł, jeśli używasz niektórych systemów szablonów i masz coś takiego { someValue }, jak automatycznie przejdzie walidację.
ncubica

@ncubica, więc używasz szablonu do czegoś innego niż json, ciąg zawiera tylko symbol zastępczy, który używa nawiasów klamrowych, a silnik szablonu nie zastępuje symbolu zastępczego rzeczywistą wartością? Pamiętaj też, jak już wyjaśniłem Duke'owi, pierwotne pytanie nie wspomina o weryfikacji. Chcieli tylko wiedzieć, czy wygląda na json, czy nie.
carlin.scott

0

Możesz wypróbować poniższy, ponieważ sprawdza również liczbę, null, ciąg, ale zaznaczona powyżej odpowiedź nie działa poprawnie, to tylko poprawka powyższej funkcji:

function isJson(str) {
  try {
      const obj = JSON.parse(str);
      if (obj && typeof obj === `object`) {
        return true;
      }
    } catch (err) {
      return false;
    }
   return false;
}

-1

Oprócz wcześniejszych odpowiedzi, w przypadku konieczności zweryfikowania formatu JSON, takiego jak „{}”, możesz użyć następującego kodu:

const validateJSON = (str) => {
  try {
    const json = JSON.parse(str);
    if (Object.prototype.toString.call(json).slice(8,-1) !== 'Object') {
      return false;
    }
  } catch (e) {
    return false;
  }
  return true;
}

Przykłady użycia:

validateJSON('{}')
true
validateJSON('[]')
false
validateJSON('')
false
validateJSON('2134')
false
validateJSON('{ "Id": 1, "Name": "Coke" }')
true