添加类似json的值

时间:2017-08-24 06:01:58

标签: json clojure

我有2个完全相似的JSON对象,我想添加相应键的值并返回单个JSON。 JSON也将包含JSON数组。在clojure中做什么是惯用的方式?

输入JSON 1:

{
   "data":[
      {
         "val1":{
            "total":"1.00",
            "val2":{
               "total":"1.00"
            },
            "val3":{
               "total":"1.00"
            }
         }
      }
   ]
}

输入JSON 2:

{
   "data":[
      {
         "val1":{
            "total":"2.00",
            "val2":{
               "total":"3.00"
            },
            "val3":{
               "total":"4.00"
            }
         }
      }
   ]
}

预期的JSON:

{
       "data":[
          {
             "val1":{
                "total":"3.00",
                "val2":{
                   "total":"4.00"
                },
                "val3":{
                   "total":"5.00"
                }
             }
          }
       ]
    }

2 个答案:

答案 0 :(得分:3)

首先你需要解析json。

[org.clojure/data.json "0.2.6"]添加到您的:dependencies向量。

(ns foo.bar
  (:require
   [clojure.data.json :as json]))

(defn read-input [path]
  ;; Substitute slurp for however you get the input.
  (json/read-str (slurp path)
                 :key-fn keyword ; In clojure it's nice to have keys as keywords
                 :value-fn (fn [k v]
                             ;; Parse the numbers.
                             (if (= :total k)
                               (Double/parseDouble v)
                               v))))

(def input1 (read-input "input1.json"))
(def input2 (read-input "input2.json"))


;; The parsed input.
(def input1 {:data [{:val1 {:total 5.0, :val2 {:total 4.0}, :val3 {:total 3.0}}}]})
(def input2 {:data [{:val1 {:total 1.0, :val2 {:total 1.0}, :val3 {:total 1.0}}}]})

然后要合并它们,你可以使用漂亮的核心函数merge-with和一些递归。

(defn merge-jsons [a b]
  (merge-with (fn [v1 v2]
                (cond (every? vector? [v1 v2]) (mapv merge-jsons v1 v2)
                      (every? map? [v1 v2]) (merge-jsons v1 v2)
                      :else (+ v1 v2)))
              a, b))

然后你只需要将它转换回json。

write-str有一个:value-fn选项,就像read-str一样,如果你真的需要这里的数字是字符串的话)

(json/write-str (merge-jsons input1 input2))
;;=> "{\"data\":[{\"val1\":{\"total\":3.0,\"val2\":{\"total\":4.0},\"val3\":{\"total\":5.0}}}]}"

答案 1 :(得分:0)

这可以通过3个步骤来实现。我在下面的代码中提到了它们作为注释。

Clojure字符串太强大了。

(def input1 {:val1 {:total 5, :val2 {:total 4}, :val3 {:total 3}}})                                                                                                                                         
(def input2 {:val1 {:total 1, :val2 {:total 1}, :val3 {:total 1}}})                                                                                                                                         

;; Step 1: Flatten the data                                                                                                                                                                                 
(defn flatten-input [input]                                                                                                                                                                                 
  (let [values (str "[" (clojure.string/replace (str input) #"([{]*[:]*[a-zA-Z]+[1-9]*)|([}])|([,])" "") "]")]                                                                                              
       (read-string values)))                                                                                                                                                                                    

;; Step 2: Sum the data                                                                                                                                                                                     
(defn sum-the-data [& inputs]                                                                                                                                                                               
  (apply map +  inputs))                                                                                                                                                                                    

;; Step 3: Convert it into the  original data structure                                                                                                                                                     
(defn get-final-data [summed-data]
  (let [string-data (apply format "{:val1 {:total %s, :val2 {:total %s}, :val3 {:total %s}}}" summed-data)]                                                                                                 
        (read-string string-data)))                                                                                                                                                              

(defn main []
   (let [flattened-input1 (flatten-input input1)                                                                                                                                                             
         flattened-input2 (flatten-input input2)                                                                                                                                                             
         summed-data (sum-the-data flattened-input1 flattened-input2)]                                                                                                                                       
   (prn (get-final-data summed-data)))) 

免责声明:这只适用于没有矢量的数据结构。

注意:请评论可能比传统方法更耗时的任何事情。