Як перевірити, чи є рядок JSON чи ні?


191

У мене простий AJAX-дзвінок, і сервер поверне або рядок JSON з корисними даними, або рядок повідомлення про помилку, вироблений функцією PHP mysql_error(). Як я можу перевірити, чи є ці дані рядком JSON або повідомлення про помилку.

Було б непогано використовувати функцію, яка називається так isJSONсамо, як ви можете скористатися функцією instanceofдля перевірки, чи щось є масив.

Це те, що я хочу:

if (isJSON(data)){
    //do some data stuff
}else{
    //report the error
    alert(data);
}

Можливо, eval()якщо він повернеться undefined, це не JSON
MatuDuke

4
Це вирішено тут: stackoverflow.com/questions/3710204/…
Reinard

2
Дякую всім, вибачте, що раніше не знайшов інший пост
jeffery_the_wind

1
Технічно це не дупа 3710204, тому що він запитує, чи дійсний json, який може пройти набагато вище смуги, ніж це взагалі json.
carlin.scott

Відповіді:


324

Використовуйте JSON.parse

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

53
Поводження з винятками не повинно використовуватися для виконання чогось очікуваного.
luisZavaleta

46
JSON.parse(1234)АБО JSON.parse(0)АБО JSON.parse(false)АБО JSON.parse(null)НЕ всі не піднімуть Виняток і повернуть справжнє !!. не використовуйте цю відповідь
Залабоза

19
@Zalaboza 1234, 0, falseі nullвсі дійсні значення JSON. Якщо ви хочете, щоб предикат перевіряв, чи JSON являє собою об'єкт, вам потрібно зробити трохи більше.
Майкл Ланг

20
JSON.parseробить багато обчислень, щоб проаналізувати рядок і дати вам об’єкт json, якщо це вдасться, але ви відкидаєте результат, який деякі користувачі могли б використовувати. Це, здається, не добре. Я б замість цього return {value: JSON.parse(str), valid: true};і в блоці лову return {value: str, valid: false};.. і змінив би ім'я функції на tryParse().
Наваз

7
@luisZavaleta, то що ви пропонуєте як метод
PirateApp

80

Цей код JSON.parse(1234)або JSON.parse(0)або JSON.parse(false)або JSON.parse(null)все повертає істину.

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

Тому я переписав код таким чином:

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

Результат тестування:

є результат тестування Джона


4
Хороша робота! Ваше останнє, якщо заяву можна спростити до простої заяви про повернення, наприклад:return (typeof suspect === "object" && suspect !== null);
Небулосар

38

Давайте резюмуємо це (для 2019+).

Аргумент : Значення , такі як true, false, nullдійсні в форматі JSON (?)

ФАКТ : Ці примітивні значення можна порівняти з JSON, але вони не є добре сформованими структурами JSON . Специфікація JSON вказує, що JSON побудований на двох структурах: набір пар імен / значень (об'єкт) або упорядкований список значень (масив).

Аргумент : Поводження з винятками не повинно використовуватися для виконання чогось очікуваного.
(Це коментар, який містить 25+ оновлень!)

ФАКТ : Ні! Однозначно законно використовувати try / catch, особливо у такому випадку. В іншому випадку вам знадобиться зробити багато матеріалів аналізу рядків, таких як операції з токенізацією / регексом; які мали б жахливі результати.

hasJsonStructure()

Це корисно, якщо ваша мета - перевірити, чи деякі дані / текст мають належний формат обміну 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;
    }
}

Використання:

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

safeJsonParse()

І це корисно, якщо ви хочете бути обережними при аналізі деяких даних на значення JavaScript.

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

Використання:

const [err, result] = safeJsonParse('[Invalid JSON}');
if (err) {
    console.log('Failed to parse JSON: ' + err.message);
} else {
    console.log(result);
}

1
Ви посилаєтесь на специфікацію JSON: "Текст JSON - це послідовність лексем, утворена з кодових точок Unicode, що відповідає граматиці значення JSON." та "Значення JSON може бути об'єктом, масивом, числом, рядком, true, false або null." - Як ви дійшли висновку, що JSON може бути лише об'єктом або масивом на рівні кореня? Я не бачу цього в специфікації, і нічого щодо "добре сформованих структур JSON"
Requequestual

Прочитайте другий параграф, який починається з "JSON побудований на двох структурах ..." @ json.org або 4-й та 5-й абзаци ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
Onur Yıldırım

json.org є лише інформаційним. Читання специфікацій, з якими ви пов’язані, не підтримує вашу пропозицію. У специфікації згадується RFC 8259 як остання RFC. Погляньте на приклади дійсних текстових файлів JSON, що містять лише значень tools.ietf.org/html/rfc8259#section-13 - RFC 8259 призначений для вирішення можливих неясностей та плутанин, як саме це.
Вимовний

Прочитайте ще раз відповідь. Я кажу, такі значення, як примітиви (тобто текстові значення в прикладах RFC), не є "структурою" JSON. Неясностей немає. Ви МОЖЕТЕ розбирати їх як JSON, це дійсно. Але вони не є структурованими даними. JSON в основному придумується як формат обміну ", який використовується для структурованих даних", який може бути об'єктом або масивом.
Onur Yıldırım

1
Гаразд, тому я думаю, що ми згодні. Пріоритети є дійсними JSON відповідно до специфікації, але не є "структурами". Добре. Але, ви сказали: "Аргумент. Такі значення, як істина, помилка, нуль, дійсні JSON (?). Факт: Так і ні!" - Справа в тому, що ARE діє JSON відповідно до специфікації. Думки про те, корисні вони чи ні, до цього факту не мають значення.
Випускний

20

Якщо сервер відповідає JSON, він матиме тип application/jsonвмісту, якщо він відповідає звичайним текстовим повідомленням, то він повинен мати text/plainтип вмісту. Переконайтесь, що сервер відповідає відповідним типом вмісту і перевірити це.


4
Це неправильно, є багато інших медіатипів, сумісних з json. Крім того, overrideMimeTypeможе змінити заголовок типу вмісту.
Кну

14

при використанні jQuery $.ajax()відповіді буде responseJSONвластивість, якщо відповідь була JSON, це можна перевірити так:

if (xhr.hasOwnProperty('responseJSON')) {}

3
Це я підозрюю, що це справді відповідь, яку шукають більшість людей, ймовірно, навіть ОП
Кірбі

1
Це набагато елегантніше, ніж використання блоку
пробування

6

Мені подобається найкраща відповідь, але якщо це порожній рядок, він повертає істину. Тож ось виправлення:

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

var json не використовується? чи просто вловити помилку?
stackdave

5
var parsedData;

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

Однак я пропоную вам, щоб ваш http-дзвінок / послуга завжди повертав дані в тому самому форматі. Отже, якщо ви маєте помилку, тоді у вас повинен бути об’єкт JSON, який завершує цю помилку:

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

І, можливо, використовувати, а також HTTP статус 5xx код.


5

Ну ... Це залежить від способу отримання ваших даних. Я думаю, що сервер відповідає за допомогою форматованого рядка JSON (використовуючи json_encode () у PHP, наприклад). Якщо ви використовуєте повідомлення JQuery і встановлюєте дані відповіді форматом JSON, і це JSON, який неправильно формується, це призведе до помилки:

$.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!"); }
});

Але якщо ви використовуєте відповідь типу як текст, вам потрібно використовувати $ .parseJSON. Як повідомляє сайт jquery: "Передача в неправильно сформовану рядок JSON може призвести до викидання виключення". Таким чином ваш код буде:

$.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',
});

якщо, звичайно, ви не намагаєтеся проаналізувати текст помилки у функції помилки у наведеному вище прикладі та не впевнені, чи це JSON ...
Кірбі

Чудова відповідь, хоча якщо responseвона порожня, вона піде на success: '(
Генрік Петтерсон

4

Напевно, ви можете зробити тести, наприклад, якщо ви знаєте, що повернений JSON завжди буде оточений, {і }тоді ви можете протестувати на цих персонажів чи якийсь інший хакі-метод. Або ви могли б використовувати json.org JS бібліотеку , щоб спробувати розібрати його і випробування , якщо це вдасться.

Однак я б запропонував інший підхід. Ваш сценарій PHP наразі повертає JSON, якщо виклик вдалий, але щось інше, якщо його немає. Чому б не завжди повернути JSON?

Напр

Успішний дзвінок:

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

Помилковий дзвінок:

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

Це полегшило б написання вашої клієнтської сторони JS - все, що вам потрібно зробити, це перевірити "статусу" члена та діяти відповідно.


4

Для цього я використовую лише два рядки:

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

Це все!

Але майте на увазі, що є 2 пастки:
1. JSON.parse(null)повертає null
2. Будь-яке число або рядок можна проаналізувати JSON.parse()методом.
   JSON.parse("5")повертає 5
   JSON.parse(5)прибутки5

Давайте пограємо в код:

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

Я створив загадку для цієї відповіді на jsfiddle.net/fatmonk/gpn4eyav, яка включає можливість додавання власних тестових даних користувачів. Це виглядає як основа хорошої бібліотечної функції для мене, але я хотів би зрозуміти більше, чому тест 1 не є дійсним масивом JSON.
Товстий монах

Оскільки масив повинен бути визначений за допомогою [і ]. Наприклад, [1, 2, 3]це масив чисел. ["a", "b", "c"]є рядковим масивом. І [{"a":1}, {"b":2}]це масив JSON. Ваша робота jsfiddle здається дуже корисною!
efkan

Настільки просто! Отже, Тест 1 - це об'єкт JSON, а Тест 2 - це масив JSON, що складається з одного об'єктного елемента JSON. Я правильно це зрозумів?
Товстий монах

Питання, позначене як можливий дублікат цього ( stackoverflow.com/questions/3710204/… ), запитує про досягнення цього без використання спробу / лову, тому я розпрощав свою загадку намагатися також досягти цієї мети. Вилка знаходиться на jsfiddle.net/fatmonk/827jsuvr і працює з усіма тестами вище, за винятком тесту 3, який помилки в JSON.parse. Хтось може порадити, як уникнути цієї помилки, не використовуючи спробу?
Товстий монах

Ваша jsfiddleпрограма видає помилку через тест 3 не має дійсного вираження JSON. Таким чином, try-catchнеобхідно використовувати, щоб визначити цю помилку та оцінити будь-яку помилку, оскільки вираз не є JSON при розборі, як тест 3 вище:try { JSON.parse(data3) } catch(e) { isValidJSON = false }
efkan

2

Ви можете спробувати розшифрувати його і зафіксувати виняток (рідний або json2.js ):

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

Однак я б запропонував зробити відповідь завжди дійсною JSON. Якщо ви отримаєте помилку з вашого запиту MySQL, просто надішліть назад JSON з помилкою:

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

І потім:

if (myParsedJSON.error) {
  console.log('An error occurred: ' + myParsedJSON.error);
}

2

Попередження: Для методів, на які покладається JSON.parse- Масиви та цитати, оточені рядки, також будуть передані (тобто console.log(JSON.parse('[3]'), JSON.parse('"\uD800"')))

Щоб уникнути всіх необ'єктних примітивів JSON (булева, null, масив, число, рядок), я пропоную використовувати наступне:

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

Пояснення коду

  • !! o - Не хибний (виключає null, який реєструється як typeof 'object')
  • (typeof o === 'object') - Виключає булева, число та рядок
  • ! Array.isArray (o) - Виключити масиви (які реєструються як typeof 'object')
  • спробуйте ... JSON.stringify / JSON.parse - Попросить движок JavaScript визначити, чи дійсний JSON

Чому б не використати відповідь hasJsonStructure ()?

Покладатися на toString()це не дуже гарна ідея. Це пояснюється тим, що різні двигуни JavaScript можуть повертати інше рядкове представлення. Взагалі методи, які покладаються на це, можуть вийти з ладу в різних умовах або можуть згодом зазнати збою, якщо двигун колись змінить результат рядка

Чому ловити виняток - не хак?

Було висунуто, що пошук винятку для визначення дійсності чогось ніколи не є правильним шляхом. Це взагалі гарна порада, але не завжди. У цьому випадку найкращим маршрутом є вилучення винятків, оскільки воно спирається на реалізацію JavaScript механізмом перевірки даних JSON.

Покладаючись на двигун JS, пропонуються наступні переваги:

  1. Більш ретельне та постійне оновлення в міру зміни специфікацій JSON
  2. Ймовірно, працює швидше (оскільки це код нижчого рівня)

Коли мені нададуть можливість спиратися на механізм JavaScript, я б запропонував це зробити. Особливо так у цьому випадку. Хоча, можливо, здається, що випробувати виняток хакі, ви дійсно просто обробляєте два можливих стани повернення із зовнішнього методу.


1

Ось код з незначною модифікацією у відповіді Борна. Оскільки JSON.parse (число) працює без будь-яких винятків, так додано isNaN.

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

0

Всі рядки json починаються з '{' або '[' і закінчуються відповідним '}' або ']', тому просто перевірте це.

Ось як це робить 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


@DukeDougal піклуватися про уточнення? Іноді люди починають свій json із знака "[", але це не дуже страшно.
carlin.scott

1
Вам потрібно розібратися, щоб розробити з нього дійсний JSON. Якщо він недійсний JSON, то це не JSON. Питання "як сказати, чи є рядок JSON чи ні?". За вашим підходом це буде JSON {fibble - і це насправді не JSON. Розглянемо також такі випадки, як число 1 самостійно - це дійсно JSON.
Герцог Дугал

1
"Якщо він недійсний JSON, це не JSON". Той факт, що вам доводиться використовувати слово "дійсний", свідчить про те, що ви додаєте кваліфікацію до того, що це більше, ніж просто json. Питання було просто "це json", і мій приклад коду відповідає на це питання чудово, не передбачаючи додаткових вимог.
carlin.scott

погана ідея, якщо ви використовуєте деякі шаблонні системи і у вас щось на зразок { someValue }автоматично пройде перевірку.
ncubica

@ncubica, тож ви використовуєте шаблон для чогось іншого, ніж json, рядок містить лише заповнювач, який використовує фігурні дужки, і двигун шаблону не зможе замінити заповнювач на реальне значення? Також майте на увазі, як я вже пояснив герцогу, в оригінальному питанні не йдеться про валідацію. Вони хотіли лише знати, схоже це на json чи ні.
carlin.scott

0

Я пропоную в режимі Typescript:

export function stringify(data: any): string {
    try {
         return JSON.stringify(data)
    } catch (e) {
         return 'NOT_STRINGIFIABLE!'
    }
}

0

Я використовував цей (вид суміші різних відповідей, але все одно):

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('-----------------')


0

Ви можете спробувати наступне, тому що воно також підтверджує число, null, string, але зазначена вище відповідь не працює належним чином, це лише виправлення наведеної вище функції:

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

-1

На додаток до попередніх відповідей, якщо вам потрібно перевірити формат JSON типу "{}", ви можете використовувати наступний код:

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

Приклади використання:

validateJSON('{}')
true
validateJSON('[]')
false
validateJSON('')
false
validateJSON('2134')
false
validateJSON('{ "Id": 1, "Name": "Coke" }')
true
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.