読者です 読者をやめる 読者になる 読者になる

Thanks Driven Life

日々是感謝

json-reformat.el v0.0.4 リリースしました

Release 0.0.4 · gongo/json-reformat · GitHub

修正内容

v0.0.3 までは、空のハッシュに対して json-reformat-region とかを仕掛けると、
下記のように null になってしまう という 仕様 でした。

{"foo": {}}

// ↓↓↓

{
  "foo": null
}

v0.0.4 からは、ちゃんと空のハッシュのまま整形できるようになりました。

{"foo": {}}

// ↓↓↓

{
    "foo": {
    }
}

空のハッシュであれば "foo": { } という感じで改行入らない方がいいかもしれませんね。
いつか考えます。

今回の内容を仕様としていた理由

json-reformat.el では、JSON テキストのパースを (json-read) で行っています。

;; $ cat foo.json
;; {
;;     "foo": 3,
;;     "bar": "pizza"
;; }

(dolist (type '(plist alist hash-table))
  (let ((json-object-type type))
    (with-temp-buffer
      (insert-file-contents "foo.json")
      (json-read))))

;; => (:bar "pizza" :foo 3)
;; => ((bar . "pizza") (foo . 3))
;; => #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8 data ("foo" 3 "bar" "pizza" ...))

このように alist plist hash-table の中の好きな形でパースした結果を受け取れます。

v0.0.3 までは plist で実装していたのですが、この時問題となるのが、前述の 空のハッシュ です。

;; $ cat foo.json
;; {
;;     "foo": {},
;;     "bar": null
;; }

(let ((json-object-type 'plist))
  (with-temp-buffer
    (insert-file-contents "foo.json")
    (json-read)))

;; => (:bar nil :foo nil)

このように 空のハッシュも null もパースすると nil になってしまう ということで、
受け取った側としては「どっちかわからんから {} に直すこともできねーなー」となって
最終的に「これは仕様ですわー」みたいな感じにしていました。

光明

そんなことを Tweet してみたところ、 @ さんから以下の reply をいただきました。

なるほど hash-table と思って実際に試してみたところ

;; $ cat foo.json
;; {
;;     "foo": {}
;; }

;; $ cat bar.json
;; {
;;     "bar": null
;; }

(let ((json-object-type 'hash-table))
  (dolist (filename '("foo.json" "bar.json"))
    (with-temp-buffer
      (insert-file-contents filename)
      (json-read))))

;; => #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8 data ("foo" #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8 data ( ...)) ...))
;; => #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8 data ("bar" nil ...))

ちゃんと nil なのか空ハッシュなのか判別できる!!

というわけで、今までの仕様はバグということにできて修正することができました。 @ さんありがとうございました!!

P.S.

Emacs 24.4 から標準実装されている M-x json-pretty-print も似たような問題が起きていますが
M-x json-pretty-print または M-x json-pretty-print-buffer 実行前に

(setq json-object-type 'hash-table)

とかしておくと、空ハッシュが壊れることはありません。