Как проверить, является ли строка JSON или нет?

191

У меня простой вызов AJAX, и сервер вернет либо строку JSON с полезными данными, либо строку сообщения об ошибке, созданную функцией PHP mysql_error(). Как я могу проверить, являются ли эти данные строкой JSON или сообщением об ошибке.

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

Это то, что я хочу:

if (isJSON(data)){
    //do some data stuff
}else{
    //report the error
    alert(data);
}
jeffery_the_wind
источник
Может быть, используя, eval()если он возвращает undefinedтогда, это не JSON
MatuDuke
4
Это было решено здесь: stackoverflow.com/questions/3710204/…
Reinard
2
Спасибо всем, извините, я не нашел этот пост раньше.
jeffery_the_wind
1
Технически это не дурак 3710204, так как тот спрашивает, действительно ли это JSON, который является гораздо более высокой планкой, чем JSON вообще.
carlin.scott
Возможный дубликат AJAX: Проверьте, является ли строка JSON?
Мехди

Ответы:

324

Используйте JSON.parse

function isJson(str) {
    try {
        JSON.parse(str);
    } catch (e) {
        return false;
    }
    return true;
}
Bourne
источник
53
Обработка исключений не должна использоваться, чтобы сделать что-то ожидаемое.
ЛуисЗавалета
46
JSON.parse(1234)ИЛИ JSON.parse(0)ИЛИ JSON.parse(false)ИЛИ JSON.parse(null)все не вызовет Exception и вернет true !! Не используйте этот ответ
Zalaboza
19
@Zalaboza 1234, 0, falseи nullвсе действительные значения JSON. Если вы хотите предикат, который проверяет, представляет ли JSON объект, вам нужно сделать немного больше.
Майкл Ланг
20
JSON.parseвыполняет много вычислений для анализа строки и выдачи вам объекта json в случае успеха, но вы отбрасываете результат, который некоторые пользователи могут захотеть использовать. Это не кажется хорошим. Я бы вместо этого return {value: JSON.parse(str), valid: true};и в блоке catch return {value: str, valid: false};.. и я бы изменил имя функции на tryParse().
Наваз
7
@luisZavaleta тогда, что вы предлагаете в качестве метода
PirateApp
80

Этот код JSON.parse(1234)или JSON.parse(0)или JSON.parse(false)или JSON.parse(null)все вернет true.

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

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

Результаты теста isJson

kubosho_
источник
4
Хорошая работа! Ваш последний оператор if можно упростить до простого оператора return, такого как:return (typeof suspect === "object" && suspect !== null);
Nebulosar
39

Давайте повторим это (для 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 может быть объектом, массивом, числом, строкой, истиной, ложью или нулем». - Как вы пришли к выводу, что JSON может быть только объектом или массивом на корневом уровне? Я не вижу этого ни в спецификации, ни в отношении «правильно сформированных структур JSON»
Релевестуал
Прочитайте второй абзац, который начинается с «JSON построен на двух структурах ...» @ json.org или 4-й и 5-й абзацы ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
Онур Йылдырым
json.org только для информации. Чтение спецификации, на которую вы ссылаетесь, не поддерживает ваше предложение. Спецификация упоминает RFC 8259 как последний RFC. Взгляните на примеры допустимых текстов в формате JSON, содержащих только значения tools.ietf.org/html/rfc8259#section-13 - RFC 8259 предназначен для устранения возможных двусмысленностей и недоразумений, вот так.
Релевестуал
Прочитайте ответ снова. Я говорю, что такие значения, как примитивы (т. Е. Текстовые значения в примерах RFC), не являются структурами JSON. Здесь нет двусмысленностей. Вы МОЖЕТЕ разобрать их как JSON, это действительно так. Но они не структурированные данные. JSON в основном придуман как формат обмена », который используется для структурированных данных», который может быть объектом или массивом.
Онур Йылдырым
1
ОК, так что я думаю, что мы согласны. Примитивы действительны в соответствии со спецификацией JSON, но не являются «структурами». Это хорошо. Но вы сказали: «Аргумент: значения true, false, null действительны в формате JSON (?). Факт: да и нет!» - Факт является действительным JSON в соответствии со спецификацией. Мнения о том, полезны они или нет, не имеют отношения к этому факту.
Relequestual
20

Если сервер отвечает JSON, он будет иметь application/jsonтип контента, если он отвечает простым текстовым сообщением, то он должен иметь text/plainтип контента. Убедитесь, что сервер отвечает с правильным типом содержимого и проверьте это.

Quentin
источник
4
Это неправильно, есть много других json-совместимых медиатипов. Кроме того overrideMimeTypeможет переопределить заголовок типа содержимого.
Кну
14

если при использовании jQuery $.ajax()ответа будет responseJSONсвойство, если ответ был JSON, это можно проверить следующим образом:

if (xhr.hasOwnProperty('responseJSON')) {}
rémy
источник
3
Я подозреваю, что это действительно тот ответ, который ищут большинство людей, вероятно, даже ОП
Кирби
1
Это намного элегантнее, чем использование блока try catch
Anurag Sinha
6

Мне нравится лучший ответ, но если это пустая строка, она возвращает true. Итак, вот исправление:

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;
}
Лонни Прайс старший
источник
вар json не используется? или просто поймать ошибку?
стек
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 код.

ZER0
источник
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 всегда будет окружен, {а }затем вы можете проверить эти символы или какой-нибудь другой хакерский метод. Или вы можете использовать JS-библиотеку json.org, чтобы попытаться проанализировать ее и проверить, успешно ли она работает.

Однако я бы предложил другой подход. Ваш PHP-скрипт в настоящее время возвращает JSON, если вызов успешен, но что-то еще, если это не так. Почему не всегда возвращать JSON?

Например

Успешный звонок:

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

Ошибочный вызов:

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

Это значительно упростит написание JS на стороне клиента - все, что вам нужно сделать, это проверить элемент «status» и действовать соответственно.

MDM
источник
4

Я использую только 2 строки, чтобы выполнить это:

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
efkan
источник
Я создал скрипку для этого ответа на jsfiddle.net/fatmonk/gpn4eyav, которая также включает возможность добавления собственных пользовательских тестовых данных. Это выглядит для меня хорошей библиотечной функцией, но я бы хотел больше понять, почему Test 1 не является допустимым массивом JSON.
Толстый монах
Потому что массив должен быть указан с помощью [и ]. Например, [1, 2, 3]это массив чисел. ["a", "b", "c"]это массив строк И [{"a":1}, {"b":2}]это массив JSON. Ваша работа с jsfiddle кажется действительно полезной!
Эфкан
Так просто, как, что?! Итак, Test 1 - это объект JSON, а Test 2 - массив JSON, состоящий из одного элемента объекта JSON. Я правильно понял?
Толстый монах
Вопрос, помеченный как возможный дубликат этого ( stackoverflow.com/questions/3710204/… ), задает вопрос о достижении этого без использования try / catch, поэтому я разобрался со своей скрипкой, чтобы попытаться достичь этой цели. Разветвление находится по адресу jsfiddle.net/fatmonk/827jsuvr и работает со всеми тестами, описанными выше, за исключением теста 3, в котором ошибки JSON.parse. Кто-нибудь может посоветовать, как избежать этой ошибки без использования try?
Толстый монах
Ваше jsfiddleприложение выдает ошибку, потому что в Test 3 нет допустимого выражения JSON. Таким образом, a try-catchнужно использовать, чтобы перехватить эту ошибку и оценить любую ошибку, так как выражение не является JSON при синтаксическом анализе, как в тесте 3 выше:try { JSON.parse(data3) } catch(e) { isValidJSON = false }
efkan
2

Вы можете попробовать расшифровать его и поймать исключение (native или 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-примитивов (логических, нулевых, массивов, чисел, строк), я предлагаю использовать следующее:

/* 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 - не ложно (исключая ноль, который регистрируется как typeof 'объект')
  • (typeof o === 'object') - исключает логическое значение, число и строку
  • ! Array.isArray (o) - исключить массивы (которые регистрируются как typeof 'object')
  • try ... JSON.stringify / JSON.parse - запрашивает движок JavaScript, чтобы определить, действительно ли JSON

Почему бы не использовать ответ hasJsonStructure ()?

Полагаться на toString()это не очень хорошая идея. Это потому, что разные движки JavaScript могут возвращать разные строковые представления. В общем, методы, которые полагаются на это, могут потерпеть неудачу в разных средах или могут стать причиной сбоя позже, если механизм когда-либо изменит строковый результат

Почему поймать исключение не взломать?

Было высказано мнение, что поймать исключение для определения достоверности чего-либо никогда нельзя. Это вообще хороший совет, но не всегда. В этом случае перехват исключений, вероятно, является наилучшим маршрутом, поскольку он основан на реализации механизма проверки данных JSON в движке JavaScript.

Опора на движок JS предлагает следующие преимущества:

  1. Более тщательный и постоянно обновляющийся по мере изменения спецификации JSON
  2. Вероятно, будет работать быстрее (так как это код нижнего уровня)

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

Рон С.
источник
1

Вот код с некоторыми незначительными изменениями в ответе Борна. Поскольку JSON.parse (число) работает нормально без каких-либо исключений, поэтому добавлен isNaN.

function isJson(str) {
    try {
        JSON.parse(str);
    } catch (e) {
        return false;
    }
    return isNaN(str);
}
Devashish Mamgain
источник
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

carlin.scott
источник
@ DukeDougal хотите уточнить? Иногда люди начинают свой json с «[», но это не очень часто встречается.
carlin.scott
1
Вам нужно разобрать его, чтобы из него действовал действительный JSON. Если это неверный JSON, то это не JSON. Вопрос в том, как определить, является ли строка JSON или нет? По вашему подходу это будет JSON {fibble - и это действительно не JSON. Рассмотрим также такие случаи, как число 1 само по себе - это действительно JSON.
Герцог Дугал

1
Msgstr "Если это неверный JSON, то это не JSON". Тот факт, что вы должны использовать слово «действительный», показывает, что вы добавляете квалификацию к тому факту, что это больше, чем просто json. Вопрос был просто "is it 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

Вы можете попробовать следующий, потому что он также проверяет число, ноль, строку, но отмеченный выше ответ не работает правильно, это просто исправление вышеуказанной функции:

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
K.King