如何在Clojure中附加到JSON文件中的列表?

时间:2018-01-20 16:17:40

标签: json clojure

我需要在JSON文件中编写一个输出,该文件随着时间的推移会变长。我有什么:

{ 
  "something": {  
     "foo" : "bar"
  }
}

我使用(spit "./my_file" my-text :append true)

使用append,我包含新条目,它看起来像这样:

  {
    "something": { 
      "foo": "bar"
    }
  },
  {
    "something": {
      "foo": "bar"
     }
  },
}

我的问题是我需要这样的东西:

[
  {
   "something": { 
       "foo": "bar"
      }
   },
   {
    "something": {
        "foo": "bar"
     }
   }
]

但我真的不知道如何在[ ]

中包含新数据

1 个答案:

答案 0 :(得分:0)

如果您想要就地更新 - 这意味着您更关心性能而不是安全 - 这可以使用java.io.RandomAccessFile来完成:

(import '[java.io RandomAccessFile])

(defn append-to-json-list-in-file [file-name new-json-text]
  (let [raf (RandomAccessFile. file-name "rw")
        lock (.lock (.getChannel raf))    ;; avoid concurrent invocation across processes
        current-length (.length raf)]
    (if (= current-length 0)
      (do
        (.writeBytes raf "[\n")           ;; On the first write, prepend a "["
        (.writeBytes raf new-json-text)   ;; ...before the data...
        (.writeBytes raf "\n]\n"))        ;; ...and a final "\n]\n"
      (do
        (.seek raf (- current-length 3))  ;; move to before the last "\n]\n"
        (.writeBytes raf ",\n")           ;; put a comma where that "\n" used to be
        (.writeBytes raf new-json-text)   ;; ...then the new data...
        (.writeBytes raf "\n]\n")))       ;; ...then a new "\n]\n"
    (.close lock)
    (.close raf)))

作为使用示例 - 如果不存在预先存在的out.txt,则接下来三次调用的结果:

(append-to-json-list-in-file "out.txt" "{\"hello\": \"birds\"}")
(append-to-json-list-in-file "out.txt" "{\"hello\": \"trees\"}")
(append-to-json-list-in-file "out.txt" "{\"goodbye\": \"world\"}")

...将是一个包含以下内容的文件:

[
{"hello": "birds"},
{"hello": "trees"},
{"goodbye": "world"}
]

请注意,锁定会阻止多个进程使用相同的输出文件一次调用此代码。 不会在同一进程中为多个线程提供安全性进行并发调用 - 如果您需要,我建议使用代理或其他固有的单线程构造。

如果文件以"\n]\n\n\n"而不是"\n]\n"结束,那么还有一些危险可能会破坏已由其他软件编辑的文件,然后在此之前寻找三个字节当前的长度会把我们放在错误的地方,我们会产生错误的输出。

如果您更关心确保输出是完整的而不是损坏的,那么相关技术不是特定于JSON的(并且要求重写整个输出文件,而不是逐步更新它);见Atomic file replacement in Clojure