Работа с плохим JSON.parse() в node безопасно. Работа с json в с


Быстрая работа с JSON в Swift / Хабр

Работа с форматом JSON в Swift на первый взгляд не представляет особых сложностей, с одной стороны в стандартном наборе есть класс NSJSONSerialization который умеет парсить файлы, с другой стороны множество сторонних библиотек обещающих сделать этот процесс проще, а код нагляднее. В рамках же данной статьи я хотел бы рассмотреть как читать JSON файлы быстрее и почему очевидные подходы работают медленно. И так, сначала стоит сформулировать решаемую задачу: есть некий достаточно объемный JSON (около 30 мегабайт) следующей структуры:

data -> [Node] -> [Item] -> id: String, pos: [Int], coo: [Double]

Требуется распарсить и получить массив объектов типа Item соответсвенно имеющих строковое поле id, поле pos — целочисленный массив и поле coo — массив чисел с плавающей точкой.

Вариант первый — использование сторонней библиотеки:

Надо сказать что все попавшиеся мне решения использовали в качестве парсера стандартный NSJSONSerialization, а свою задачу видели исключительно в добавлении “синтаксического сахара” и более строгой типизации. В качестве примера возьмем одну из наиболее популярных SwiftyJSON:

let json = JSON(data: data) if let nodes = json["data"].array { for node in nodes { if let items = node.array { for item in items { if let id = item["id"].string, let pos = item["pos"].arrayObject as? [Int], let coo = item["coo"].arrayObject as? [Double] { Item(id: id, pos: pos, coo: coo) } } } } } На iPhone 6 выполнение данного кода заняло примерно 7.5 секунд, что непозволительно много для довольно быстрого устройства. Для дальнейшего сравнение будем считать это время эталонным. Теперь попробуем написать то же самое без использования SwiftyJSON.

Вариант второй — использование «чистого» swift:

let json = try! NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) if let nodes = (json as? [String: AnyObject])?["data"] as? [[[String: AnyObject]]] { for node in nodes { for item in node { if let id = item["id"] as? String, let pos = item["pos"] as? [Int], let coo = item["coo"] as? [Double] { Item(id: id, pos: pos, coo: coo) } } } } Наш «велосипед» справился за 6 секунд (80% от изначального), но все равно очень долго.

Попробуем разобраться, профайлер подсказывает что строка:

let nodes = (json as? [String: AnyObject])?["data"] as? [[[String: AnyObject]]] выполняется неожиданно долго.

Поведение класса NSJSONSerialization в Swift'е полностью аналогично его поведению в Objective C, а значит результатом парсинга будет некая иерархия состоящая из объектов типа NSDictionary, NSArray, NSNumber, NSString и NSNull. Данная же команда преобразует объекты этих классов в структуры Swift'а Array и Dictionary, а значит копирует данные! (Массивы в Swift более сходны с массивами в C++ чем в Objective C)

Чтобы избежать подобного копирования попробуем не использовать красивые типизированные массивы Swift'a.

Вариант третий — без использования Array и Dictionary:

let json = try! NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) if let nodes = (json as? NSDictionary)?["data"] as? NSArray { for node in nodes { if let node = node as? NSArray { for item in node { if let item = item as? NSDictionary, let id = item["id"] as? NSString, let pos = item["pos"] as? NSArray, let coo = item["coo"] as? NSArray { var _pos = [Int](count: pos.count, repeatedValue: 0) var _coo = [Double](count: coo.count, repeatedValue: 0) for var i = 0; i < pos.count; i++ { if let p = pos[i] as? NSNumber { _pos.append(p.integerValue) } } for var i = 0; i < coo.count; i++ { if let c = coo[i] as? NSNumber { _coo.append(c.doubleValue) } } Item(id: String(id), pos: _pos, coo: _coo) } } } } } Выглядит, конечно, ужасно. Но нас интересует прежде всего скорость работы: 2 сек (почти в 4 раза быстрее SwiftyJSON!)

Таким образом исключив неявные преобразования можно добиться значительного увеличения в скорости.

habr.com

Работа с JSON в 1С (до версии платформы 8.3.6)

В связи с тем, что в платформе версии 8.3.6 и старше появились встроенные объекты для работы с JSON, эта статья более не является актуальной, просьба проследовать в новую статью о работе с JSON в 1С.

Данную статью не убираю, ибо не всегда есть возможность обновить платформу до новой версии, возможно кому-то пригодится.

JSON — популярный, в настоящее время, формат, который часто используется при обмене данными с веб-сайтами или веб-сервисами. Стандартных средств для работы с JSON в 1С не существует, и в этой статье я расскажу о том, как восполнить этот недостаток своими силами.

Для начала следует сказать, что при работе с JSON требуется всего две основные функции: чтение JSON-данных и формирование JSON-данных.

Начнем, пожалуй с чтения. Дело в том, что данные в формате JSON выглядят примерно вот так:

{ "firstName": "Иван", "lastName": "Иванов", "address": { "streetAddress": "Московское ш., 101, кв.101", "city": "Ленинград", "postalCode": 101101 }, "phoneNumbers": [ "812 123-1234", "916 123-4567" ] }

{

   "firstName": "Иван",

   "lastName": "Иванов",

   "address": {

      "streetAddress": "Московское ш., 101, кв.101",

      "city": "Ленинград",

      "postalCode": 101101

   },

   "phoneNumbers": [

      "812 123-1234",

      "916 123-4567"

   ]

}

Видно, что все это достаточно сильно напоминает структуру, поэтому JSON-данные преобразуются именно в структуру, итак:

Функция ЗаполнитьСтруктуруИзJSON(<ДанныеJSON>)

<ДанныеJSON> — обязательный параметр, строка формата JSON, которую необходимо прочитать.

Возвращаемое значение: структура.

Вторая функция превращает значения с типами структура, массив или таблица значений в строку JSON-данных:

Функция СформироватьСтрокуJSON(<Объект>)

<Объект> — обязательный параметр, должен иметь тип «Структура», «Массив» или «ТаблицаЗначений».

Возвращаемое значение: строка JSON-данных.

Посмотреть полный текст модуля можно в виде cf-файла или текстового файла.

Загрузка...

1c-programmer-blog.ru

json - Работа с схемой JSON в CouchDB

Вы задаете тот же вопрос, который у меня был в течение многих лет, изучая потенциальные преимущества CouchDB в случаях использования форм с избыточными данными.

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

Где помещается схема в CouchDB? Как обычный документ? Проектный документ? Или, может быть, сохранить их как файл? Но если я буду проверять их, особенно на стороне сервера в функции validate_doc_update, они должны храниться в проектных документах.

Вы правы. Проект doc (ddoc), который также включает функцию validate_doc_update для выполнения проверки перед обновлением doc, является наиболее распространенным местом для размещения этих схем. this в функции validate_doc_update является самой ddoc - все, что включено в ddoc, может быть доступ из кода проверки.

Я начал хранить схемы как объект JSON в моем общем библиотечном свойстве/папке для модулей commonjs, например. lib/schemata.json. Свойство type моих документов указало ключ схемы, который должна получить подтверждение проверки документа. type: 'adr' → lib/schemata/adr. Схема может также ссылаться на другие схемы на каждое свойство - рекурсивная функция проверки прошла до конца любого свойства независимо от того, какой тип вложенных свойств был. Он хорошо зарекомендовал себя в первом проекте.

{ "person": { "name": "/type/name", "adr": "/type/adr", ... }, "name": { "forname": { "minlenght": 2, "maxlength": 42, ... }, "surname": { ... } }, "adr": { ... } }

Но тогда я хотел использовать подмножество этих схем в другом проекте. Чтобы просто скопировать его и добавить/удалить некоторые схемы, было бы слишком недальновидным мышлением. Что делать, если общая схема для адреса имеет ошибку и нуждается в обновлении в каждом проекте, который он использует?

В этот момент мои схемы были сохранены в одном файле в репозитории (я использую erica в качестве инструмента для загрузки для ddocs). Затем я понял, что когда я храню каждую схему в отдельном файле, например. adr.json, geo.json, tel.json и т.д. это приводит к тому же JSON-структуре на серверах ddoc, как и раньше, с однопользовательским подходом. Но он был более подходящим для управления исходным кодом. Мало того, что файлы меньшего размера приводят к меньшим конфликтам слияния и более чистой истории фиксации, также было включено управление зависимостями схем через субрепозитории (подмодули).

Другая мысль заключалась в том, чтобы использовать CouchDB как место хранения и управления схемами. Но, как вы уже упоминали об этом, схемы должны быть доступны в функции validate_doc_update. Сначала я попробовал подход с обработчиком обновлений - каждое обновление doc должно передать обработчик обновления проверки, который сам по себе получает правильную схему из CouchDB:

POST /_design/validator/_update/doctype/person function (schema, req) { ... //validate req.body against schema "person" return [req.body, {code: 202, headers: ...}] }

Но этот подход не работает хорошо с вложенными схемами. Хуже того - для предотвращения обновлений doc без проверки через обработчик мне пришлось использовать прокси перед CouchDB, чтобы скрыть прямые встроенные пути обновления док-станции (например, POST to/the/doc/_id). Я не нашел способ обнаружить в функции validate_doc_update, был ли обработчик обновления задействован до или нет (возможно, у кого-то есть? Я был бы рад прочитать такое решение.).

Во время этого исследования проблема с различными версиями той же схемы появляется на моем радаре. Как мне это сделать? Должны ли все документы из того же типа быть действительными в отношении одной и той же версии схемы (что означает необходимость переноса данных в формате db перед почти каждой версией схемы)? Если свойство type также содержит номер версии? и др.

Но подождите! Что делать, если схема документа прикреплена к самому документу? Это:

  • предоставит совместимую версию для содержимого документа для каждого документа
  • быть доступным в функции validate_doc_update (в oldDoc)
  • может быть реплицирован без прав доступа администратора (как вам нужно для обновлений ddoc)
  • будет включен в каждый ответ для запроса на стороне клиента

Это звучало очень интересно, и мне кажется, что это самый подход CouchDB-ish до сих пор. Сказать это ясно - схема документа прикреплена к самому документу - означает его сохранение в собственности документа. Как хранилище как вложение, так и использование самой схемы в качестве структуры документа не были успешными.

Наиболее чувствительным моментом такого подхода является C(создать) в жизненном круге CRUD документа. Существует множество различных решений, которые можно представить, чтобы убедиться, что присоединенная схема "правильная и приемлемая". Но это зависит от вашего определения этих терминов в вашем конкретном проекте.

Есть ли какая-нибудь библиотека (лучше будет JavaScript) с работами в CouchDB и Client (веб-браузер)? Библиотека с я мог бы генерировать JSON и автоматически проверять их?

Я начал реализовывать с помощью популярного плагина проверки JQuery. Я мог бы использовать схему как конфигурацию и автоматически проверять на стороне клиента. На стороне сервера я извлек функции проверки как модуль commonjs. Я ожидал, что позже найду модульный способ управления кодом, который предотвратит дублирование кода.

Оказалось, что большинство существующих фреймворков валидации очень хороши в сопоставлении шаблонов и валидации с одним свойством, но не способны проверять значения зависимостей в одном документе. Также требования к определению схемы часто слишком проприетарны. Для меня правильное правило для выбора правильного определения схемы: предпочитайте стандартизованное определение (jsonschema.org, microdata, rdfa, hcard и т.д.) Над собственной реализацией. Если вы оставите структуру и имена свойств как они есть, вам потребуется меньше документации, меньше трансформации, а иногда вы получите совместимость с зарубежным программным обеспечением, которое ваши пользователи используют также (например, календари, адресные книги и т.д.) Автоматически. Если вы хотите реализовать HTML-презентацию для своих документов, вы хорошо подготовлены к ее выполнению в семантическом веб-и-SEO-режиме.

И, наконец, - не желая звучать высокомерно - написать схему проверки достоверности не сложно. Возможно, вы хотите прочитать исходный код плагина проверки JQuery - я уверен, что вы находите это, как я, удивительным. Во времена, когда скорость оттока интерфейсных фреймворков увеличивается, возможно, это самый надежный способ обеспечения собственной проверки. Кроме того, я считаю, что вы должны иметь 100% понимание реализации валидации - это критическая часть вашего приложения. И если вы понимаете иностранную реализацию - вы также можете написать библиотеку самостоятельно.

Ok. Это легкий ответ. Сожалею. Если кто-то читает это до конца и хочет увидеть его подробным в действии с примером исходного кода - upvote, и я напишу сообщение в блоге и добавлю URI в качестве комментария.

qaru.site

Работа с JSON в Scala при помощи библиотеки json4s

Все мы любим JSON. Это простой формат, хорошо читаемый, удобный при отладке, стандарт де-факто во всяких там REST-ах, и не только. Более того, JSON может быть еще и довольно компактным, например, если передавать список с именами полей один раз, а за ним — списки значений. Или если просто сжать его при помощи gzip. В мире Scala есть немало библиотек для работы с JSON, но наиболее мощной и производительной, видимо, является json4s.

Для подключения json4s к проекту прописываем в build.sbt:

"org.json4s" %% "json4s-jackson" % "3.2.11"

Простой пример декодирования JSON:

import org.json4s._import org.json4s.jackson.JsonMethods._

object Json4sTests extends App {  val t = parse("""{"postId": 123123123123123, "text":"ololo"}""")  println(t)}

Результат:

JObject(List((postId,JInt(123123123123123)), (text,JString(ololo))))

То есть, получили AST.

С его же помощью можно собрать и сериализовать JSON объект:

val obj = JObject(List(   "postId" -> JInt(123123123123123L),   "text" -> JString("ololo")))

val doc = render(obj)val compactJson = compact(doc)val prettyJson = pretty(doc)println(s"compact:\n$compactJson\n\npretty:\n$prettyJson")

Результат:

compact: {"postId":123123123123123,"text":"ololo"}

pretty:{  "postId":123123123123123,  "text":"ololo"}

Строительство AST при помощи JObject’ов и JString’ов довольно многословно, давайте это исправим:

import org.json4s._import org.json4s.jackson.JsonMethods._import org.json4s.JsonDSL._

object Json4sTests extends App {  val obj = ("type" -> "post") ~            ("info" ->              ("postId" -> 12345L) ~              ("tags" -> Seq("ololo", "trololo"))            )  println(compact(render(obj)))}

Результат:

{"type":"post","info":{"postId":12345,"tags":["ololo","trololo"]}}

Что еще многословно — это парсить AST используя только паттерн матчинг. Поэтому json4s предлагает XPath-подобные комбинаторы, вроде тех, что мы использовали в свое время при парсинге XML:

val json = parse(   """|{"posts":[{"id":1,"text":"ololo"},     |{"id":2,"text":"trololo"}]}""".stripMargin)val postTexts: List[String] = {   json \ "posts" \\ "text" \ classOf[JString]} println(s"postTexts = $postTexts")

val JInt(firstPostId) = (json \ "posts")(0) \ "id"  println(s"firstPostId = $firstPostId")

Результат:

postIdsObj = List(ololo, trololo)firstPostId = 1

А что, если у нас есть набор каких-то case class’ов и мы хотели бы сериализовать их в JSON? Писать сериализацию и десериализацию вручную? Конечно же нет:

import org.json4s._import org.json4s.jackson.JsonMethods._import org.json4s.jackson.Serialization

object Json4sTests extends App {  sealed trait Status  case object StatusOk extends Status  case object StatusBanned extends Status  case class User(name: (String, String, String), friends: Seq[User],                  status: Option[Status])

  // implicit val formats = Serialization.formats(NoTypeHints)  implicit val formats = {    Serialization.formats(FullTypeHints(List(classOf[Status])))  }

  val john = {    val jane = User(("Jane", "J", "Doe"), Nil, Some(StatusBanned))    User(("John", "J", "Doe"), Seq(jane), None)  }

  val json = pretty(render(Extraction.decompose(john)))  println(s"json:\n$json")

  val decodedUser = parse(json).extract[User]  println(s"decoded user: $decodedUser")}

Вывод программы:

json:{  "name" : {    "_1" : "John",    "_2" : "J",    "_3" : "Doe"  },  "friends" : [ {    "name" : {      "_1" : "Jane",      "_2" : "J",      "_3" : "Doe"    },    "friends" : [ ],    "status" : {      "jsonClass" : "Json4sTests$StatusBanned$"    }  } ]}decoded user: User((John,J,Doe),List(User((Jane,J,Doe),List(),              Some(StatusBanned))),None)

Как видите, все на месте, никакие данные не потерялись. Что интересно, кортежи из двух элементов кодируются как {"Jane":"Doe"} :) Если же воспользоваться альтернативным значением неявного аргумента formats, который с NoTypeHints, то программа тоже будет работать, причем в полученном JSON’е не будет страшной строчки "Json4sTests$StatusBanned$". Но значение поля status будет потеряно — при десериализации мы будем всегда получать None. Собственно, это логично, должен же status как-то кодироваться.

Что еще интересно, мы можем добавлять в сериализуемые классы Option-поля и поля со значениями по умолчанию, а также удалять поля, и это не сломает обратную совместимость. То есть, программа, в которой были сделаны такие изменения, сможет десериализовать объект, сериализованный программой до внесения изменений. Проверьте сами!

Наконец, рассмотрим последний пример. Допустим, мы хотим сериализовать данные вроде таких:

object Operation extends Enumeration {   val READ, WRITE = Value}

type OperationType = Operation.Value

case class Stat(min: Double, max: Double, sum: Double, count: Long)

val stats: Map[OperationType, Stat] = {  Map(    Operation.READ ->  Stat(1.0, 2.0, 15.0, 7L),    Operation.WRITE -> Stat(0.5, 3.0, 13.0, 8L)  )}

В этом случае мы получим ошибку:

Exception in thread "main" org.json4s.package$MappingException: Do notknow how to serialize key of type class scala.Enumeration$Val. Considerimplementing a CustomKeySerializer.

… потому что ключами в JSON-объектах могут быть только строки. Нам нужно как-то отображать OperationType на строки и обратно. Сказано — сделано:

val OperationSerializer = new CustomKeySerializer[OperationType](   format => (     { case s: String => Operation.withName(s) },     { case k: OperationType => k.toString }   ))

implicit val serializationFormats = {  Serialization.formats(NoTypeHints) + OperationSerializer}

val json = pretty(render(Extraction.decompose(stats)))println(s"json:\n$json")

val decodedStat = parse(json).extract[Map[OperationType, Stat]]println(s"decodedStat:\n$decodedStat")

Вывод программы:

json:{  "READ" : {    "min" : 1.0,    "max" : 2.0,    "sum" : 15.0,    "count" : 7  },  "WRITE" : {    "min" : 0.5,    "max" : 3.0,    "sum" : 13.0,    "count" : 8  }}decodedStat:Map(READ -> Stat(1.0,2.0,15.0,7), WRITE -> Stat(0.5,3.0,13.0,8))

Все в полном соответствии с нашими ожиданиями!

Больше примеров использования json4s вы можете найти на официальном сайте библиотеки, а также в репозитории на GitHub. Особое внимание уделите каталогу tests. Например, в нем можно найти интересный файлик XmlExamples.scala. Да, json4s также поддерживает и XML!

А как вы относитесь к JSON и при помощи какой библиотеки работаете с ним?

Метки: Scala, Функциональное программирование.

eax.me

json - Работа с плохим JSON.parse() в node безопасно

Лучший способ уловить неверные ошибки разбора JSON - это перевести вызовы на JSON.parse() в блок try/catch.

У вас действительно нет другого варианта - встроенная реализация генерирует исключение из недопустимых данных JSON, и единственный способ предотвратить это исключение от остановки приложения - это поймать его. Даже использование сторонней библиотеки не позволит избежать этого - они должны сделать try/catch при вызове JSON.parse() где-нибудь.

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

Примечание о производительности

JavaScript-движок v8, используемый Node.js не может оптимизировать функции, содержащие блок try/catch.

Обновление: v8 4.5 и выше может оптимизировать try/catch. Для более старых версий см. Ниже.

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

function safelyParseJSON (json) { // This function cannot be optimised, it best to // keep it small! var parsed try { parsed = JSON.parse(json) } catch (e) { // Oh well, but whatever... } return parsed // Could be undefined! } function doAlotOfStuff () { // ... stuff stuff stuff var json = safelyParseJSON(data) // Tadaa, I just got rid of an optimisation killer! }

Если разбор JSON выполняется спорадически, это может не иметь заметного влияния на производительность, но если его использовать ненадлежащим образом в функции интенсивного использования, это может привести к резкому увеличению времени отклика.

Примечание о блокировке try/catch

Следует отметить, что every.single.statement кода JavaScript в Node.js выполняется только один раз, независимо от того, вызвал ли он основную функцию или от обратного вызова или от другого модуля или чего-то еще. Таким образом, каждый оператор блокирует процесс. Это не обязательно плохо - хорошо спроектированное приложение будет тратить большую часть своего времени на ожидание внешнего ресурса (ответ базы данных, HTTP-связь, операции с файловой системой и т.д.). Поэтому очень важно, что часто исполняемый код JavaScript может быть оптимизирован движком v8, поэтому в этом заблокированном состоянии требуется как можно меньше времени - см. Примечание о производительности.

qaru.site