На мой вопрос можно было бы легко ответить, но я только начал изучать clojure и понятия не имею, как это сделать.
У меня есть файл JSON, который я прочитал и проанализировал. Теперь мне нужно получить каждый элемент этого JSON и обработать его в соответствии с типом. Например, у меня есть JSON:
{
"foo": {
"id": 1,
"name": "foo1"
},
"bar": {
"id": 1,
"name": "bar1"
},
"foo": {
"id": 2,
"name": "foo2"
},
"bar": {
"id": 2,
"name": "bar2"
}
}
Итак, я хочу создать функцию, которая выполняет итерацию для каждого элемента моего JSON, а затем вызывает другую функцию с множественными объявлениями для каждого типа элемента моего JSON.
Проблема в том, что я не знаю, как получить тип элемента JSON ... Кто-нибудь может мне помочь?
2 ответа
Вы можете получить все пары "ключ-значение" карты, рассматривая ее как последовательность Clojure. Затем он ведет себя как последовательность двухэлементных векторов, а точнее последовательность элементов MapEntry
. Когда вы позвоните first
на MapEntry
, вы получите ключ.
Итак, (first (seq {:a 1}))
возвращает [:a 1]
, а (first [:a 1])
возвращает :a
, ключ. Вызов seq
в этом примере не нужен, он нужен только для того, чтобы сделать пример более явным.
Затем вы можете написать выражение case
для выполнения действий в соответствии с ключом. Чтобы сделать его более расширяемым, вы можете использовать мультиметоды.
(def json-str "{\n \"foo\": {\n \"id\": 1,\n \"name\": \"foo1\"\n },\n \"bar\": {\n \"id\": 1,\n \"name\": \"bar1\"\n },\n \"foo\": {\n \"id\": 2,\n \"name\": \"foo2\"\n },\n \"bar\": {\n \"id\": 2,\n \"name\": \"bar2\"\n }\n}")
(def parsed (cheshire.core/parse-string json-str true))
;; {:foo {:id 2, :name "foo2"}, :bar {:id 2, :name "bar2"}}
;; handle with case:
(defn handle-data
[data]
(doseq [[k v] data]
(case k
:foo (println "this is a foo!" v)
:bar (println "this is a bar!" v)
(throw (ex-info (str "Did not recognize key "
key)
{:key k
:map v})))))
;; handle with multimethods:
(defmulti do-thing-depending-on-key first)
(defmethod do-thing-depending-on-key :foo [[k v]]
(println "this is a foo!" v))
(defmethod do-thing-depending-on-key :bar [[k v]]
(println "this is a bar!" k))
(defmethod do-thing-depending-on-key :default [[k v]]
(throw (ex-info (str "Did not recognize key "
key)
{:key k
:map v})))
(run! do-thing-depending-on-key parsed)
;; this is a foo! {:id 2, :name foo2}
;; this is a bar! {:id 2, :name bar2}
;; => nil
(run! do-thing-depending-on-key {:unknown {:id 3 :name "Unknown"}})
;; =>
;; clojure.lang.ExceptionInfo:
;; Did not recognize key :unknown {:key :unknown, :map {:id 3, :name "Unknown"}}
Мультиметоды могут оказаться излишними для чего-то столь простого. Я бы просто использовал cond
:
(ns tst.demo.core
(:require
[cheshire.core :as cheshire]
[clojure.java.io :as io] ))
(let [json-str (slurp (io/resource "data.json"))
edn-data (cheshire/parse-string json-str true) ; need `true` to emit keywords
]
(doseq [[type id-name-map] edn-data]
(cond
(= type :foo) (println :foo-proc id-name-map)
(= type :bar) (println :bar-proc id-name-map)
:else (println :other-proc id-name-map) )))
С результатами:
:foo-proc {:id 2, :name foo2}
:bar-proc {:id 2, :name bar2}
Для чего в project.clj
требуется следующее:
:dependencies [
[cheshire "5.8.0"]
....
И где ваши данные находятся в resources/data.json
:
~/expr/demo > cat resources/data.json
{
"foo": {
"id": 1,
"name": "foo1"
},
"bar": {
"id": 1,
"name": "bar1"
}, ....
Похожие вопросы
Новые вопросы
json
JSON (объектная нотация JavaScript) - это сериализуемый формат обмена данными, предназначенный для машинного и человеческого чтения. Не используйте этот тег для собственных объектов JavaScript или литералов объектов JavaScript. Прежде чем задать вопрос, проверьте свой JSON с помощью валидатора JSON, такого как JSONLint (https://jsonlint.com).